Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add script to update generated SDS to SCAP 1.3 #4302

Merged
merged 13 commits into from
Apr 30, 2019
Merged
139 changes: 139 additions & 0 deletions build-scripts/update_sds_to_scap_1_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@

from __future__ import print_function

import sys
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse

import ssg.constants
import ssg.xml
ocil_ns = ssg.constants.ocil_namespace

oval_ns = ssg.constants.oval_namespace
xccdf_ns = ssg.constants.XCCDF12_NS
ds_ns = ssg.constants.datastream_namespace
xlink_ns = ssg.constants.xlink_namespace
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just renaming variables. Please use them directly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the import definition and changed code to use them.

cat_ns = ssg.constants.cat_namespace

component_ref_prefix = "#scap_org.open-scap_cref_"


# Inspired by openscap ds_sds_mangle_filepath() function
def mangle_path(path):
path = path.replace('/', '-')
path = path.replace('@', '-')
path = path.replace('~', '-')
return path


def move_patches_up_to_date_to_source_data_stream_component(datastreamtree):
ds_checklists = datastreamtree.find(".//{%s}checklists" % ds_ns)

for component_ref in ds_checklists:
component_ref_id = component_ref.get('id')
component_ref_href = component_ref.get('{%s}href' % xlink_ns)
# The component ID is the component-ref href without leading '#'
component_id = component_ref_href[1:]

# Locate the <xccdf:check> element of an <xccdf:Rule> with id security_patches_up_to_date
component = None
oval_check = None
components = datastreamtree.findall(".//{%s}component" % ds_ns)
for component in components:
if component.get('id') == component_id:
component = component
if component is None:
# Something strange happened
sys.stderr.write("Couldn't find <component> %s referenced by <component-ref> %s" %
(component_id, component_ref_id))
sys.exit(1)

rules = component.findall(".//{%s}Rule" % (xccdf_ns))
for rule in rules:
if rule.get('id').endswith('rule_security_patches_up_to_date'):
rule_checks = rule.findall("{%s}check" % xccdf_ns)
for check in rule_checks:
if check.get('system') == oval_ns:
oval_check = check
break

if oval_check is None:
# The component doesn't have a security patches up to date rule with an OVAL check
continue

# SCAP 1.3 demands multi-check true if the Rules security_patches_up_to_date is
# evaluated by multiple OVAL patch class definitinos.
# See 3.2.4.3, SCAP 1.3 standard (NIST.SP.800-126r3)
oval_check.set('multi-check', 'true')

check_content_ref = oval_check.find('{%s}check-content-ref' % xccdf_ns)
href_url = check_content_ref.get('href')

# Use URL's path to define the component name and URI
# Path attribute returned from urlparse contains a leading '/', when mangling it
# it will get replaced by '-'.
# Let's strip the '/' to avoid a sequence of "_-" in the component-ref ID.
component_ref_name = mangle_path(urlparse(href_url).path[1:])
component_ref_uri = component_ref_prefix + component_ref_name

# update @href to refer the datastream component name
check_content_ref.set('href', component_ref_name)

# Add a uri refering the component in Rule's Benchmark component-ref catalog
uri_exists = False
catalog = component_ref.find('{%s}catalog' % cat_ns)
uris = catalog.findall("{%s}uri" % (cat_ns))
for uri in uris:
if uri.get('name') == component_ref_name:
uri_exists = True
break
if not uri_exists:
uri = ssg.xml.ElementTree.Element('{%s}uri' % cat_ns)
uri.set('name', component_ref_name)
uri.set('uri', component_ref_uri)
catalog.append(uri)

# The component-ref ID is the catalog uri without leading '#'
component_ref_feed_id = component_ref_uri[1:]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I build RHEL 7 data stream I get in scap_org.open-scap_cref_ssg-rhel7-pcidss-xccdf-1.2.xml component-ref this element:
<ns2:uri name="ecurity-data-oval-com.redhat.rhsa-RHEL7.xml.bz2" uri="#scap_org.open-scap_cref_ecurity-data-oval-com.redhat.rhsa-RHEL7.xml.bz2"/>. There is missing s 2 times.

Copy link
Member Author

@yuumasato yuumasato Apr 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, I ended up introducing a bug during manual filtering. Fixed in ce7ec59.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commits 15c70d0 and 304270f rename variables to make them clear about what components they are about.

# Add the component-ref to list of datastreams' checks
check_component_ref_exists = False
ds_checks = datastreamtree.find(".//{%s}checks" % ds_ns)
check_component_refs = ds_checks.findall("{%s}component-ref" % ds_ns)
for check_component_ref in check_component_refs:
if check_component_ref.get('id') == component_ref_feed_id:
check_component_ref_exists = True
break
if not check_component_ref_exists:
component_ref_feed = ssg.xml.ElementTree.Element('{%s}component-ref' %
ds_ns)
component_ref_feed.set('id', component_ref_feed_id)
component_ref_feed.set('{%s}href' % xlink_ns, href_url)
ds_checks.append(component_ref_feed)


def main():
if len(sys.argv) < 3:
print("This script updates SCAP 1.2 Source DataStream to SCAP 1.3")
sys.exit(1)

# Input datastream file
indatastreamfile = sys.argv[1]
# Output datastream file
outdatastreamfile = sys.argv[2]
# Datastream element tree
datastreamtree = ssg.xml.ElementTree.parse(indatastreamfile).getroot()

# Set SCAP version to 1.3
datastreamtree.set('schematron-version', '1.3')
datastreamtree.find('{%s}data-stream' % ds_ns).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(datastreamtree)

ssg.xml.ElementTree.ElementTree(datastreamtree).write(outdatastreamfile)

if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions cmake/SSGCommon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ macro(ssg_build_sds PRODUCT)
COMMAND "${OPENSCAP_OSCAP_EXECUTABLE}" ds sds-add --skip-valid "ssg-${PRODUCT}-cpe-dictionary.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND "${OPENSCAP_OSCAP_EXECUTABLE}" ds sds-add --skip-valid "ssg-${PRODUCT}-pcidss-xccdf-1.2.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/sds_move_ocil_to_checks.py" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/update_sds_to_scap_1_3.py" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.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-1.2.xml
DEPENDS "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf-1.2.xml"
Expand All @@ -549,6 +550,7 @@ macro(ssg_build_sds PRODUCT)
COMMAND "${SED_EXECUTABLE}" -i 's/schematron-version="[0-9].[0-9]"/schematron-version="1.2"/' "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND "${OPENSCAP_OSCAP_EXECUTABLE}" ds sds-add --skip-valid "ssg-${PRODUCT}-cpe-dictionary.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/sds_move_ocil_to_checks.py" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_BUILD_SCRIPTS}/update_sds_to_scap_1_3.py" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.xml" "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-ds.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-1.2.xml
DEPENDS "${CMAKE_BINARY_DIR}/ssg-${PRODUCT}-xccdf-1.2.xml"
Expand Down
2 changes: 2 additions & 0 deletions ssg/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
ocil_namespace = "http://scap.nist.gov/schema/ocil/2.0"
oval_footer = "</oval_definitions>"
oval_namespace = "http://oval.mitre.org/XMLSchema/oval-definitions-5"
xlink_namespace = "http://www.w3.org/1999/xlink"
cat_namespace = "urn:oasis:names:tc:entity:xmlns:xml:catalog"
ocil_cs = "http://scap.nist.gov/schema/ocil/2"
xccdf_header = xml_version + "<xccdf>"
xccdf_footer = "</xccdf>"
Expand Down