[PATCH 3/3] support for generating OCIL, synchronizing/generating OCIL IDs automatically

Jeffrey Blank blank at eclipse.ncsc.mil
Mon Sep 17 22:55:59 UTC 2012


Signed-off-by: Jeffrey Blank <blank at eclipse.ncsc.mil>
---
 RHEL6/transforms/constants.xslt  |    4 +-
 RHEL6/transforms/cpe_generate.py |    4 +-
 RHEL6/transforms/idtranslate.py  |  206 +++++++++++++++++++++++---------------
 RHEL6/transforms/relabelids.py   |  151 ++++++++++++++++------------
 4 files changed, 217 insertions(+), 148 deletions(-)

diff --git a/RHEL6/transforms/constants.xslt b/RHEL6/transforms/constants.xslt
index 3159cc9..7289561 100644
--- a/RHEL6/transforms/constants.xslt
+++ b/RHEL6/transforms/constants.xslt
@@ -13,6 +13,8 @@
 <xsl:variable name="disa-cciuri">http://iase.disa.mil/cci/index.html</xsl:variable>
 
 <xsl:variable name="ovaluri">http://oval.mitre.org/XMLSchema/oval-definitions-5</xsl:variable>
-<xsl:variable name="ociluri">http://www.mitre.org/ocil/2</xsl:variable>
+
 <xsl:variable name="ociltransitional">ocil-transitional</xsl:variable>
+<xsl:variable name="ocil_cs">http://scap.nist.gov/schema/ocil/2</xsl:variable>
+<xsl:variable name="ocil_ns">http://scap.nist.gov/schema/ocil/2.0</xsl:variable>
 </xsl:stylesheet>
diff --git a/RHEL6/transforms/cpe_generate.py b/RHEL6/transforms/cpe_generate.py
index a42cc3b..dcc1448 100755
--- a/RHEL6/transforms/cpe_generate.py
+++ b/RHEL6/transforms/cpe_generate.py
@@ -88,7 +88,7 @@ def main():
 		ovaltree.remove(variables)
 
 	# turn IDs into meaningless numbers
-	translator = idtranslate.idtranslator("./output/"+idname+".ini", "oval:"+idname)
+	translator = idtranslate.idtranslator("./output/"+idname+".ini", idname)
 	ovaltree = translator.translate(ovaltree)
 
 	newovalfile = ovalfile.replace("oval", "cpe-oval-"+idname)
@@ -99,7 +99,7 @@ def main():
 	newcpedictfile = os.path.basename(cpedictfile).replace(".xml","-"+idname+".xml")
 	for check in cpedicttree.findall(".//{%s}check" % cpe_ns):
 		check.set("href",os.path.basename(newovalfile))
-		check.text = translator.assign_id("definition", check.text)	
+		check.text = translator.assign_id("{" + oval_ns + "}definition", check.text)	
 	ET.ElementTree(cpedicttree).write("./output/"+newcpedictfile)
 
 	sys.exit(0)
diff --git a/RHEL6/transforms/idtranslate.py b/RHEL6/transforms/idtranslate.py
index 638011c..77f078a 100755
--- a/RHEL6/transforms/idtranslate.py
+++ b/RHEL6/transforms/idtranslate.py
@@ -1,94 +1,138 @@
-import ConfigParser
+import ConfigParser, sys
 import lxml.etree as ET 
 
-# This class is designed to handle the mapping of human-readable names to
-# OVAL-style IDs.  This is intentionally similar to code in  
-# Tresys SCC, to enable future integration.
+# This class is designed to handle the mapping of meaningful, human-readable
+# names to IDs in the formats required by the SCAP checking systems, such as
+# OVAL and OCIL.  
 
-ovalns = "{http://oval.mitre.org/XMLSchema/oval-definitions-5}"
+oval_ns = "http://oval.mitre.org/XMLSchema/oval-definitions-5"
+oval_cs = "http://oval.mitre.org/XMLSchema/oval-definitions-5"
 
-keyword_to_abbrev = {
-    'definition' : 'def',
-    'criteria' : 'crit',
-    'test' : 'tst',
-    'object' : 'obj',
-    'state' : 'ste',
-    'variable' : 'var',
+ocil_ns = "http://scap.nist.gov/schema/ocil/2.0"
+ocil_cs = "http://scap.nist.gov/schema/ocil/2"
+
+ovaltag_to_abbrev = {
+	'definition' : 'def',
+	'criteria' : 'crit',
+	'test' : 'tst',
+	'object' : 'obj',
+	'state' : 'ste',
+	'variable' : 'var',
+}
+
+ociltag_to_abbrev = {
+	'questionnaire' : 'questionnaire',
+	'action' : 'testaction',
+	'question' : 'question',
+	'artifact' : 'artifact',
+	'variable' : 'variable',
+}
+
+ovalrefattr_to_tag = {
+	"definition_ref" : "definition",
+	"test_ref" : "test",
+	"object_ref" : "object",
+	"state_ref" : "state",
+	"var_ref" : "variable",
+}
+
+ocilrefattr_to_tag = {
+	"question_ref" : "question",
 }
 
-refattr_to_keyword = {
-    "definition_ref" : "definition",
-    "test_ref" : "test",
-    "object_ref" : "object",
-    "state_ref" : "state",
-    "var_ref" : "variable",
+ocilrefchild_to_tag = {
+	"test_action_ref" : "action",
 }
 
+def split_namespace(tag):
+	# returns a tuple of (namespace,name) removing any fragment id from namespace
+	if tag[:1] == "{":
+		namespace, name = tag[1:].split("}", 1)
+		return namespace.split("#")[0], name
+	else:
+		return (None,tag)
+
+def namespace_to_prefix(tag):
+	namespace, name = split_namespace(tag)
+	if namespace == ocil_ns:
+		return "ocil"
+	if namespace == oval_ns:
+		return "oval"
+	sys.exit("Error: unknown checksystem referenced in tag : %s" % tag)
+
 def tagname_to_abbrev(tag):
-    tag = tag.split("}")[-1]
-    if tag == "extend_definition":
-        return tag
-    tag = tag.rsplit("_", 1)[-1]
-    return keyword_to_abbrev[tag]
+	namespace, tag = split_namespace(tag)
+	if tag == "extend_definition":
+		return tag
+	# grab the last part of the tag name to determine its type
+	tag = tag.rsplit("_", 1)[-1]
+	if namespace == ocil_ns:
+		return ociltag_to_abbrev[tag]
+	if namespace == oval_ns:
+		return ovaltag_to_abbrev[tag]
+	sys.exit("Error: unknown checksystem referenced in tag : %s" % tag)
 
 class idtranslator:
-    def __init__(self, fname, prefix):
-        self.fname = fname 
-        self.prefix = prefix
-        self.config = ConfigParser.ConfigParser()
-        f = self.config.read(fname)
-        if len(f) == 0:
-            self.__setup()
-
-    def __get_next_id(self):
-        i = self.config.getint("general", "next_id")
-        n = "%d" % (i + 1)
-        self.config.set("general", "next_id", n)
-        return i
-
-    def save(self):
-        fd = open(self.fname, "wb")
-        self.config.write(fd)
-
-    def __setup(self):
-        self.config.add_section("general")
-        self.config.set("general", "id_prefix", self.prefix)
-        self.config.set("general", "next_id", "100")
-        self.config.add_section("assigned")
-
-    def assign_id(self, tagname, name):
-        i = None
-        try:
-            i = self.config.getint("assigned", name)
-        except:
-            i = self.__get_next_id()
-            self.config.set("assigned", name, str(i))
-
-        pre = self.config.get("general", "id_prefix")
-        str_id = "%s:%s:%d" % (pre, tagname_to_abbrev(tagname), i)
-        return str_id
-
-    def translate(self, tree, store_defname=False, refsource=""):
-        for element in tree.getiterator():
-            idname = element.get("id")
-            if idname:
-                # store the old name if requested (for OVAL definitions)
-                if store_defname and element.tag == ovalns + "definition":
-                    metadata = element.find(ovalns + "metadata")
-                    if metadata is None:
-                        metadata = ET.SubElement(element, "metadata")
-                    defnam = ET.SubElement(metadata, "reference", ref_id=idname, source=refsource) 
-                # set the element to the new identifier 
-                element.set("id", self.assign_id(element.tag, idname))
-                continue
-            if element.tag == ovalns + "filter":
-                element.text = self.assign_id("state", element.text)
-                continue
-            for attr in element.keys():
-                if attr in refattr_to_keyword.keys():
-                    element.set(attr, self.assign_id(refattr_to_keyword[attr], element.get(attr)))
-        self.save()
-        # note: the ini file is not tracked by git, see .gitignore
-        return tree
+	def __init__(self, fname, content_id):
+		self.fname = fname 
+		self.content_id = content_id
+		self.config = ConfigParser.ConfigParser()
+		f = self.config.read(fname)
+		if len(f) == 0:
+			self.__setup()
+
+	def __get_next_id(self):
+		i = self.config.getint("general", "next_id")
+		n = "%d" % (i + 1)
+		self.config.set("general", "next_id", n)
+		return i
+
+	def save(self):
+		fd = open(self.fname, "wb")
+		self.config.write(fd)
+
+	def __setup(self):
+		self.config.add_section("general")
+		self.config.set("general", "next_id", "100")
+		self.config.add_section("assigned")
+
+	def assign_id(self, tagname, name):
+		i = None
+		try:
+			i = self.config.getint("assigned", name)
+		except:
+			i = self.__get_next_id()
+			self.config.set("assigned", name, str(i))
+
+		str_id = "%s:%s:%s:%d" % (namespace_to_prefix(tagname), self.content_id, tagname_to_abbrev(tagname), i)
+		return str_id
+
+	def translate(self, tree, store_defname=False):
+		for element in tree.getiterator():
+			idname = element.get("id")
+			if idname:
+				# store the old name if requested (for OVAL definitions)
+				if store_defname and element.tag == "{" + oval_ns + "}definition":
+					metadata = element.find("{" + oval_ns + "}metadata")
+					if metadata is None:
+						metadata = ET.SubElement(element, "metadata")
+					defnam = ET.SubElement(metadata, "reference", ref_id=idname, source=self.content_id) 
+				# set the element to the new identifier 
+				element.set("id", self.assign_id(element.tag, idname))
+				#continue
+			if element.tag == "{" + oval_ns + "}filter":
+				element.text = self.assign_id("{" + oval_ns + "}state", element.text)
+				continue
+			for attr in element.keys():
+				if attr in ovalrefattr_to_tag.keys():
+					element.set(attr,self.assign_id( "{" + oval_ns + "}" + ovalrefattr_to_tag[attr], element.get(attr)))
+				if attr in ocilrefattr_to_tag.keys():
+					element.set(attr, self.assign_id("{" + ocil_ns + "}" + ocilrefattr_to_tag[attr], element.get(attr)))
+			if element.tag == "{" + ocil_ns + "}test_action_ref":
+					element.text = self.assign_id("{" + ocil_ns + "}action", element.text)
+
+		self.save()
+		# note: the ini file is not tracked by git, see .gitignore
+		return tree
 
 
diff --git a/RHEL6/transforms/relabelids.py b/RHEL6/transforms/relabelids.py
index 966696c..7068437 100755
--- a/RHEL6/transforms/relabelids.py
+++ b/RHEL6/transforms/relabelids.py
@@ -5,81 +5,104 @@ import idtranslate
 import lxml.etree as ET
 
 # This script requires two arguments: an XCCDF file and an ID name scheme.
-# This script is designed to convert and synchronize all IDs referenced from the XCCDF document.
-# These references would typically be inside OVAL documents, but we also looking to OCIL.
-# The IDs are to be converted from strings to meaningless numbers.
+# This script is designed to convert and synchronize check IDs referenced from
+# the XCCDF document for the supported checksystems, which are currently OVAL
+# and OCIL.  The IDs are to be converted from strings to meaningless numbers.
 
 oval_ns = "http://oval.mitre.org/XMLSchema/oval-definitions-5"
+oval_cs = "http://oval.mitre.org/XMLSchema/oval-definitions-5"
+
+ocil_ns = "http://scap.nist.gov/schema/ocil/2.0"
+ocil_cs = "http://scap.nist.gov/schema/ocil/2"
+
 xccdf_ns = "http://checklists.nist.gov/xccdf/1.1" 
 
 
 def parse_xml_file(xmlfile):
-    with open( xmlfile, 'r') as f:
-        filestring = f.read()
-        tree = ET.fromstring(filestring)  
-        #print filestring
-    return tree
+	with open( xmlfile, 'r') as f:
+		filestring = f.read()
+		tree = ET.fromstring(filestring)  
+		#print filestring
+	return tree
 
 
-def get_ovalfiles(checks):
-    # iterate over all checks, grab the OVAL files referenced within
-    ovalfiles = set()
-    for check in checks:
-        if check.get("system") == oval_ns:
-            checkcontentref = check.find("./{%s}check-content-ref" % xccdf_ns)
-            ovalfiles.add(checkcontentref.get("href"))
-#        else:
-#            print "Non-OVAL checking system found: " + check.get("system")
-    return ovalfiles
+def get_checkfiles(checks, checksystem):
+	# iterate over all checks, grab the OVAL files referenced within
+	checkfiles = set()
+	for check in checks:
+		if check.get("system") == checksystem:
+			checkcontentref = check.find("./{%s}check-content-ref" % xccdf_ns)
+			checkfiles.add(checkcontentref.get("href"))
+	return checkfiles
 
 
 def main():
-    if len(sys.argv) < 3:
-        print "Provide an XCCDF file and an ID name scheme."
-        print "This script finds check-content files (currently, OVAL) referenced from XCCDF and synchronizes all IDs."
-        sys.exit(1)
-
-    xccdffile = sys.argv[1]
-    idname = sys.argv[2]
-
-    os.chdir("./output")
-    # step over xccdf file, and find referenced oval files
-    xccdftree = parse_xml_file(xccdffile)
-
-    checks = xccdftree.findall(".//{%s}check" % xccdf_ns)
-    ovalfiles = get_ovalfiles(checks)
-
-    if len(ovalfiles) > 1:
-        sys.exit("referencing more than one OVAL file is not yet supported by this script.")
-    ovalfile = ovalfiles.pop()
-
-    # rename all IDs in the oval file
-    ovaltree = parse_xml_file(ovalfile) 
-    translator = idtranslate.idtranslator(idname+".ini", "oval:"+idname)
-    ovaltree = translator.translate(ovaltree, store_defname=True, refsource="scap-security-guide")
-
-    newovalfile = ovalfile.replace(".xml", "-" + idname + ".xml")
-    ET.ElementTree(ovaltree).write(newovalfile)
-
-    # rename all IDs and file refs in the xccdf file
-    for check in checks:
-        checkcontentref = check.find("./{%s}check-content-ref" % xccdf_ns)
-        if checkcontentref is None:
-            continue
-        checkid = translator.assign_id("definition", checkcontentref.get("name"))
-        checkcontentref.set("name", checkid)
-        checkcontentref.set("href", newovalfile)
-
-        checkexport = check.find("./{%s}check-export" % xccdf_ns)
-        if checkexport is not None:
-            newexportname = translator.assign_id("variable", checkexport.get("export-name"))
-            checkexport.set("export-name", newexportname) 
-
-    newxccdffile = xccdffile.replace(".xml", "-" + idname + ".xml")
-    #ET.dump(xccdftree)
-    ET.ElementTree(xccdftree).write(newxccdffile)
-    sys.exit(0)
+	if len(sys.argv) < 3:
+		print "Provide an XCCDF file and an ID name scheme."
+		print "This script finds check-content files (currently, OVAL and OCIL) referenced from XCCDF and synchronizes all IDs."
+		sys.exit(1)
+
+	xccdffile = sys.argv[1]
+	idname = sys.argv[2]
+
+	os.chdir("./output")
+	# step over xccdf file, and find referenced check files
+	xccdftree = parse_xml_file(xccdffile)
+
+	checks = xccdftree.findall(".//{%s}check" % xccdf_ns)
+	ovalfiles = get_checkfiles(checks, oval_cs)
+	ocilfiles = get_checkfiles(checks, ocil_cs)
+
+	if len(ovalfiles) > 1 or len(ocilfiles) > 1:
+		sys.exit("referencing more than one file per check system is not yet supported by this script.")
+	ovalfile = ovalfiles.pop() if ovalfiles else None
+	ocilfile = ocilfiles.pop() if ocilfiles else None
+
+	translator = idtranslate.idtranslator(idname+".ini", idname)
+
+	# rename all IDs in the oval file
+	if ovalfile:
+		ovaltree = parse_xml_file(ovalfile) 
+		ovaltree = translator.translate(ovaltree, store_defname=True)
+		newovalfile = ovalfile.replace(".xml", "-" + idname + ".xml")
+		ET.ElementTree(ovaltree).write(newovalfile)
+
+	# rename all IDs in the ocil file
+	if ocilfile:
+		ociltree = parse_xml_file(ocilfile) 
+		ociltree = translator.translate(ociltree)
+		newocilfile = ocilfile.replace(".xml", "-" + idname + ".xml")
+		ET.ElementTree(ociltree).write(newocilfile)
+
+	# rename all IDs and file refs in the xccdf file
+	for check in checks:
+		checkcontentref = check.find("./{%s}check-content-ref" % xccdf_ns)
+		if checkcontentref is None:
+			continue
+
+		if check.get("system") == oval_cs:
+			checkid = translator.assign_id("{" + oval_ns + "}definition", checkcontentref.get("name"))
+			checkcontentref.set("name", checkid)
+			checkcontentref.set("href", newovalfile)
+			checkexport = check.find("./{%s}check-export" % xccdf_ns)
+			if checkexport is not None:
+				newexportname = translator.assign_id("{"+ oval_ns + "}variable", checkexport.get("export-name"))
+				checkexport.set("export-name", newexportname) 
+
+		if check.get("system") == ocil_cs:
+			checkid = translator.assign_id("{" + ocil_ns + "}questionnaire", checkcontentref.get("name"))
+			checkcontentref.set("name", checkid)
+			checkcontentref.set("href", newocilfile)
+			checkexport = check.find("./{%s}check-export" % xccdf_ns)
+			if checkexport is not None:
+				newexportname = translator.assign_id("{"+ oval_ns + "}variable", checkexport.get("export-name"))
+				checkexport.set("export-name", newexportname) 
+
+	newxccdffile = xccdffile.replace(".xml", "-" + idname + ".xml")
+	#ET.dump(xccdftree)
+	ET.ElementTree(xccdftree).write(newxccdffile)
+	sys.exit(0)
 
 if __name__ == "__main__":
-    main()
+	main()
 
-- 
1.7.1



More information about the scap-security-guide mailing list