1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
#!/usr/bin/python3
import collections
import sys
import pathlib
import re
import pygments
import pygments.lexers
import pygments.formatters
import ssg.build_yaml
import ssg.controls
import ssg.yaml
import ssg.jinja
import template_renderer
from rendering import common
POLICY_FIELDS_MAPPING = dict(
checktext="checks",
fixtext="fixes",
srg_requirement="prose",
vuldiscussion="prose",
)
class Namespace:
def __init__(self):
self.policy_specific_content = collections.defaultdict(dict)
def contains_something(self):
copy_of_attributes = dict(vars(self))
if self.policy_specific_content:
return True
else:
copy_of_attributes.pop("policy_specific_content")
for stuff in copy_of_attributes:
if stuff:
return True
return False
def sub_newlines_with_br(self):
attributes = vars(self)
for attr, value in attributes.items():
if not value:
continue
attributes[attr] = re.sub(r"\n+", r"<br/>\n", value)
class HtmlOutput(template_renderer.Renderer):
TEMPLATE_NAME = "rendering/rule-template.html"
def process_rule(self, rule_id):
self.built_content_path = pathlib.Path(self.built_content_path)
rule_fname = self.built_content_path / "rules" / (rule_id + ".yml")
try:
rule = ssg.build_yaml.Rule.from_yaml(rule_fname, self.env_yaml)
except RuntimeError as exc:
msg = (
f"{str(exc)}. Make sure that the product is compiled, "
f"and '{rule_id}' is a short rule ID.")
print(msg, file=sys.stderr)
sys.exit(1)
prose = Namespace()
prose.description = common.resolve_var_substitutions(rule.description)
prose.rationale = common.resolve_var_substitutions(rule.rationale)
prose.sub_newlines_with_br()
self.template_data["prose"] = prose
self.template_data["fixes"] = self._get_fixes(rule_id)
self.template_data["checks"] = self._get_checks(rule_id)
self.template_data["rule"] = rule
self._add_policy_content_to_categories(rule.policy_specific_content)
def _add_policy_content_to_categories(self, policy_specific_content):
for policy, contents in policy_specific_content.items():
for field_name, text in contents.items():
target_category = POLICY_FIELDS_MAPPING.get(field_name)
if target_category is None:
continue
target_destination = self.template_data[target_category]
text = re.sub(r"\n+", r"<br/>\n", text)
target_destination.policy_specific_content[policy][field_name] = text
def _highlight_file(self, fname, lexer):
with open(fname, "r") as f:
code = f.read()
code = common.resolve_var_substitutions(code)
return pygments.highlight(code, lexer, pygments.formatters.HtmlFormatter())
def _get_fixes(self, rule_id):
fixes = Namespace()
basename = rule_id + ".sh"
bash_loc = self.built_content_path / "fixes" / "bash" / basename
if not bash_loc.exists():
bash_loc = self.built_content_path / "fixes_from_templates" / "bash" / basename
if bash_loc.exists():
fixes.bash = self._highlight_file(bash_loc, pygments.lexers.BashLexer())
basename = rule_id + ".yml"
ansible_loc = self.built_content_path / "fixes" / "ansible" / basename
if not ansible_loc.exists():
ansible_loc = self.built_content_path / "fixes_from_templates" / "ansible" / basename
if ansible_loc.exists():
fixes.ansible = self._highlight_file(ansible_loc, pygments.lexers.YamlLexer())
return fixes
def _get_checks(self, rule_id):
checks = Namespace()
basename = rule_id + ".xml"
oval_loc = self.built_content_path / "checks" / "oval" / basename
if not oval_loc.exists():
oval_loc = self.built_content_path / "checks_from_templates" / "oval" / basename
if oval_loc.exists():
checks.oval = self._highlight_file(oval_loc, pygments.lexers.XmlLexer())
return checks
def parse_args():
parser = HtmlOutput.create_parser(
"Pass a short rule ID, and the script will render the rule fields and "
"associated content in a form of a single HTML file, "
"so consistency of the rule can be conveniently examined. "
"To get syntax highlighting, you will need a highlight.css file in the same directory, "
"you can generate one using pygmentize e.g. by running "
"pygmentize -S solarized-dark -f html > highlight.css")
parser.add_argument(
"rule", metavar="RULE_ID", help="The rule short ID")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
renderer = HtmlOutput(args.product, args.build_dir)
renderer.process_rule(args.rule)
renderer.output_results(args)
|