Skip to content

Commit

Permalink
build support: when SCE support is enabled, include the SCE scripts i…
Browse files Browse the repository at this point in the history
…n the output datastream
  • Loading branch information
nightmared committed Jan 7, 2023
1 parent 2a3341e commit a6c33d8
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 16 deletions.
80 changes: 76 additions & 4 deletions build-scripts/compose_ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import xml.etree.ElementTree as ET

from ssg.constants import (
cat_namespace, datastream_namespace, oval_namespace, XCCDF12_NS,
xlink_namespace)
cat_namespace, datastream_namespace, oval_namespace, sce_namespace,
SCE_SYSTEM, XCCDF12_NS, xlink_namespace)
import ssg.xml

try:
Expand All @@ -29,6 +29,72 @@ def mangle_path(path):
return path


# Retrieve the SCE checks and return a list of path to each check script.
def collect_sce_checks(datastreamtree, checklists):
checklists_component_ref = checklists.find(
"{%s}component-ref" % datastream_namespace)
# The component ID is the component-ref href without leading '#'
checklist_component_id = checklists_component_ref.get('{%s}href' % xlink_namespace)[1:]

checks_xpath = str.format(
".//{{{ds_ns}}}component[@id='{cid}']/"
"{{{xccdf_ns}}}Benchmark//"
"{{{xccdf_ns}}}Rule/"
"{{{xccdf_ns}}}check[@system='{sce_sys}']/"
"{{{xccdf_ns}}}check-content-ref",
ds_ns=datastream_namespace,
xccdf_ns=XCCDF12_NS,
cid=checklist_component_id,
sce_sys=SCE_SYSTEM
)

checks = datastreamtree.findall(checks_xpath)
# Extract the file paths of the SCE checks
return [check.get('href') for check in checks]


# From the list generated by collect_sce_checks, extract the path to the check content,
# and embed the script into the datastream
def embed_sce_checks_in_datastream(datastreamtree, checklists, sce_files, refdir):
for file in sce_files:
path = os.path.join(refdir, file)
mangled_path = mangle_path(file)

with open(path, 'rt', encoding='utf8') as fd:
sce_script_content = fd.read()

component_id = "scap_{}_ecomp_{}".format(ID_NS, mangled_path)
component = ET.SubElement(
datastreamtree, '{%s}extended-component' % datastream_namespace,
attrib={
'id': component_id,
'timestamp': get_timestamp(path)
})
# Append the file content
script_data = ET.SubElement(component, '{%s}script' % sce_namespace)
script_data.text = sce_script_content

# Create a component reference to map the checklist to the extended component
component_ref_id = "scap_{}_cref_{}".format(ID_NS, mangled_path)
component_ref = ET.SubElement(
checklists, '{%s}component-ref' % datastream_namespace,
attrib={
'id': component_ref_id,
('{%s}href' % xlink_namespace): '#' + component_id
})

# Add the component reference to the catalog of XCCDF checklists
checklists_component_ref = checklists.find(
"{%s}component-ref" % datastream_namespace)
catalog = checklists_component_ref.find('{%s}catalog' % cat_namespace)
uri = ET.SubElement(
catalog, '{%s}uri' % cat_namespace,
attrib={
'name': file,
'uri': '#' + component_ref_id
})


def move_patches_up_to_date_to_source_data_stream_component(datastreamtree):
ds_checklists = datastreamtree.find(
".//{%s}checklists" % datastream_namespace)
Expand Down Expand Up @@ -130,6 +196,7 @@ def parse_args():
parser.add_argument("--ocil", help="OCIL file name")
parser.add_argument("--cpe-dict", help="CPE dictionary file name")
parser.add_argument("--cpe-oval", help="CPE OVAL file name")
parser.add_argument("--enable-sce", action='store_true', help="Enable building sce data")
parser.add_argument(
"--output-12", help="Output SCAP 1.2 source data stream file name")
parser.add_argument(
Expand Down Expand Up @@ -180,7 +247,7 @@ def create_catalog(component_ref, dependencies):

def compose_ds(
xccdf_file_name, oval_file_name, ocil_file_name,
cpe_dict_file_name, cpe_oval_file_name):
cpe_dict_file_name, cpe_oval_file_name, sce_enabled):
ds_collection = ET.Element(
"{%s}data-stream-collection" % datastream_namespace)
name = "from_xccdf_" + os.path.basename(xccdf_file_name)
Expand All @@ -202,6 +269,10 @@ def compose_ds(
add_component(ds_collection, checks, oval_file_name)
add_component(ds_collection, checks, ocil_file_name)
add_component(ds_collection, checks, cpe_oval_file_name)
if sce_enabled:
sce_checks = collect_sce_checks(ds_collection, checklists)
refdir = os.path.dirname(oval_file_name)
embed_sce_checks_in_datastream(ds_collection, checklists, sce_checks, refdir)
return ET.ElementTree(ds_collection)


Expand All @@ -210,6 +281,7 @@ def upgrade_ds_to_scap_13(ds):
dsc_el.set("schematron-version", "1.3")
ds_el = ds.find("{%s}data-stream" % datastream_namespace)
ds_el.set("scap-version", '1.3')

# Move reference to remote OVAL content to a source data stream component
move_patches_up_to_date_to_source_data_stream_component(ds)
return ds
Expand All @@ -219,7 +291,7 @@ def upgrade_ds_to_scap_13(ds):
args = parse_args()
ssg.xml.register_namespaces()
ds = compose_ds(
args.xccdf, args.oval, args.ocil, args.cpe_dict, args.cpe_oval)
args.xccdf, args.oval, args.ocil, args.cpe_dict, args.cpe_oval, args.enable_sce)
if args.output_12:
ds.write(args.output_12)
ds_13 = upgrade_ds_to_scap_13(ds)
Expand Down
23 changes: 12 additions & 11 deletions cmake/SSGCommon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ macro(ssg_build_xccdf_oval_ocil PRODUCT)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/profiles"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/profiles"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compile_all.py" --resolved-base "${CMAKE_CURRENT_BINARY_DIR}" --controls-dir "${CMAKE_SOURCE_DIR}/controls" --build-config-yaml "${CMAKE_BINARY_DIR}/build_config.yml" --product-yaml "${CMAKE_CURRENT_SOURCE_DIR}/product.yml" --sce-metadata "${CMAKE_CURRENT_BINARY_DIR}/checks_from_templates/sce/metadata.json"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compile_all.py" --resolved-base "${CMAKE_CURRENT_BINARY_DIR}" --controls-dir "${CMAKE_SOURCE_DIR}/controls" --build-config-yaml "${CMAKE_BINARY_DIR}/build_config.yml" --product-yaml "${CMAKE_CURRENT_SOURCE_DIR}/product.yml" --sce-metadata "${CMAKE_CURRENT_BINARY_DIR}/checks/sce/metadata.json"
DEPENDS generate-internal-${PRODUCT}-sce-metadata.json
COMMENT "[${PRODUCT}-content] compiling everything"
)
else()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/profiles"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/profiles"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compile_all.py" --resolved-base "${CMAKE_CURRENT_BINARY_DIR}" --controls-dir "${CMAKE_SOURCE_DIR}/controls" --build-config-yaml "${CMAKE_BINARY_DIR}/build_config.yml" --product-yaml "${CMAKE_CURRENT_SOURCE_DIR}/product.yml" --sce-metadata "${CMAKE_CURRENT_BINARY_DIR}/checks_from_templates/sce/metadata.json" --stig-references "${STIG_REFERENCE_FILE}"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compile_all.py" --resolved-base "${CMAKE_CURRENT_BINARY_DIR}" --controls-dir "${CMAKE_SOURCE_DIR}/controls" --build-config-yaml "${CMAKE_BINARY_DIR}/build_config.yml" --product-yaml "${CMAKE_CURRENT_SOURCE_DIR}/product.yml" --sce-metadata "${CMAKE_CURRENT_BINARY_DIR}/checks/sce/metadata.json" --stig-references "${STIG_REFERENCE_FILE}"
DEPENDS generate-internal-${PRODUCT}-sce-metadata.json
COMMENT "[${PRODUCT}-content] compiling everything"
)
Expand Down Expand Up @@ -278,7 +278,7 @@ endmacro()
# (without needing a separate XML or XSLT linking step) and also place
# <complex-check /> elements as necessary.
macro(ssg_build_sce PRODUCT)
set(BUILD_CHECKS_DIR "${CMAKE_CURRENT_BINARY_DIR}/checks_from_templates")
set(BUILD_CHECKS_DIR "${CMAKE_CURRENT_BINARY_DIR}/checks")
# Unlike build_oval_unlinked, here we're ignoring the existing checks from
# templates and other places and we're merely appending/templating the
# content from the rules directories. That's why we ignore BUILD_CHECKS_DIR
Expand Down Expand Up @@ -311,7 +311,7 @@ macro(ssg_build_sce PRODUCT)
endif()
add_custom_target(
generate-internal-${PRODUCT}-sce-metadata.json
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/checks_from_templates/sce/metadata.json"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/checks/sce/metadata.json"
)
endmacro()

Expand Down Expand Up @@ -410,11 +410,17 @@ endmacro()
# evaluation using e.g., OpenSCAP) by combining XCCDF, OVAL, SCE, and OCIL
# content. This relies heavily on the OpenSCAP executable here.
macro(ssg_build_sds PRODUCT)
if(SSG_SCE_ENABLED)
set(COMPOSE_EXTRA_ARGS "--enable-sce")
else()
set(COMPOSE_EXTRA_ARGS "")
endif()

if(SSG_BUILD_SCAP_12_DS)
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds-1.2.xml"
OUTPUT "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compose_ds.py" --xccdf "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf.xml" --oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-oval.xml" --ocil "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ocil.xml" --cpe-dict "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-dictionary.xml" --cpe-oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-oval.xml" --output-12 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds-1.2.xml" --output-13 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compose_ds.py" --xccdf "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf.xml" --oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-oval.xml" --ocil "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ocil.xml" --cpe-dict "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-dictionary.xml" --cpe-oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-oval.xml" --output-12 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds-1.2.xml" --output-13 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" ${COMPOSE_EXTRA_ARGS}
COMMAND "${XMLLINT_EXECUTABLE}" --nsclean --format --output "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds-1.2.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds-1.2.xml"
COMMAND "${XMLLINT_EXECUTABLE}" --nsclean --format --output "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
DEPENDS generate-ssg-${PRODUCT}-xccdf.xml
Expand All @@ -431,7 +437,7 @@ macro(ssg_build_sds PRODUCT)
else()
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compose_ds.py" --xccdf "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf.xml" --oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-oval.xml" --ocil "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ocil.xml" --cpe-dict "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-dictionary.xml" --cpe-oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-oval.xml" --output-13 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/compose_ds.py" --xccdf "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf.xml" --oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-oval.xml" --ocil "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ocil.xml" --cpe-dict "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-dictionary.xml" --cpe-oval "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-cpe-oval.xml" --output-13 "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" ${COMPOSE_EXTRA_ARGS}
COMMAND "${XMLLINT_EXECUTABLE}" --nsclean --format --output "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
DEPENDS generate-ssg-${PRODUCT}-xccdf.xml
DEPENDS generate-ssg-${PRODUCT}-oval.xml
Expand Down Expand Up @@ -758,11 +764,6 @@ macro(ssg_build_product PRODUCT)
DESTINATION "${SSG_CONTENT_INSTALL_DIR}")
endif()

if (SSG_SCE_ENABLED)
install(DIRECTORY "${CMAKE_BINARY_DIR}/${PRODUCT}/checks/sce/"
DESTINATION "${SSG_CONTENT_INSTALL_DIR}/${PRODUCT}/checks/sce")
endif()

install(FILES "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
DESTINATION "${SSG_CONTENT_INSTALL_DIR}")

Expand Down
2 changes: 2 additions & 0 deletions ssg/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
xhtml_namespace = "http://www.w3.org/1999/xhtml"
xsi_namespace = "http://www.w3.org/2001/XMLSchema-instance"
cat_namespace = "urn:oasis:names:tc:entity:xmlns:xml:catalog"
sce_namespace = "http://open-scap.org/page/SCE_xccdf_stream"
ocil_cs = "http://scap.nist.gov/schema/ocil/2"
xccdf_header = xml_version + "<xccdf>"
xccdf_footer = "</xccdf>"
Expand Down Expand Up @@ -140,6 +141,7 @@
"cpe-dict": "http://cpe.mitre.org/dictionary/2.0",
"cat": cat_namespace,
"cpe-lang": "http://cpe.mitre.org/language/2.0",
"sce": sce_namespace,
}

FIX_TYPE_TO_SYSTEM = {
Expand Down
2 changes: 1 addition & 1 deletion ssg/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"kubernetes": TemplatingLang("kubernetes", ".yml", TemplateType.REMEDIATION, "kubernetes"),
"oval": TemplatingLang("oval", ".xml", TemplateType.CHECK, "oval"),
"puppet": TemplatingLang("puppet", ".pp", TemplateType.REMEDIATION, "puppet"),
"sce-bash": TemplatingLang("sce-bash", ".sh", TemplateType.REMEDIATION, "sce")
"sce-bash": TemplatingLang("sce-bash", ".sh", TemplateType.CHECK, "sce")
}

PREPROCESSING_FILE_NAME = "template.py"
Expand Down

0 comments on commit a6c33d8

Please sign in to comment.