def from_yaml(yaml_file, env_yaml=None): yaml_contents = ssgcommon.open_and_macro_expand_yaml(yaml_file, env_yaml) if yaml_contents is None: return None rule_id, _ = os.path.splitext(os.path.basename(yaml_file)) rule = Rule(rule_id) rule.prodtype = yaml_contents.pop("prodtype", "all") rule.title = required_yaml_key(yaml_contents, "title") del yaml_contents["title"] rule.description = required_yaml_key(yaml_contents, "description") del yaml_contents["description"] rule.rationale = required_yaml_key(yaml_contents, "rationale") del yaml_contents["rationale"] rule.severity = required_yaml_key(yaml_contents, "severity") del yaml_contents["severity"] rule.references = yaml_contents.pop("references", {}) rule.identifiers = yaml_contents.pop("identifiers", {}) rule.ocil_clause = yaml_contents.pop("ocil_clause", None) rule.ocil = yaml_contents.pop("ocil", None) rule.external_oval = yaml_contents.pop("oval_external_content", None) rule.warnings = yaml_contents.pop("warnings", []) for warning_list in rule.warnings: if len(warning_list) != 1: raise ValueError("Only one key/value pair should exist for each dictionary") if yaml_contents: raise RuntimeError("Unparsed YAML data in '%s'.\n\n%s" % (yaml_file, yaml_contents)) rule.validate_identifiers(yaml_file) rule.validate_references(yaml_file) return rule
def from_yaml(yaml_file, product_yaml=None): yaml_contents = ssgcommon.open_and_macro_expand_yaml( yaml_file, product_yaml) if yaml_contents is None: return None rule_id, _ = os.path.splitext(os.path.basename(yaml_file)) rule = Rule(rule_id) rule.prodtype = yaml_contents.get("prodtype", "all") rule.title = required_yaml_key(yaml_contents, "title") rule.description = required_yaml_key(yaml_contents, "description") rule.rationale = required_yaml_key(yaml_contents, "rationale") rule.severity = required_yaml_key(yaml_contents, "severity") rule.references = yaml_contents.get("references", []) rule.identifiers = yaml_contents.get("identifiers", []) rule.ocil_clause = yaml_contents.get("ocil_clause") rule.ocil = yaml_contents.get("ocil") rule.external_oval = yaml_contents.get("oval_external_content") rule.warnings = yaml_contents.get("warnings", []) for warning_list in rule.warnings: if len(warning_list) != 1: raise ValueError( "Only one key/value pair should exist for each dictionary") return rule
def from_yaml(yaml_file, product_yaml=None): yaml_contents = open_yaml(yaml_file, product_yaml) if yaml_contents is None: return None group_id, _ = os.path.splitext(os.path.basename(yaml_file)) group = Group(group_id) group.prodtype = yaml_contents.get("prodtype", "all") group.title = required_yaml_key(yaml_contents, "title") group.description = required_yaml_key(yaml_contents, "description") return group
def from_yaml(yaml_file, product_yaml=None): yaml_contents = open_yaml(yaml_file, product_yaml) if yaml_contents is None: return None value_id, _ = os.path.splitext(os.path.basename(yaml_file)) value = Value(value_id) value.title = required_yaml_key(yaml_contents, "title") value.description = required_yaml_key(yaml_contents, "description") value.type = required_yaml_key(yaml_contents, "type") value.options = required_yaml_key(yaml_contents, "options") return value
def from_yaml(yaml_file, product_yaml=None): yaml_contents = ssgcommon.open_and_expand_yaml(yaml_file, product_yaml) if yaml_contents is None: return None basename, _ = os.path.splitext(os.path.basename(yaml_file)) profile = Profile(basename) profile.title = required_yaml_key(yaml_contents, "title") profile.description = required_yaml_key(yaml_contents, "description") profile.extends = yaml_contents.get("extends", None) profile.selections = required_yaml_key(yaml_contents, "selections") return profile
def main(): parser = argparse.ArgumentParser( description="Converts SCAP Security Guide YAML benchmark data " "(benchmark, rules, groups) to XCCDF Shorthand Format" ) parser.add_argument( "--build-config-yaml", required=True, dest="build_config_yaml", help="YAML file with information about the build configuration. " "e.g.: ~/scap-security-guide/build/build_config.yml" ) parser.add_argument( "--product-yaml", required=True, dest="product_yaml", help="YAML file with information about the product we are building. " "e.g.: ~/scap-security-guide/rhel7/product.yml" ) parser.add_argument("--bash_remediation_fns", required=True, help="XML with the XCCDF Group containing all bash " "remediation functions stored as values." "e.g.: build/bash-remediation-functions.xml") parser.add_argument("--output", required=True, help="Output XCCDF shorthand file. " "e.g.: /tmp/shorthand.xml") parser.add_argument("action", choices=["build", "list-inputs", "list-outputs"], help="Which action to perform.") args = parser.parse_args() # save a whole bunch of time if args.action == "list-outputs": print(args.output) sys.exit(0) env_yaml = ssgcommon.open_environment_yamls( args.build_config_yaml, args.product_yaml) base_dir = os.path.dirname(args.product_yaml) benchmark_root = required_yaml_key(env_yaml, "benchmark_root") profiles_root = required_yaml_key(env_yaml, "profiles_root") # we have to "absolutize" the paths the right way, relative to the # product_yaml path if not os.path.isabs(benchmark_root): benchmark_root = os.path.join(base_dir, benchmark_root) if not os.path.isabs(profiles_root): profiles_root = os.path.join(base_dir, profiles_root) add_from_directory(args.action, None, benchmark_root, profiles_root, args.bash_remediation_fns, args.output, env_yaml)
def from_yaml(yaml_file, product_yaml=None): yaml_contents = open_yaml(yaml_file, product_yaml) if yaml_contents is None: return None group_id, _ = os.path.splitext(os.path.basename(yaml_file)) group = Group(group_id) group.prodtype = yaml_contents.get("prodtype", "all") group.title = required_yaml_key(yaml_contents, "title") group.description = required_yaml_key(yaml_contents, "description") group.warnings = yaml_contents.get("warnings", []) for warning_list in group.warnings: if len(warning_list) != 1: raise ValueError("Only one key/value pair should exist for each dictionary") return group
def from_yaml(yaml_file, product_yaml=None): yaml_contents = open_yaml(yaml_file, product_yaml) if yaml_contents is None: return None value_id, _ = os.path.splitext(os.path.basename(yaml_file)) value = Value(value_id) value.title = required_yaml_key(yaml_contents, "title") value.description = required_yaml_key(yaml_contents, "description") value.type = required_yaml_key(yaml_contents, "type") value.options = required_yaml_key(yaml_contents, "options") value.warnings = yaml_contents.get("warnings", []) for warning_list in value.warnings: if len(warning_list) != 1: raise ValueError("Only one key/value pair should exist for each dictionary") return value
def from_yaml(yaml_file, product_yaml=None): yaml_contents = open_yaml(yaml_file, product_yaml) if yaml_contents is None: return None rule_id, _ = os.path.splitext(os.path.basename(yaml_file)) rule = Rule(rule_id) rule.prodtype = yaml_contents.get("prodtype", "all") rule.title = required_yaml_key(yaml_contents, "title") rule.description = required_yaml_key(yaml_contents, "description") rule.rationale = required_yaml_key(yaml_contents, "rationale") rule.severity = required_yaml_key(yaml_contents, "severity") rule.references = yaml_contents.get("references", []) rule.identifiers = yaml_contents.get("identifiers", []) rule.ocil_clause = yaml_contents.get("ocil_clause") rule.ocil = yaml_contents.get("ocil") rule.external_oval = yaml_contents.get("oval_external_content") return rule
def from_yaml(yaml_file, env_yaml=None): yaml_contents = ssgcommon.open_and_expand_yaml(yaml_file, env_yaml) if yaml_contents is None: return None value_id, _ = os.path.splitext(os.path.basename(yaml_file)) value = Value(value_id) value.title = required_yaml_key(yaml_contents, "title") del yaml_contents["title"] value.description = required_yaml_key(yaml_contents, "description") del yaml_contents["description"] value.type_ = required_yaml_key(yaml_contents, "type") del yaml_contents["type"] value.operator = yaml_contents.pop("operator", "equals") possible_operators = ["equals", "not equal", "greater than", "less than", "greater than or equal", "less than or equal", "pattern match"] if value.operator not in possible_operators: raise ValueError( "Found an invalid operator value '%s' in '%s'. " "Expected one of: %s" % (value.operator, yaml_file, ", ".join(possible_operators)) ) value.interactive = \ yaml_contents.pop("interactive", "false").lower() == "true" value.options = required_yaml_key(yaml_contents, "options") del yaml_contents["options"] value.warnings = yaml_contents.pop("warnings", []) for warning_list in value.warnings: if len(warning_list) != 1: raise ValueError("Only one key/value pair should exist for each dictionary") if yaml_contents: raise RuntimeError("Unparsed YAML data in '%s'.\n\n%s" % (yaml_file, yaml_contents)) return value
def from_yaml(yaml_file, env_yaml=None): yaml_contents = ssgcommon.open_and_expand_yaml(yaml_file, env_yaml) if yaml_contents is None: return None basename, _ = os.path.splitext(os.path.basename(yaml_file)) profile = Profile(basename) profile.title = required_yaml_key(yaml_contents, "title") del yaml_contents["title"] profile.description = required_yaml_key(yaml_contents, "description") del yaml_contents["description"] profile.extends = yaml_contents.pop("extends", None) profile.selections = required_yaml_key(yaml_contents, "selections") del yaml_contents["selections"] if yaml_contents: raise RuntimeError("Unparsed YAML data in '%s'.\n\n%s" % (yaml_file, yaml_contents)) return profile
def from_yaml(yaml_file, id_, product_yaml=None): yaml_contents = ssgcommon.open_and_expand_yaml(yaml_file, product_yaml) if yaml_contents is None: return None benchmark = Benchmark(id_) benchmark.title = required_yaml_key(yaml_contents, "title") benchmark.status = required_yaml_key(yaml_contents, "status") benchmark.description = required_yaml_key(yaml_contents, "description") notice_contents = required_yaml_key(yaml_contents, "notice") benchmark.notice_id = required_yaml_key(notice_contents, "id") benchmark.notice_description = required_yaml_key( notice_contents, "description") benchmark.front_matter = required_yaml_key(yaml_contents, "front-matter") benchmark.rear_matter = required_yaml_key(yaml_contents, "rear-matter") benchmark.cpes = yaml_contents.get("cpes", []) benchmark.version = str(required_yaml_key(yaml_contents, "version")) return benchmark
def from_yaml(yaml_file, env_yaml=None): yaml_contents = ssgcommon.open_and_macro_expand_yaml(yaml_file, env_yaml) if yaml_contents is None: return None group_id, _ = os.path.splitext(os.path.basename(yaml_file)) group = Group(group_id) group.prodtype = yaml_contents.pop("prodtype", "all") group.title = required_yaml_key(yaml_contents, "title") del yaml_contents["title"] group.description = required_yaml_key(yaml_contents, "description") del yaml_contents["description"] group.warnings = yaml_contents.pop("warnings", []) for warning_list in group.warnings: if len(warning_list) != 1: raise ValueError("Only one key/value pair should exist for each dictionary") if yaml_contents: raise RuntimeError("Unparsed YAML data in '%s'.\n\n%s" % (yaml_file, yaml_contents)) return group
def from_yaml(yaml_file, id_, product_yaml=None): yaml_contents = ssgcommon.open_and_macro_expand_yaml(yaml_file, product_yaml) if yaml_contents is None: return None benchmark = Benchmark(id_) benchmark.title = required_yaml_key(yaml_contents, "title") del yaml_contents["title"] benchmark.status = required_yaml_key(yaml_contents, "status") del yaml_contents["status"] benchmark.description = required_yaml_key(yaml_contents, "description") del yaml_contents["description"] notice_contents = required_yaml_key(yaml_contents, "notice") benchmark.notice_id = required_yaml_key(notice_contents, "id") del notice_contents["id"] benchmark.notice_description = required_yaml_key(notice_contents, "description") del notice_contents["description"] if not notice_contents: del yaml_contents["notice"] benchmark.front_matter = required_yaml_key(yaml_contents, "front-matter") del yaml_contents["front-matter"] benchmark.rear_matter = required_yaml_key(yaml_contents, "rear-matter") del yaml_contents["rear-matter"] benchmark.cpes = yaml_contents.pop("cpes", []) benchmark.version = str(required_yaml_key(yaml_contents, "version")) del yaml_contents["version"] if yaml_contents: raise RuntimeError("Unparsed YAML data in '%s'.\n\n%s" % (yaml_file, yaml_contents)) return benchmark
def checks(product_yaml, oval_version, oval_dirs): """Concatenate all XML files in the oval directory, to create the document body oval_dirs: list of directory with oval files (later has higher priority) Return: The document body""" body = [] included_checks_count = 0 reversed_dirs = oval_dirs[::-1] # earlier directory has higher priority already_loaded = dict() # filename -> oval_version for oval_dir in reversed_dirs: try: # sort the files to make output deterministic for filename in sorted(os.listdir(oval_dir)): if filename.endswith(".xml"): xml_content = ssgcommon.process_file_with_jinja( os.path.join(oval_dir, filename), product_yaml) if not check_is_applicable_for_product( xml_content, ssgcommon.required_yaml_key( product_yaml, "product")): continue if check_is_loaded(already_loaded, filename, oval_version): continue if not check_oval_version_from_oval( xml_content, oval_version): continue body.append(xml_content) included_checks_count += 1 already_loaded[filename] = oval_version except OSError as e: if e.errno != errno.ENOENT: raise else: sys.stderr.write("Not merging OVAL content from the " "'%s' directory as the directory does not " "exist\n" % (oval_dir)) sys.stderr.write("Merged %d OVAL checks.\n" % (included_checks_count)) return "".join(body)
def main(): p = argparse.ArgumentParser() p.add_argument("--ssg_version", default="unknown", help="SSG version for reporting purposes. example: 0.1.34") p.add_argument( "--product-yaml", required=True, dest="product_yaml", help="YAML file with information about the product we are building. " "e.g.: ~/scap-security-guide/rhel7/product.yml") p.add_argument("--oval_config", required=True, help="Location of the oval.config file.") p.add_argument("--oval_version", required=True, help="OVAL version to use. Example: 5.11, 5.10, ...") p.add_argument("--output", type=argparse.FileType("wb"), required=True) p.add_argument("ovaldirs", metavar="OVAL_DIR", nargs="+", help="Directory(ies) from which we will collect " "OVAL definitions to combine. Order matters, latter " "directories override former.") args, unknown = p.parse_known_args() if unknown: sys.stderr.write("Unknown positional arguments " + ",".join(unknown) + ".\n") sys.exit(1) product_yaml = ssgcommon.open_product_yaml(args.product_yaml) if os.path.isfile(args.oval_config): multi_platform = parse_conf_file( args.oval_config, ssgcommon.required_yaml_key(product_yaml, "product")) header = ssgcommon.oval_generated_header("combine-ovals.py", args.oval_version, args.ssg_version) else: sys.stderr.write("The directory specified does not contain the %s " "file!\n" % (args.oval_config)) sys.exit(1) body = checks(product_yaml, args.oval_version, args.ovaldirs) # parse new file(string) as an ElementTree, so we can reorder elements # appropriately corrected_tree = ElementTree.fromstring( ("%s%s%s" % (header, body, footer)).encode("utf-8")) tree = add_platforms(corrected_tree, multi_platform) definitions = ElementTree.Element("{%s}definitions" % oval_ns) tests = ElementTree.Element("{%s}tests" % oval_ns) objects = ElementTree.Element("{%s}objects" % oval_ns) states = ElementTree.Element("{%s}states" % oval_ns) variables = ElementTree.Element("{%s}variables" % oval_ns) for childnode in tree.findall("./{%s}def-group/*" % oval_ns): if childnode.tag is ElementTree.Comment: continue elif childnode.tag.endswith("definition"): append(definitions, childnode) elif childnode.tag.endswith("_test"): append(tests, childnode) elif childnode.tag.endswith("_object"): append(objects, childnode) elif childnode.tag.endswith("_state"): append(states, childnode) elif childnode.tag.endswith("_variable"): append(variables, childnode) else: sys.stderr.write("Warning: Unknown element '%s'\n" % (childnode.tag)) root = ElementTree.fromstring(("%s%s" % (header, footer)).encode("utf-8")) root.append(definitions) root.append(tests) root.append(objects) root.append(states) if list(variables): root.append(variables) ElementTree.ElementTree(root).write(args.output) sys.exit(0)
def main(): p = argparse.ArgumentParser() p.add_argument( "--product-yaml", required=True, dest="product_yaml", help="YAML file with information about the product we are building. " "e.g.: ~/scap-security-guide/rhel7/product.yml") p.add_argument( "--remediation_type", required=True, help="language or type of the remediations we are combining." "example: ansible") p.add_argument("--build_dir", required=True, help="where is the cmake build directory. pass value of " "$CMAKE_BINARY_DIR.") p.add_argument("--output", type=argparse.FileType("wb"), required=True) p.add_argument("fixdirs", metavar="FIX_DIR", nargs="+", help="directory(ies) from which we will collect " "remediations to combine.") args, unknown = p.parse_known_args() if unknown: sys.stderr.write("Unknown positional arguments " + ",".join(unknown) + ".\n") sys.exit(1) product_yaml = open_yaml(args.product_yaml) fixcontent = ElementTree.Element( "fix-content", system="urn:xccdf:fix:script:sh", xmlns="http://checklists.nist.gov/xccdf/1.1") fixgroup = get_fixgroup_for_remediation_type(fixcontent, args.remediation_type) fixes = dict() remediation_functions = get_available_remediation_functions(args.build_dir) included_fixes_count = 0 for fixdir in args.fixdirs: try: for filename in os.listdir(fixdir): if not is_supported_filename(args.remediation_type, filename): continue # Create and populate new fix element based on shell file fixname = os.path.splitext(filename)[0] mod_file = [] config = {} fix_file_lines = process_file_with_jinja( os.path.join(fixdir, filename), product_yaml).splitlines() # Assignment automatically escapes shell characters for XML for line in fix_file_lines: line += "\n" if line.startswith('#'): try: (key, value) = line.strip('#').split('=') if key.strip() in [ 'complexity', 'disruption', 'platform', 'reboot', 'strategy' ]: config[key.strip()] = value.strip() else: if not line.startswith(FILE_GENERATED): mod_file.append(line) except ValueError: if not line.startswith(FILE_GENERATED): mod_file.append(line) else: mod_file.append(line) complexity = None disruption = None reboot = None script_platform = None strategy = None if 'complexity' in config: complexity = config['complexity'] if 'disruption' in config: disruption = config['disruption'] if 'platform' in config: script_platform = config['platform'] if 'complexity' in config: reboot = config['reboot'] if 'complexity' in config: strategy = config['strategy'] if script_platform: product_name, result = fix_is_applicable_for_product( script_platform, required_yaml_key(product_yaml, "product")) if result: if fixname in fixes: fix = fixes[fixname] for child in list(fix): fix.remove(child) else: fix = ElementTree.SubElement(fixgroup, "fix") fix.set("rule", fixname) if complexity is not None: fix.set("complexity", complexity) if disruption is not None: fix.set("disruption", disruption) if reboot is not None: fix.set("reboot", reboot) if strategy is not None: fix.set("strategy", strategy) fixes[fixname] = fix included_fixes_count += 1 fix.text = "".join(mod_file) # Expand shell variables and remediation functions # into corresponding XCCDF <sub> elements expand_xccdf_subs(fix, args.remediation_type, remediation_functions) else: sys.stderr.write("Skipping '%s' remediation script. " "The platform identifier in the " "script is missing!\n" % (filename)) except OSError as e: if e.errno != errno.ENOENT: raise else: sys.stderr.write("Not merging remediation scripts from the " "'%s' directory as the directory does not " "exist.\n" % (fixdir)) sys.stderr.write("Merged %d %s remediations.\n" % (included_fixes_count, args.remediation_type)) tree = ElementTree.ElementTree(fixcontent) tree.write(args.output) sys.exit(0)