Exemple #1
0
    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
Exemple #2
0
    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
Exemple #5
0
    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
Exemple #6
0
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)
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #10
0
    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
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #13
0
    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
Exemple #14
0
    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
Exemple #15
0
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)
Exemple #16
0
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)