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
|
#!/usr/bin/python3
import argparse
import os
import sys
import xml.etree.cElementTree as ET
try:
import ssg.constants
import ssg.xccdf
import ssg.xml
except ImportError:
print("The ssg module could not be found.", file=sys.stderr)
print(
"Run .pyenv.sh available in the project root directory,"
" or add it to PYTHONPATH manually.",
file=sys.stderr,
)
print("$ source .pyenv.sh", file=sys.stderr)
exit(2)
SSG_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
BINARY_DIR = os.path.join(SSG_ROOT, "build")
PREFIX = "xccdf_org.ssgproject.content_"
SRG_GPOS_URL = "https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=operating-systems%2Cgeneral-purpose-os"
SRG_PREFIX = "SRG-OS"
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Ensure that rules with STIG IDs also have SRG references."
)
parser.add_argument(
"-r",
"--root",
help=f"Root of the project. Defaults to {SSG_ROOT}",
default=SSG_ROOT,
)
parser.add_argument(
"-b",
"--build-root",
help=f"Path to the root the cmake build directory. Defaults to {BINARY_DIR}.",
default=BINARY_DIR,
)
parser.add_argument(
"-u",
"--srg-url",
help=f"URL for the SRG reference. Defaults to {SRG_GPOS_URL.replace('%', '%%')}.",
default=SRG_GPOS_URL,
)
parser.add_argument(
"--prefix",
help=f"Prefix used for the SRG reference. Defaults to {SRG_PREFIX}.",
default=SRG_PREFIX,
)
parser.add_argument("product")
return parser.parse_args()
def _print_missing(missing):
if len(missing) != 0:
for rule in missing:
content_id = rule.replace(f"{PREFIX}rule_", "")
print(f"Missing SRG in {content_id}")
exit(3)
def _get_srg_rules(ref_url, root, srg_prefix):
srg_rule = set()
for rule in root.findall(".//xccdf-1.2:Rule", ssg.constants.PREFIX_TO_NS):
ref = rule.find(
f".//xccdf-1.2:reference[@href='{ref_url}']", ssg.constants.PREFIX_TO_NS
)
if ref is not None and ref.text.startswith(srg_prefix):
srg_rule.add(rule.attrib.get("id"))
return srg_rule
def _get_stig_rules(stig_profile):
stig_rules = set()
for rule in stig_profile.findall(
"xccdf-1.2:select[@selected='true']", ssg.constants.PREFIX_TO_NS
):
stig_rules.add(rule.attrib.get("idref"))
return stig_rules
def _get_stig_profile(root):
profile_id = f"{PREFIX}profile_stig"
stig_profile = root.find(
f".//xccdf-1.2:Profile[@id='{profile_id}']", ssg.constants.PREFIX_TO_NS
)
return stig_profile
def _get_root(build_root, product):
filename = f"ssg-{product}-ds.xml"
datastream_path = os.path.join(build_root, filename)
root = ET.parse(datastream_path).getroot()
if not os.path.exists(datastream_path):
print(
f"Product {product} datastream dose not exist. Has it been built?",
file=sys.stderr,
)
exit(1)
return root
def main():
ssg.xml.register_namespaces()
args = _parse_args()
product = args.product
build_root = args.build_root
root = _get_root(build_root, product)
ref_url = args.srg_url
srg_prefix = args.prefix
stig_profile = _get_stig_profile(root)
stig_rules = _get_stig_rules(stig_profile)
srg_rules = _get_srg_rules(ref_url, root, srg_prefix)
missing = stig_rules - srg_rules
_print_missing(missing)
if __name__ == "__main__":
main()
|