Hi Nicholas, sorry for the long delay in getting back to you on this topic. I finally set aside the time to go through your work, and it's quite impressive. I'll need to do a bit more testing, but we should be able to integrate your contribution into the security repository, and use that to provide OVAL definitions again.
Thanks again for your work. Cheers, --Seb On Nov/13, Luedtke, Nicholas S wrote: > Attached is a working solution to this bug. Right now parsing the JSON > Security Tracker Information results in a one definition per CVE. I > hope to reduce this to one definition per package. (Shouldn't be too > much work) Also included in the tar is are more updates to the legacy > versions of parsing the dsa/wml files. If you need a debian oval > interpreter I previously attached one to this forum. > > I encourage you to use -h option for the JSON parsing script as > downloading the security tracker each time is cumbersome. > > shasums: > 5d13f72cccc6ad1774e2437c98c26bebfc2100ac 68910 diff.txt > 57fa67e77ed5212d65deca0483b7ac90b5bb7203 21423 oval.tar.gz > > -- > Nicholas Luedtke > Linux for HPE Helion OpenStack, Hewlett Packard Enterprise > Binary files oval_old/oval/definition/differ.pyc and > oval/oval/definition/differ.pyc differ > diff -urN oval_old/oval/definition/generator.py > oval/oval/definition/generator.py > --- oval_old/oval/definition/generator.py 2011-10-12 15:14:34.000000000 > -0600 > +++ oval/oval/definition/generator.py 2015-11-13 11:20:30.043834000 -0700 > @@ -1,5 +1,5 @@ > # -*- coding: utf-8 -*- > -# oval.definitio.generator - generate well-formed xml file with > +# oval.definition.generator - generate well-formed xml file with > # OVAL definitions of Debian Security Advisories. > # Use various optimizations to minimize result XML > # > @@ -19,44 +19,44 @@ > RE_XML_ILLEGAL = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' > + u'|' + u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % > (unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff), > unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff), > unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff)) > regex = re.compile(RE_XML_ILLEGAL) > > - > + > class OvalGeneratorException (Exception): > pass > > class DSAFormatException (OvalGeneratorException): > - code = 1 > - > + code = 1 > + > def __createXMLElement (name, descr = None, attrs = {}): > - """ > - Create XML element with text descr and attributes attrs > - > - Keyword arguments: > - name -- Name of XML element > - descr -- content of textNode (default None) > - attrs -- attributes of element (default {}) > - > - Return created XML element > - """ > - > - doc = xml.dom.minidom.Document () > - element = doc.createElement (name) > - > - for (attr, value) in attrs.items(): > - for match in regex.finditer(attr): > - attr = attr[:match.start()] + "?" + attr[match.end():] > - for match in regex.finditer(value): > - value = value[:match.start()] + "?" + value[match.end():] > - attribute = doc.createAttribute (attr.encode("utf8")) > - attribute.value = value.encode("utf8") > - element.attributes.setNamedItem (attribute) > - > - if descr != None: > - for match in regex.finditer(descr): > - descr = descr[:match.start()] + "?" + descr[match.end():] > - description = doc.createTextNode (descr.encode("utf8")) > - element.appendChild (description) > - > - return (element) > + """ > + Create XML element with text descr and attributes attrs > + > + Keyword arguments: > + name -- Name of XML element > + descr -- content of textNode (default None) > + attrs -- attributes of element (default {}) > + > + Return created XML element > + """ > + > + doc = xml.dom.minidom.Document () > + element = doc.createElement (name) > + > + for (attr, value) in attrs.items(): > + for match in regex.finditer(attr): > + attr = attr[:match.start()] + "?" + attr[match.end():] > + for match in regex.finditer(value): > + value = value[:match.start()] + "?" + value[match.end():] > + attribute = doc.createAttribute (attr.encode("utf8")) > + attribute.value = value.encode("utf8") > + element.attributes.setNamedItem (attribute) > + > + if descr != None: > + for match in regex.finditer(descr): > + descr = descr[:match.start()] + "?" + descr[match.end():] > + description = doc.createTextNode (descr.encode("utf8")) > + element.appendChild (description) > + > + return (element) > > namespace = "oval:org.debian.oval" > tests = __createXMLElement ("tests") > @@ -66,464 +66,464 @@ > testsCurId = 1 > objectsCurId = 1 > statesCurId = 1 > - > releaseArchHash = {"2.0" : 2, "2.1" : 4, "2.2": 6, "3.0" : 11, "3.1" : 12, > "4.0" : 11, "5.0": 12, "6.0": 11} > testsHash = {"arch" : {}, "release": {}, "obj": {}, "fileSte": {}, > "unameSte" : {}, "dpkgSte": {}} > #We need more info about alpha, arm, hppa, bmips, lmips > unameArchTable = {'i386' : 'i686', 'amd64' : 'x86-64', 'ia64' : 'ia64', > 'powerpc' : 'ppc', 's390' : 's390x', 'm86k' : 'm86k'} > > def __trimzero (val): > - value = val[:] > - while value[0] == "0": > - value = value[1:] > - return value > + value = val[:] > + while value[0] == "0": > + value = value[1:] > + return value > > def __getNewId (type): > - """Generate new unique id for tests, objects or states > - > - Argument keqywords: > - type -- type of generated id test | object | state > - > - return Generate id like <namespace>:tst|obj|ste:<id> > - """ > - global testsCurId, objectsCurId, statesCurId > - > - if type == "test": > - result = "%s:tst:%d" % (namespace, testsCurId) > - testsCurId += 1 > - > - if type == "object": > - result = "%s:obj:%d" % (namespace, objectsCurId) > - objectsCurId += 1 > - > - if type == "state": > - result = "%s:ste:%d" % (namespace, statesCurId) > - statesCurId += 1 > - > - return (result) > + """Generate new unique id for tests, objects or states > + > + Argument keqywords: > + type -- type of generated id test | object | state > + > + return Generate id like <namespace>:tst|obj|ste:<id> > + """ > + global testsCurId, objectsCurId, statesCurId > + > + if type == "test": > + result = "%s:tst:%d" % (namespace, testsCurId) > + testsCurId += 1 > + > + if type == "object": > + result = "%s:obj:%d" % (namespace, objectsCurId) > + objectsCurId += 1 > + > + if type == "state": > + result = "%s:ste:%d" % (namespace, statesCurId) > + statesCurId += 1 > + > + return result > > def __createOVALDpkginfoObject (name): > - """ Generate OVAL dpkginfo_object definition """ > - > - if not testsHash["obj"].has_key(name): > - objectId = __getNewId ("object"); > - object = __createXMLElement("dpkginfo_object", > - attrs={"id":objectId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux"}) > - object.appendChild ( __createXMLElement ("name", name)) > - objects.appendChild (object) > - > - testsHash["obj"][name] = objectId > - > - return (testsHash["obj"][name]) > + """ Generate OVAL dpkginfo_object definition """ > + > + if not testsHash["obj"].has_key(name): > + objectId = __getNewId ("object"); > + object = __createXMLElement("dpkginfo_object", > + attrs={"id":objectId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux"}) > + object.appendChild ( __createXMLElement ("name", name)) > + objects.appendChild (object) > + > + testsHash["obj"][name] = objectId > + > + return (testsHash["obj"][name]) > > def __createOVALTextfilecontentObject (pattern, path = "/etc", filename = > "debian_version"): > - """ Generate OVAL textfilecontent_object definition """ > - name = path + filename + pattern > - > - if not testsHash["obj"].has_key(name): > - objectId = __getNewId ("object"); > - object = __createXMLElement("textfilecontent_object", > - attrs={"id":objectId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent"}) > - object.appendChild ( __createXMLElement ("path", path)) > - object.appendChild ( __createXMLElement ("filename", filename)) > - object.appendChild ( __createXMLElement ("line", pattern, > attrs={"operation" : "pattern match"})) > - objects.appendChild (object) > - > - testsHash["obj"][name] = objectId > - > - return (testsHash["obj"][name]) > + """ Generate OVAL textfilecontent_object definition """ > + name = path + filename + pattern > + > + if not testsHash["obj"].has_key(name): > + objectId = __getNewId ("object"); > + object = __createXMLElement("textfilecontent_object", > + attrs={"id":objectId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent"}) > + object.appendChild ( __createXMLElement ("path", path)) > + object.appendChild ( __createXMLElement ("filename", filename)) > + object.appendChild ( __createXMLElement ("line", pattern, > attrs={"operation" : "pattern match"})) > + objects.appendChild (object) > + > + testsHash["obj"][name] = objectId > + > + return (testsHash["obj"][name]) > > def __createOVALUnameObject (): > - """ Generate OVAL textfilecontent_object definition """ > - name = "uname_object" > - > - if not testsHash["obj"].has_key(name): > - objectId = __getNewId ("object"); > - object = __createXMLElement("uname_object", > - attrs={"id":objectId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix"}) > - objects.appendChild (object) > - > - testsHash["obj"][name] = objectId > - > - return (testsHash["obj"][name]) > + """ Generate OVAL textfilecontent_object definition """ > + name = "uname_object" > + > + if not testsHash["obj"].has_key(name): > + objectId = __getNewId ("object"); > + object = __createXMLElement("uname_object", > + attrs={"id":objectId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix"}) > + objects.appendChild (object) > + > + testsHash["obj"][name] = objectId > + > + return (testsHash["obj"][name]) > > def __createOVALState (value, operation = "less than"): > - """ Generate OVAL state definition > - > - Use state hash for optimization of resulted XML > - """ > - #TODO: Add arch state generation > - if not testsHash["dpkgSte"].has_key(operation) or not > testsHash["dpkgSte"][operation].has_key(value): > - stateId = __getNewId ("state") > - > - state = __createXMLElement("dpkginfo_state", > - attrs={"id":stateId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux"}) > - state.appendChild ( __createXMLElement ("evr", "0:"+value, > - > {"datatype":"evr_string", > - > "operation":operation})) > - states.appendChild (state) > - > - testsHash["dpkgSte"][operation] = {value : stateId} > - > - return (testsHash["dpkgSte"][operation][value]) > + """ Generate OVAL state definition > + > + Use state hash for optimization of resulted XML > + """ > + #TODO: Add arch state generation > + if not testsHash["dpkgSte"].has_key(operation) or not > testsHash["dpkgSte"][operation].has_key(value): > + stateId = __getNewId ("state") > + > + state = __createXMLElement("dpkginfo_state", > + attrs={"id":stateId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux"}) > + state.appendChild ( __createXMLElement ("evr", "0:"+value, > + {"datatype":"evr_string", > + "operation":operation})) > + states.appendChild (state) > + > + testsHash["dpkgSte"][operation] = {value : stateId} > + > + return (testsHash["dpkgSte"][operation][value]) > > def __createOVALUnameState (field, value, operation = "equals"): > - """ Generate OVAL uname state definition > - > - Use unameArchTable to convert dsa arch to uname arch value > - """ > - > - try: > - value = unameArchTable[value] > - except KeyError: > - pass > - > - #TODO: Add arch state generation > - if not testsHash["unameSte"].has_key(operation) or not > testsHash["unameSte"][operation].has_key(value): > - stateId = __getNewId ("state") > - > - state = __createXMLElement("uname_state", > - attrs={"id":stateId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix"}) > - state.appendChild ( __createXMLElement (field, value, > - > {"operation":operation})) > - states.appendChild (state) > - > - testsHash["unameSte"][operation] = {value : stateId} > - > - return (testsHash["unameSte"][operation][value]) > + """ Generate OVAL uname state definition > + > + Use unameArchTable to convert dsa arch to uname arch value > + """ > + > + try: > + value = unameArchTable[value] > + except KeyError: > + pass > + > + #TODO: Add arch state generation > + if not testsHash["unameSte"].has_key(operation) or not > testsHash["unameSte"][operation].has_key(value): > + stateId = __getNewId ("state") > + > + state = __createXMLElement("uname_state", > + attrs={"id":stateId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix"}) > + state.appendChild ( __createXMLElement (field, value, > + {"operation":operation})) > + states.appendChild (state) > + > + testsHash["unameSte"][operation] = {value : stateId} > + > + return (testsHash["unameSte"][operation][value]) > > def __createOVALTextfilecontentState (value, operation = "equals"): > - """ Generate OVAL state definition > - > - Use state hash for optimization of resulted XML > - """ > - #TODO: Add arch state generation > - if not testsHash["fileSte"].has_key(operation) or not > testsHash["fileSte"][operation].has_key(value): > - stateId = __getNewId ("state") > - > - state = __createXMLElement("textfilecontent_state", > - attrs={"id":stateId, > - "version":"1", > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent"}) > - state.appendChild ( __createXMLElement ("line", value, > - > {"operation":operation})) > - states.appendChild (state) > - > - testsHash["fileSte"][operation] = {value : stateId} > - > - return (testsHash["fileSte"][operation][value]) > - > + """ Generate OVAL state definition > + > + Use state hash for optimization of resulted XML > + """ > + #TODO: Add arch state generation > + if not testsHash["fileSte"].has_key(operation) or not > testsHash["fileSte"][operation].has_key(value): > + stateId = __getNewId ("state") > + > + state = __createXMLElement("textfilecontent_state", > + attrs={"id":stateId, > + "version":"1", > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent"}) > + state.appendChild ( __createXMLElement ("line", value, > + {"operation":operation})) > + states.appendChild (state) > + > + testsHash["fileSte"][operation] = {value : stateId} > + > + return (testsHash["fileSte"][operation][value]) > + > def __createDPKGTest(name, version): > - """ Generate OVAL DPKG test """ > - > - ref = __getNewId ("test") > - test = __createXMLElement("dpkginfo_test", > - attrs={"id":ref, > - "version":"1", > - "check":"all", > - "check_existence":"at_least_one_exists", > - "comment":"%s is earlier than %s" % (name, > version), > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux" > - }) > - test.appendChild ( __createXMLElement("object", attrs={"object_ref" : > __createOVALDpkginfoObject (name)})) > - test.appendChild ( __createXMLElement("state", attrs={"state_ref" : > __createOVALState (version)})) > - tests.appendChild(test) > + """ Generate OVAL DPKG test """ > + > + ref = __getNewId ("test") > + test = __createXMLElement("dpkginfo_test", > + attrs={"id":ref, > + "version":"1", > + "check":"all", > + "check_existence":"at_least_one_exists", > + "comment":"%s is earlier than %s" % (name, version), > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#linux" > + }) > + test.appendChild ( __createXMLElement("object", attrs={"object_ref" : > __createOVALDpkginfoObject (name)})) > + test.appendChild ( __createXMLElement("state", attrs={"state_ref" : > __createOVALState (version)})) > + tests.appendChild(test) > + > + return (ref) > > - return (ref) > - > def __createTest(testType, value): > - """ Generate OVAL test for release or architecture cases""" > - > - if not testsHash[testType].has_key(value): > - comment = None > - > - ref = __getNewId("test") > - > - if testType == "release": > - objectId = __createOVALTextfilecontentObject ("\d\.\d") > - comment = "Debian GNU/Linux %s is installed" % value > - > - test = __createXMLElement("textfilecontent_test", > - attrs={"id":ref, > - "version":"1", > - "check":"all", > - "check_existence":"at_least_one_exists", > - "comment":comment, > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" > - }) > - test.appendChild ( __createXMLElement("object", > attrs={"object_ref" : objectId})) > - test.appendChild ( __createXMLElement("state", > attrs={"state_ref" : __createOVALTextfilecontentState (value, "equals")})) > - > - else: > - objectId = __createOVALUnameObject () > - comment = "Installed architecture is %s" % value > - > - test = __createXMLElement("uname_test", > - attrs={"id":ref, > - "version":"1", > - "check":"all", > - "check_existence":"at_least_one_exists", > - "comment":comment, > - > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix" > - }) > - test.appendChild ( __createXMLElement("object", > attrs={"object_ref" : objectId})) > - if value != "all": > - test.appendChild ( __createXMLElement("state", > attrs={"state_ref" : __createOVALUnameState ("processor_type", value, > "equals")})) > - > - tests.appendChild(test) > - > - testsHash[testType][value] = ref > - > - return (testsHash[testType][value]) > + """ Generate OVAL test for release or architecture cases""" > + > + if not testsHash[testType].has_key(value): > + comment = None > + > + ref = __getNewId("test") > + > + if testType == "release": > + objectId = __createOVALTextfilecontentObject ("\d\.\d") > + comment = "Debian GNU/Linux %s is installed" % value > + > + test = __createXMLElement("textfilecontent_test", > + attrs={"id":ref, > + "version":"1", > + "check":"all", > + "check_existence":"at_least_one_exists", > + "comment":comment, > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" > + }) > + test.appendChild ( __createXMLElement("object", > attrs={"object_ref" : objectId})) > + test.appendChild ( __createXMLElement("state", > attrs={"state_ref" : __createOVALTextfilecontentState (value, "equals")})) > + > + else: > + objectId = __createOVALUnameObject () > + comment = "Installed architecture is %s" % value > + > + test = __createXMLElement("uname_test", > + attrs={"id":ref, > + "version":"1", > + "check":"all", > + "check_existence":"at_least_one_exists", > + "comment":comment, > + > "xmlns":"http://oval.mitre.org/XMLSchema/oval-definitions-5#unix" > + }) > + test.appendChild ( __createXMLElement("object", > attrs={"object_ref" : objectId})) > + if value != "all": > + test.appendChild ( __createXMLElement("state", > attrs={"state_ref" : __createOVALUnameState ("processor_type", value, > "equals")})) > + > + tests.appendChild(test) > + > + testsHash[testType][value] = ref > + > + return (testsHash[testType][value]) > > def __createGeneratorHeader (): > - """ > - Create OVAL definitions XML generator element. > - > - return xml.dom.minidom.Document with header information > - """ > - > - doc = xml.dom.minidom.Document () > - generator = doc.createElement ("generator") > - > - generator.appendChild ( __createXMLElement ("oval:product_name", > "Debian") ) > - generator.appendChild ( __createXMLElement ("oval:schema_version", > "5.3") ) > - generator.appendChild ( __createXMLElement ("oval:timestamp", > datetime.datetime.now().strftime ("%Y-%m-%dT%H:%M:%S.188-04:00")) ) > + """ > + Create OVAL definitions XML generator element. > + > + return xml.dom.minidom.Document with header information > + """ > + > + doc = xml.dom.minidom.Document () > + generator = doc.createElement ("generator") > + > + generator.appendChild ( __createXMLElement ("oval:product_name", > "Debian") ) > + generator.appendChild ( __createXMLElement ("oval:schema_version", > "5.3") ) > + generator.appendChild ( __createXMLElement ("oval:timestamp", > datetime.datetime.now().strftime ("%Y-%m-%dT%H:%M:%S.188-04:00")) ) > > - return (generator) > + return (generator) > > def createPlatformDefinition (release, data, dsa): > - """ Generate OVAL definitions for current release > - > - Generate full criteria tree for specified release. Tests, > states and objects > - stored in global dictionaries. > - Use differ module for otimize generated tree. > - > - Argument keywords: > - release -- Debian release > - data -- dict with information about packages > - dsa - DSA id > - > - return Generated XML fragment > - """ > - #Raise exception if we receive too small data > - if len(data) == 0: > - logging.log(logging.WARNING, "DSA %s: Information of affected > platforms is not available." % dsa) > - > - softwareCriteria = __createXMLElement ("criteria", attrs = {"comment" : > "Release section", "operator" : "AND"}) > - softwareCriteria.appendChild ( __createXMLElement ("criterion", > attrs={"test_ref" : __createTest("release", release), "comment" : "Debian %s > is installed" % release})) > - > - archCriteria = __createXMLElement ("criteria", attrs = {"comment" : > "Architecture section", "operator" : "OR"}) > - > - # Handle architecture independed section > - if data.has_key ("all"): > - archIndepCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Architecture independet section", "operator" : "AND"}) > - > - archIndepCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createTest("arch", "all"), "comment" : > "all architecture"})) > - #Build packages section only if we have more then one package > - if len (data["all"]) > 1: > - packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > - archIndepCriteria.appendChild (packageCriteria) > - else: > - packageCriteria = archIndepCriteria > - > - for pkg in data["all"].keys(): > - packageCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createDPKGTest(pkg, data["all"][pkg]), > "comment" : "%s DPKG is earlier than %s" % (pkg, data["all"][pkg])})) > - > - archCriteria.appendChild (archIndepCriteria) > - > - # Optimize packages tree in 2 stages > - diff = differ () > - for i in range(2): > - > - if i == 0: > - dsaData = data > - else: > - dsaData = diff.getDiffer() > - > - diff.Clean() > - for (key, value) in dsaData.iteritems(): > - if key != "all": > - diff.compareElement(key, value) > - > - eq = diff.getEqual() > - di = diff.getDiffer() > - > - # Generate XML for optimized packages > - if (len(eq)): > - if len(diff.getArchs()) != releaseArchHash[release]: > - archDependCriteria = __createXMLElement > ("criteria", attrs={"comment" : "Architecture depended section", "operator" : > "AND"}) > - > - supportedArchCriteria = __createXMLElement > ("criteria", attrs={"comment" : "Supported architectures section", "operator" > : "OR"}) > - for arch in diff.getArchs(): > - supportedArchCriteria.appendChild ( > __createXMLElement ("criterion", attrs = {"test_ref" : __createTest("arch", > arch), "comment" : "%s architecture" % arch})) > - archDependCriteria.appendChild > (supportedArchCriteria) > - > - packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > - for bpkg in eq.keys(): > - packageCriteria.appendChild ( > __createXMLElement ("criterion", attrs = {"test_ref" : __createDPKGTest(bpkg, > eq[bpkg]), "comment" : "%s DPKG is earlier than %s" % (bpkg, eq[bpkg])})) > - > - if len(diff.getArchs()) != releaseArchHash[release]: > > - archDependCriteria.appendChild (packageCriteria) > - archCriteria.appendChild (archDependCriteria) > - else: > - archCriteria.appendChild (packageCriteria) > - > - # Generate XML for all other packages > - if len(di): > - archDependCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Architecture depended section", "operator" : "AND"}) > - > - for (key, value) in di.iteritems(): > - supportedPlatformCriteria = __createXMLElement > ("criteria", attrs={"comment" : "Supported platform section", "operator" : > "AND"}) > - supportedPlatformCriteria.appendChild ( > __createXMLElement ("criterion", attrs = {"test_ref" : __createTest("arch", > key), "comment" : "%s architecture" % key})) > - > - packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > - > - for bpkg in di[key].keys(): > - packageCriteria.appendChild ( > __createXMLElement ("criterion", attrs = {"test_ref" : __createDPKGTest(bpkg, > di[key][bpkg]), "comment" : "%s DPKG is earlier than %s" % (bpkg, > di[key][bpkg])})) > - supportedPlatformCriteria.appendChild > (packageCriteria) > - > - archDependCriteria.appendChild (supportedPlatformCriteria) > - archCriteria.appendChild (archDependCriteria) > - > - softwareCriteria.appendChild (archCriteria) > - > - return (softwareCriteria) > + """ Generate OVAL definitions for current release > + > + Generate full criteria tree for specified release. Tests, states and > objects > + stored in global dictionaries. > + Use differ module for optimize generated tree. > + > + Argument keywords: > + release -- Debian release > + data -- dict with information about packages > + dsa - DSA id > + > + return Generated XML fragment > + """ > + #Raise exception if we receive too small data > + if len(data) == 0: > + logging.log(logging.WARNING, "DSA %s: Information of affected > platforms is not available." % dsa) > + > + softwareCriteria = __createXMLElement ("criteria", attrs = {"comment" : > "Release section", "operator" : "AND"}) > + softwareCriteria.appendChild ( __createXMLElement ("criterion", > attrs={"test_ref" : __createTest("release", release), "comment" : "Debian %s > is installed" % release})) > + > + archCriteria = __createXMLElement ("criteria", attrs = {"comment" : > "Architecture section", "operator" : "OR"}) > + > + # Handle architecture independent section > + if data.has_key ("all"): > + archIndepCriteria = __createXMLElement ("criteria", attrs={"comment" > : "Architecture independent section", "operator" : "AND"}) > + > + archIndepCriteria.appendChild ( __createXMLElement ("criterion", > attrs = {"test_ref" : __createTest("arch", "all"), "comment" : "all > architecture"})) > + #Build packages section only if we have more then one package > + if len (data["all"]) > 1: > + packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > + archIndepCriteria.appendChild (packageCriteria) > + else: > + packageCriteria = archIndepCriteria > + > + for pkg in data["all"].keys(): > + packageCriteria.appendChild ( __createXMLElement ("criterion", > attrs = {"test_ref" : __createDPKGTest(pkg, data["all"][pkg]), "comment" : > "%s DPKG is earlier than %s" % (pkg, data["all"][pkg])})) > + > + archCriteria.appendChild (archIndepCriteria) > + > + # Optimize packages tree in 2 stages > + diff = differ() > + for i in range(2): > + > + if i == 0: > + dsaData = data > + else: > + dsaData = diff.getDiffer() > + > + diff.Clean() > + for (key, value) in dsaData.iteritems(): > + if key != "all": > + diff.compareElement(key, value) > + > + eq = diff.getEqual() > + di = diff.getDiffer() > + > + # Generate XML for optimized packages > + if (len(eq)): > + if len(diff.getArchs()) != releaseArchHash[release]: > + archDependCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Architecture depended section", "operator" : "AND"}) > + > + supportedArchCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Supported architectures section", "operator" : "OR"}) > + for arch in diff.getArchs(): > + supportedArchCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createTest("arch", arch), "comment" : > "%s architecture" % arch})) > + archDependCriteria.appendChild (supportedArchCriteria) > + > + packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > + for bpkg in eq.keys(): > + packageCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createDPKGTest(bpkg, eq[bpkg]), > "comment" : "%s DPKG is earlier than %s" % (bpkg, eq[bpkg])})) > + > + if len(diff.getArchs()) != releaseArchHash[release]: > + archDependCriteria.appendChild (packageCriteria) > + archCriteria.appendChild (archDependCriteria) > + else: > + archCriteria.appendChild (packageCriteria) > + > + # Generate XML for all other packages > + if len(di): > + archDependCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Architecture depended section", "operator" : "AND"}) > + > + for (key, value) in di.iteritems(): > + supportedPlatformCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Supported platform section", "operator" : "AND"}) > + supportedPlatformCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createTest("arch", key), "comment" : > "%s architecture" % key})) > + > + packageCriteria = __createXMLElement ("criteria", > attrs={"comment" : "Packages section", "operator" : "OR"}) > + > + for bpkg in di[key].keys(): > + packageCriteria.appendChild ( __createXMLElement > ("criterion", attrs = {"test_ref" : __createDPKGTest(bpkg, di[key][bpkg]), > "comment" : "%s DPKG is earlier than %s" % (bpkg, di[key][bpkg])})) > + supportedPlatformCriteria.appendChild (packageCriteria) > + > + archDependCriteria.appendChild (supportedPlatformCriteria) > + archCriteria.appendChild (archDependCriteria) > + > + softwareCriteria.appendChild (archCriteria) > + > + return (softwareCriteria) > > def createDefinition (dsa, dsaref): > - """ Generate OVAL header of Definition tag > - > - Print general informaton about OVAL definition. Use > createPlatformDefinition for generate criteria > - sections for each affected release. > - > - Argument keywords: > - dsa -- DSA dentificator > - dsaref -- DSA parsed data > - """ > - if not dsaref.has_key("release"): > - logging.log(logging.WARNING, "DSA %s: Release definition not > well formatted. Ignoring this DSA." % dsa) > - raise DSAFormatException > - > - if not dsaref.has_key("packages"): > - logging.log(logging.WARNING, "DSA %s: Package information > missed. Ignoring this DSA." % dsa) > - dsaref["packages"] = "" > - > - if not dsaref.has_key("description"): > - logging.log(logging.WARNING, "DSA %s: Description information > missed." % dsa) > - dsaref["description"] = "" > - > - if not dsaref.has_key("moreinfo"): > - logging.log(logging.WARNING, "DSA %s: Moreinfo information > missed." % dsa) > - dsaref["moreinfo"] = "" > - > - if not dsaref.has_key("secrefs"): > - logging.log(logging.WARNING, "DSA %s: Secrefs information > missed." % dsa) > - dsaref["secrefs"] = "" > - > - doc = xml.dom.minidom.Document () > - > - ### Definition block: Metadata, Notes, Criteria > - ### TODO: Replace DSA id with unique id > - definition = __createXMLElement ("definition", attrs = {"id" : > "oval:org.debian:def:%s" % __trimzero(dsa), "version" : "1", "class" : > "vulnerability"}) > - > - ### Definition : Metadata : title, affected, reference, description ### > - metadata = __createXMLElement ("metadata") > - metadata.appendChild (__createXMLElement ("title", > dsaref["description"])) > - > - ### Definition : Metadata : Affected : platform, product ### > - affected = __createXMLElement ("affected", attrs = {"family" : "unix"}) > - for platform in dsaref["release"]: > - affected.appendChild ( __createXMLElement ("platform", "Debian > GNU/Linux %s" % platform)) > - affected.appendChild ( __createXMLElement ("product", > dsaref.get("packages"))) > - > - metadata.appendChild (affected) > - ### Definition : Metadata : Affected : END ### > - > - refpatern = re.compile (r'((CVE|CAN)-[\d-]+)') > - for ref in dsaref.get("secrefs").split(" "): > - result = refpatern.search(ref) > - if result: > - (ref_id, source) = result.groups() > - metadata.appendChild ( __createXMLElement ("reference", > attrs = {"source" : source, "ref_id" : ref_id, "ref_url" : > "http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s" % ref_id}) ) > - > - #TODO: move this info to other place > - metadata.appendChild ( __createXMLElement ("description", "What > information can i put there?")) > - debianMetadata = __createXMLElement ("debian") > - if dsaref.has_key("date"): > - debianMetadata.appendChild ( __createXMLElement ("date", > dsaref["date"]) ) > - debianMetadata.appendChild ( __createXMLElement ("moreinfo", > dsaref["moreinfo"]) ) > - metadata.appendChild (debianMetadata) > - definition.appendChild ( metadata ) > - > - ### Definition : Criteria ### > - if len(dsaref["release"]) > 1: > - #f we have more than one release - generate additional criteria > section > - platformCriteria = __createXMLElement ("criteria", attrs = > {"comment" : "Platform section", "operator" : "OR"}) > - definition.appendChild (platformCriteria) > - else: > - platformCriteria = definition > - > - for platform in dsaref["release"]: > - data = dsaref["release"][platform] > - platformCriteria.appendChild > (createPlatformDefinition(platform, data, dsa)) > - > - ### Definition : Criteria END ### > - > - return (definition) > - > -def createOVALDefinitions (dsaref): > - """ Generate XML OVAL definition tree for range of DSA > - > - Generate namespace section and use other functions to generate > definitions, > - tests, objects and states subsections. > - > - return -- Generated OVAL XML definition > - """ > - doc = xml.dom.minidom.Document () > - > - root = __createXMLElement ("oval_definitions", > - attrs= { > - "xsi:schemaLocation" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#independent > independent-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5#linux > linux-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5#unix > unix-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5 > oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 > oval-common-schema.xsd", > - "xmlns:xsi" : > "http://www.w3.org/2001/XMLSchema-instance", > - "xmlns:ind-def " : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#independent", > - "xmlns:linux-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#linux", > - "xmlns:oval-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5", > - "xmlns:unix-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#unix", > - "xmlns" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5", > - "xmlns:oval" : > "http://oval.mitre.org/XMLSchema/oval-common-5" > - } > - ) > - doc.appendChild (root) > - root.appendChild ( __createGeneratorHeader () ) > - > - definitions = doc.createElement ("definitions") > - > - keyids = dsaref.keys() > - keyids.sort() > - for dsa in keyids: > - try: > - definitions.appendChild (createDefinition(dsa, > dsaref[dsa])) > - except DSAFormatException: > - logging.log (logging.WARNING, "DSA %s: Bad data file. > Ignoring this DSA." % dsa) > - > - root.appendChild (definitions) > - > - root.appendChild(tests) > - root.appendChild(objects) > - root.appendChild(states) > + """ Generate OVAL header of Definition tag > + > + Print general information about OVAL definition. Use > createPlatformDefinition for generate criteria > + sections for each affected release. > + > + Argument keywords: > + dsa -- DSA identifier > + dsaref -- DSA parsed data > + """ > + > + if not dsaref.has_key("release"): > + logging.log(logging.WARNING, "DSA %s: Release definition not well > formatted. Ignoring this DSA." % dsa) > + raise DSAFormatException > + > + if not dsaref.has_key("packages"): > + logging.log(logging.WARNING, "DSA %s: Package information missed. > Ignoring this DSA." % dsa) > + dsaref["packages"] = "" > + > + if not dsaref.has_key("description"): > + logging.log(logging.WARNING, "DSA %s: Description information > missed." % dsa) > + dsaref["description"] = "" > + > + if not dsaref.has_key("moreinfo"): > + logging.log(logging.WARNING, "DSA %s: Moreinfo information missed." > % dsa) > + dsaref["moreinfo"] = "" > + > + if not dsaref.has_key("secrefs"): > + logging.log(logging.WARNING, "DSA %s: Secrefs information missed." % > dsa) > + dsaref["secrefs"] = "" > + > + doc = xml.dom.minidom.Document () > + > + ### Definition block: Metadata, Notes, Criteria > + ### TODO: Replace DSA id with unique id > + definition = __createXMLElement ("definition", attrs = {"id" : > "oval:org.debian:def:%s" % __trimzero(dsa), "version" : "1", "class" : > "vulnerability"}) > + > + ### Definition : Metadata : title, affected, reference, description ### > + metadata = __createXMLElement ("metadata") > + metadata.appendChild (__createXMLElement ("title", > dsaref["description"])) > + > + ### Definition : Metadata : Affected : platform, product ### > + affected = __createXMLElement ("affected", attrs = {"family" : "unix"}) > + for platform in dsaref["release"]: > + affected.appendChild ( __createXMLElement ("platform", "Debian > GNU/Linux %s" % platform)) > + affected.appendChild ( __createXMLElement ("product", > dsaref.get("packages"))) > + > + metadata.appendChild (affected) > + ### Definition : Metadata : Affected : END ### > + > + refpatern = re.compile (r'((CVE|CAN)-[\d-]+)') > + for ref in dsaref.get("secrefs").split(" "): > + result = refpatern.search(ref) > + if result: > + (ref_id, source) = result.groups() > + metadata.appendChild ( __createXMLElement ("reference", attrs = > {"source" : source, "ref_id" : ref_id, "ref_url" : > "http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s" % ref_id}) ) > + > + #TODO: move this info to other place > + metadata.appendChild ( __createXMLElement ("description", "What > information can i put there?")) > + debianMetadata = __createXMLElement ("debian") > + if dsaref.has_key("date"): > + debianMetadata.appendChild ( __createXMLElement ("date", > dsaref["date"]) ) > + debianMetadata.appendChild ( __createXMLElement ("moreinfo", > dsaref["moreinfo"]) ) > + metadata.appendChild (debianMetadata) > + definition.appendChild ( metadata ) > + > + ### Definition : Criteria ### > + if len(dsaref["release"]) > 1: > + #f we have more than one release - generate additional criteria > section > + platformCriteria = __createXMLElement ("criteria", attrs = > {"comment" : "Platform section", "operator" : "OR"}) > + definition.appendChild (platformCriteria) > + else: > + platformCriteria = definition > + > + for platform in dsaref["release"]: > + data = dsaref["release"][platform] > + platformCriteria.appendChild (createPlatformDefinition(platform, > data, dsa)) > + > + ### Definition : Criteria END ### > + > + return (definition) > + > +def createOVALDefinitions(dsaref): > + """ Generate XML OVAL definition tree for range of DSA > + > + Generate namespace section and use other functions to generate > definitions, > + tests, objects and states subsections. > + > + return -- Generated OVAL XML definition > + """ > + doc = xml.dom.minidom.Document () > + > + root = __createXMLElement ("oval_definitions", > + attrs= { > + "xsi:schemaLocation" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#independent > independent-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5#linux > linux-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5#unix > unix-definitions-schema.xsd > http://oval.mitre.org/XMLSchema/oval-definitions-5 > oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 > oval-common-schema.xsd", > + "xmlns:xsi" : > "http://www.w3.org/2001/XMLSchema-instance", > + "xmlns:ind-def " : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#independent", > + "xmlns:linux-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#linux", > + "xmlns:oval-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5", > + "xmlns:unix-def" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5#unix", > + "xmlns" : > "http://oval.mitre.org/XMLSchema/oval-definitions-5", > + "xmlns:oval" : > "http://oval.mitre.org/XMLSchema/oval-common-5" > + } > + ) > + doc.appendChild (root) > + root.appendChild ( __createGeneratorHeader () ) > + > + definitions = doc.createElement ("definitions") > + > + keyids = dsaref.keys() > + keyids.sort() > + for dsa in keyids: > + try: > + definitions.appendChild (createDefinition(dsa, dsaref[dsa])) > + except DSAFormatException: > + logging.log (logging.WARNING, "DSA %s: Bad data file. Ignoring > this DSA." % dsa) > + > + root.appendChild (definitions) > + > + root.appendChild(tests) > + root.appendChild(objects) > + root.appendChild(states) > > - return doc > + return doc > > def printOVALDefinitions (doc): > - if doc.getElementsByTagName("definitions")[0].hasChildNodes(): > - print doc.toprettyxml() > + if doc.getElementsByTagName("definitions")[0].hasChildNodes(): > + print doc.toprettyxml() > Binary files oval_old/oval/definition/generator.pyc and > oval/oval/definition/generator.pyc differ > Binary files oval_old/oval/definition/__init__.pyc and > oval/oval/definition/__init__.pyc differ > Binary files oval_old/oval/__init__.pyc and oval/oval/__init__.pyc differ > diff -urN oval_old/oval/parser/dsa.py oval/oval/parser/dsa.py > --- oval_old/oval/parser/dsa.py 2011-10-11 17:41:19.000000000 -0600 > +++ oval/oval/parser/dsa.py 2015-11-13 09:34:04.843882000 -0700 > @@ -9,6 +9,7 @@ > # <isvulnerable> > # <fixed> > # > +# (c) 2015 Nicholas Luedtke > # (c) 2007 Pavel Vinogradov > # (c) 2004 Javier Fernandez-Sanguino > > # Licensed under the GNU General Public License version 2. > @@ -17,105 +18,113 @@ > import os > import logging > > + > # Format of data files is: > -#<define-tag pagetitle>DSA-###-# PACKAGE</define-tag> > > -#<define-tag report_date>yyyy-mm-dd</define-tag> > > -#<define-tag secrefs>CAN|CVE-XXXX-XXXX</define-tag> > > -#<define-tag packages>PACKAGE</define-tag> > > -#<define-tag isvulnerable>yes|no</define-tag> > > -#<define-tag fixed>yes|no</define-tag> > -def parseFile (path): > - """ Parse data file with information of Debian Security Advisories > +# <define-tag pagetitle>DSA-###-# PACKAGE</define-tag> > +# <define-tag report_date>yyyy-mm-dd</define-tag> > +# <define-tag secrefs>CAN|CVE-XXXX-XXXX</define-tag> > +# <define-tag packages>PACKAGE</define-tag> > +# <define-tag isvulnerable>yes|no</define-tag> > +# <define-tag fixed>yes|no</define-tag> > +def parseFile(path): > + """ Parse data file with information of Debian Security Advisories > > Keyword arguments: > path -- full path to data file > > return list (dsa id, tags and packages data)""" > - > - > - data = {} > - deb_ver = None > - fdeb_ver = None > - > - filename = os.path.basename (path) > > - patern = re.compile(r'dsa-(\d+)') > - result = patern.search(filename) > - if result: > - dsa = result.groups()[0] > - else: > - logging.log(logging.WARNING, "File %s does not look like a > proper DSA, not checking" % filename) > - return (None) > + data = {} > + deb_ver = None > + fdeb_ver = None > + > + filename = os.path.basename(path) > + > + patern = re.compile(r'dsa-(\d+)') > + result = patern.search(filename) > + > + if result: > + dsa = result.groups()[0] > + > + else: > + logging.log(logging.WARNING, > + "File %s does not look like a proper DSA, not checking" > % filename) > + return (None) > + > + logging.log(logging.DEBUG, "Parsing DSA %s from file %s" % (dsa, > filename)) > + > + dsaFile = open(path) > + > + for line in dsaFile: > + line = line.decode("ISO-8859-2") > + datepatern = re.compile(r'report_date>([\d-]+)</define-tag>') > + result = datepatern.search(line) > + if result: > + date = result.groups()[0] > + normDate = lambda (date): "-".join( > + [(len(p) > 1 and p or "0" + p) for p in date.split("-")]) > + data["date"] = normDate(date) > + > + refspatern = re.compile(r'secrefs>(.*?)</define-tag>') > + result = refspatern.search(line) > + if result: > + data["secrefs"] = result.groups()[0] > + logging.log(logging.DEBUG, > + "Extracted security references: " + data["secrefs"]) > + > + pakpatern = re.compile(r'packages>(.*?)</define-tag>') > + result = pakpatern.search(line) > + if result: > + data["packages"] = result.groups()[0] > + > + vulpatern = re.compile(r'isvulnerable>(.*?)</define-tag>') > + result = vulpatern.search(line) > + if result: > + data["vulnarable"] = result.groups()[0] > + > + fixpatern = re.compile(r'fixed>(.*?)</define-tag>') > + result = fixpatern.search(line) > + if result: > + data["fixed"] = result.groups()[0] > + > + versionpatern = re.compile( > + r'<h3>Debian GNU/Linux (\d.\d) \((.*?)\)</h3>') > + result = versionpatern.search(line) > + if result: > + fdeb_ver = result.groups()[0] > + > + # Alternative format for data files > + versionpatern = re.compile(r'affected_release>([\d\.]+)<') > + result = versionpatern.search(line) > + if result: > + fdeb_ver = result.groups()[0] > + > + if fdeb_ver: > + deb_ver = fdeb_ver > + fdeb_ver = None > + if data.has_key("release"): > + if data["release"].has_key(deb_ver): > + logging.log(logging.WARNING, > + "DSA %s: Found second files section for > release %s" % ( > + dsa, deb_ver)) > + else: > + data["release"][deb_ver] = {} > + else: > + data["release"] = {deb_ver: {}} > + > + # Binary packages are pushed into array > + # Those are prepended by fileurls > + # TODO: Packages do _NOT_ include epochs > + # (that should be fixed) > + if data.has_key("release") and deb_ver: > + urlpatern = re.compile(r'fileurl > [\w:/.\-+]+/([\w\-.+~]+)\.deb[^i]') > + result = urlpatern.search(line) > + if result: > + (package, version, architecture) = > result.groups()[0].split("_") > + > + if data["release"][deb_ver].has_key(architecture): > + data["release"][deb_ver][architecture][package] = version > + else: > + data["release"][deb_ver][architecture] = {package: > version} > > - logging.log (logging.DEBUG, "Parsing DSA %s from file %s" % (dsa, > filename)) > - > - dsaFile = open(path) > - > - for line in dsaFile: > - line= line.decode ("ISO-8859-2") > - datepatern = re.compile (r'report_date>([\d-]+)</define-tag>') > - result = datepatern.search (line) > - if result: > - date = result.groups()[0] > - normDate = lambda (date): "-".join([(len(p) > 1 and p > or "0"+p) for p in date.split("-")]) > - data["date"] = normDate(date) > - > - refspatern = re.compile (r'secrefs>(.*?)</define-tag>') > - result = refspatern.search (line) > - if result: > - data["secrefs"] = result.groups()[0] > - logging.log(logging.DEBUG, "Extracted security > references: " + data["secrefs"]) > - > - pakpatern = re.compile (r'packages>(.*?)</define-tag>') > - result = pakpatern.search (line) > - if result: > - data["packages"] = result.groups()[0] > - > - vulpatern = re.compile (r'isvulnerable>(.*?)</define-tag>') > - result = vulpatern.search (line) > - if result: > - data["vulnarable"] = result.groups()[0] > - > - fixpatern = re.compile (r'fixed>(.*?)</define-tag>') > - result = fixpatern.search (line) > - if result: > - data["fixed"] = result.groups()[0] > - > - versionpatern = re.compile (r'<h3>Debian GNU/Linux (\d.\d) > \((.*?)\)</h3>') > - result = versionpatern.search (line) > - if result: > - fdeb_ver = result.groups()[0] > - > - # Alternative format for data files > - versionpatern = re.compile (r'affected_release>([\d\.]+)<') > - result = versionpatern.search (line) > - if result: > - fdeb_ver = result.groups()[0] > - > - if fdeb_ver: > - deb_ver = fdeb_ver > - fdeb_ver = None > - if data.has_key("release"): > - if data["release"].has_key(deb_ver): > - logging.log(logging.WARNING, "DSA %s: > Found second files section for release %s" % (dsa, deb_ver)) > - else: > - data["release"][deb_ver] = {} > - else: > - data["release"] = {deb_ver: {}} > - > - # Binary packages are pushed into array > - # Those are prepended by fileurls > - # TODO: Packages do _NOT_ include epochs > - # (that should be fixed) > - if data.has_key("release") and deb_ver: > - urlpatern = re.compile (r'fileurl > [\w:/.\-+]+/([\w\-.+~]+)\.deb[^i]') > - result = urlpatern.search (line) > - if result: > - (package, version, architecture) = > result.groups()[0].split("_") > - > - if > data["release"][deb_ver].has_key(architecture): > - > data["release"][deb_ver][architecture][package] = version > - else: > - data["release"][deb_ver][architecture] > = {package : version} > - > - return (dsa, data) > + return (dsa, data) > Binary files oval_old/oval/parser/dsa.pyc and oval/oval/parser/dsa.pyc differ > Binary files oval_old/oval/parser/__init__.pyc and > oval/oval/parser/__init__.pyc differ > diff -urN oval_old/oval/parser/wml.py oval/oval/parser/wml.py > --- oval_old/oval/parser/wml.py 2013-01-21 00:48:41.000000000 -0700 > +++ oval/oval/parser/wml.py 2015-11-06 08:50:30.632363000 -0700 > @@ -1,13 +1,14 @@ > # -*- coding: utf-8 -*- > -# oval.parser.wml - module to parse descriptions of > +# oval.parser.wml - module to parse descriptions of > # Debian Security Advisories stored in wml format. > # Extrected tags: > # <description> > # <moreinfo>- Paragraphs before descriptions of > # each release status > # > -# (c) 2007 Pavel Vinogradov > -# (c) 2004 Javier Fernandez-Sanguino > > +# (c) 2015 Nicholas Luedtke > +# (c) 2007 Pavel Vinogradov > +# (c) 2004 Javier Fernandez-Sanguino > # Licensed under the GNU General Public License version 2. > > import re > @@ -15,76 +16,111 @@ > import sys > import logging > > +DEBIAN_VERSION = {"wheezy" : "7.0", "jessie" : "8.2", "stretch" : "9.0", > + "sid" : "9.0", "etch" : "4.0", "squeeze":"6.0", > "lenny":"5.0"} > + > # Format of wml files is: > #<define-tag description>DESCRIPTION</define-tag> > #<define-tag moreinfo>Multiline information</define-tag> > def parseFile (path): > - """ Parse wml file with description of Debian Security Advisories > - > - Keyword arguments: > - path -- full path to wml file > - > - return list (dsa id, tags data)""" > - > - data = {} > - moreinfo = False > - > - filename = os.path.basename (path) > - > - patern = re.compile(r'dsa-(\d+)') > - result = patern.search(filename) > - if result: > - dsa = result.groups()[0] > - else: > - logging.log(logging.WARNING, "File %s does not look like a > proper DSA wml description, not checking" % filename) > - return (None) > - > - logging.log (logging.DEBUG, "Parsing information for DSA %s from wml > file %s" % (dsa, filename)) > - > - try: > - wmlFile = open(path) > - > - for line in wmlFile: > - line= line.decode ("ISO-8859-2") > - > - descrpatern = re.compile > (r'description>(.*?)</define-tag>') > - result = descrpatern.search (line) > - if result: > - data["description"] = result.groups()[0] > - continue > - > - sinfopatern = re.compile (r'<define-tag moreinfo>(.*?)') > - result = sinfopatern.search (line) > - if result: > - moreinfo = True > - data["moreinfo"] = result.groups()[0] > - continue > - > - einfopatern = re.compile (r'</define-tag>') > - if moreinfo and einfopatern.search (line): > - data["moreinfo"] = > __parseMoreinfo(data["moreinfo"]) > - moreinfo = False > - continue > - > - if moreinfo: > - data["moreinfo"] += line > - continue > - > - except IOError: > - logging.log (logging.ERROR, "Can't work with file %s" % path) > - > - return (dsa, data) > + """ Parse wml file with description of Debian Security Advisories > + > + Keyword arguments: > + path -- full path to wml file > + > + return list (dsa id, tags data)""" > + > + data = {} > + moreinfo = False > + pack_ver = "" > + deb_version = "" > + releases = {} > + > + filename = os.path.basename (path) > + > + patern = re.compile(r'dsa-(\d+)') > + result = patern.search(filename) > + if result: > + dsa = result.groups()[0] > + else: > + logging.log(logging.WARNING, "File %s does not look like a proper > DSA wml description, not checking" % filename) > + return (None) > + > + logging.log (logging.DEBUG, "Parsing information for DSA %s from wml > file %s" % (dsa, filename)) > + > + try: > + wmlFile = open(path) > + > + for line in wmlFile: > + line= line.decode ("ISO-8859-2") > + > + descrpatern = re.compile (r'description>(.*?)</define-tag>') > + result = descrpatern.search (line) > + if result: > + data["description"] = result.groups()[0] > + continue > + > + sinfopatern = re.compile (r'<define-tag moreinfo>(.*?)') > + result = sinfopatern.search (line) > + if result: > + moreinfo = True > + data["moreinfo"] = result.groups()[0] > + continue > + > + einfopatern = re.compile (r'</define-tag>') > + if moreinfo and einfopatern.search (line): > + data["moreinfo"] = __parseMoreinfo(data["moreinfo"]) > + moreinfo = False > + continue > + > + if moreinfo: > + data["moreinfo"] += line > + #continue > + > + dversion_pattern = re.compile(r'distribution \((.*?)\)') > + result = dversion_pattern.search(line) > + if result: > + deb_version = result.groups()[0] > + > + new_version_pattern = re.compile(r'version (.*?).</p>') > + result = new_version_pattern.search(line) > + if result and deb_version != "": > + pack_ver = result.groups()[0] > + releases.update({DEBIAN_VERSION[deb_version]: {u"all": > {grabPackName(path) : pack_ver}}}) > + > + > + > + except IOError: > + logging.log (logging.ERROR, "Can't work with file %s" % path) > + > + return dsa, data, releases > > def __parseMoreinfo (info): > - """ Remove unnecessary information form moreinfo tag""" > + """ Remove unnecessary information form moreinfo tag""" > + > + p = re.compile ("<p>(.*?)</p>", re.DOTALL) > + paragraphs = [m.groups()[0] for m in re.finditer(p, info)] > + result = "" > + > + for par in paragraphs: > + if re.match(re.compile("For the .* distribution"), par): > + break > + result += "\n" + par > + > + return result > + > +def grabPackName(path): > + """ > + :param path: full path to wml file > + :return: string: Package Name > + """ > > - p = re.compile ("<p>(.*?)</p>", re.DOTALL) > - paragraphs = [m.groups()[0] for m in re.finditer(p, info)] > - result = "" > - > - for par in paragraphs: > - if re.match(re.compile("For the .* distribution"), par): > - break > - result += "\n" + par > - > - return result > + try: > + wmlFile = open(path) > + package_name = re.compile (r'We recommend that you upgrade your > (.*?) packages') > + for line in wmlFile: > + result = package_name.search(line) > + if result: > + return result.groups()[0] > + except IOError: > + logging.log (logging.ERROR, "Can't work with file %s" % path) > Binary files oval_old/oval/parser/wml.pyc and oval/oval/parser/wml.pyc differ > diff -urN oval_old/parseDsa2Oval.py oval/parseDsa2Oval.py > --- oval_old/parseDsa2Oval.py 2011-10-12 15:14:55.000000000 -0600 > +++ oval/parseDsa2Oval.py 2015-11-13 11:13:33.923838000 -0700 > @@ -2,11 +2,11 @@ > # -*- coding: utf-8 -*- > # Extracts the data DSA files and creates OVAL queries to > # be used with the OVAL query interpreter (see http://oval.mitre.org) > - > +# (c) 2015 Nicholas Luedtke > # (c) 2007 Pavel Vinogradov > # (c) 2004 Javier Fernandez-Sanguino > > # Licensed under the GNU General Public License version 2. > > - > > + > import os > import sys > import getopt > @@ -18,89 +18,89 @@ > > dsaref = {} > > + > def usage (prog = "parse-wml-oval.py"): > - """Print information about script flags and options""" > + """Print information about script flags and options""" > > - print """ > -usage: %s [vh] [-d <directory>] > -\t-d\twhich directory use for dsa definition search > -\t-v\tverbose mode > -\t-h\tthis help > - """ % prog > - > -def printdsas (dsaref): > + print """usage: %s [vh] [-d <directory>]\t-d\twhich directory use for > + dsa definition search\t-v\tverbose mode\t-h\tthis help""" % prog > + > +def printdsas(dsaref): > """ Generate and print OVAL Definitions for collected DSA information """ > - > + > ovalDefinitions = oval.definition.generator.createOVALDefinitions > (dsaref) > oval.definition.generator.printOVALDefinitions (ovalDefinitions) > > def parsedirs (directory, postfix, depth): > - """ Recursive search directory for DSA files contain postfix in their > names. > - > - For this files called oval.parser.dsa.parseFile() for > extracting DSA information. > - """ > - > - if depth == 0: > - logging.log(logging.DEBUG, "Maximum depth reached at directory > " + directory) > - return (0) > + """ Recursive search directory for DSA files contain postfix in their > names. > + For this files called oval.parser.dsa.parseFile() for extracting DSA > + information. > + """ > + > + if depth == 0: > + logging.log(logging.DEBUG, "Maximum depth reached at directory " + > directory) > + return (0) > > - for file in os.listdir (directory): > - > - path = "%s/%s" % (directory, file) > - > - logging.log (logging.DEBUG, "Checking %s (for %s at %s)" % > (file, postfix, depth)) > + for file in os.listdir (directory): > > - if os.access(path, os.R_OK) and os.path.isdir (path) and not > os.path.islink (path) and file[0] != '.': > - logging.log(logging.DEBUG, "Entering directory " + path) > - parsedirs (path, postfix, depth-1) > + path = "%s/%s" % (directory, file) > + logging.log (logging.DEBUG, "Checking %s (for %s at %s)" % (file, > postfix, depth)) > > + if os.access(path, os.R_OK) and os.path.isdir (path) and not > os.path.islink (path) and file[0] != '.': > + logging.log(logging.DEBUG, "Entering directory " + path) > + parsedirs (path, postfix, depth-1) > + > #Parse DSA data files > - if os.access(path, os.R_OK) and file.endswith(postfix) and > file[0] != '.' and file[0] != '#': > - result = dsa.parseFile (path) > - if result: > - if dsaref.has_key (result[0]): > - for (k, v) in result[1].iteritems(): > - dsaref[result[0]][k] = v > - else: > - dsaref[result[0]] = result[1] > - > + if os.access(path, os.R_OK) and file.endswith(postfix) and file[0] > != '.' and file[0] != '#': > + result = dsa.parseFile(path) > + if result: > + if dsaref.has_key(result[0]): > + for (k, v) in result[1].iteritems(): > + dsaref[result[0]][k] = v > + else: > + dsaref[result[0]] = result[1] > + > #Parse DSA wml descriptions > - if os.access(path, os.R_OK) and file.endswith(".wml") and > file[0] != '.' and file[0] != '#': > - result = wml.parseFile(path) > - if result: > - if dsaref.has_key (result[0]): > - for (k, v) in result[1].iteritems(): > - dsaref[result[0]][k] = v > - else: > - dsaref[result[0]] = result[1] > - > - return 0 > + if os.access(path, os.R_OK) and file.endswith(".wml") and file[0] != > '.' and file[0] != '#': > + result = wml.parseFile(path) > + if result: > + if dsaref.has_key(result[0]): > + for (k, v) in result[1].iteritems(): > + dsaref[result[0]][k] = v > + if not dsaref[result[0]].has_key("release"): > + dsaref[result[0]]['release']=result[2] > + else: > + dsaref[result[0]] = result[1] > + dsaref[result[0]]['release']=result[2] > + > + return 0 > > if __name__ == "__main__": > - > + > # Parse cmd options with getopt > opts = {} > - > + > #By default we search dsa definitions from current directory, but -d > option override this > opts['-d'] = "./" > - > + > try: > opt, args = getopt.getopt (sys.argv[1:], 'vhd:') > except getopt.GetoptError: > usage () > sys.exit(1) > - > + > for key, value in opt: > opts[key] = value > - > + > if opts.has_key ('-h'): > usage() > sys.exit(0) > - > + > if opts.has_key('-v'): > logging.basicConfig(level=logging.DEBUG) > - > + > logging.basicConfig(level=logging.WARNING) > - > + > parsedirs (opts['-d'], '.data', 2) > + > printdsas(dsaref) > diff -urN oval_old/parseJSON2Oval.py oval/parseJSON2Oval.py > --- oval_old/parseJSON2Oval.py 1969-12-31 17:00:00.000000000 -0700 > +++ oval/parseJSON2Oval.py 2015-11-13 13:16:42.523783000 -0700 > @@ -0,0 +1,191 @@ > +#!/usr/bin/python > +# -*- coding: utf-8 -*- > +# Extracts the data from the security tracker and creates OVAL queries to > +# be used with the OVAL query interpreter (see http://oval.mitre.org) > + > +# (c) 2015 Nicholas Luedtke > +# Licensed under the GNU General Public License version 2. > > + > +import os > +from subprocess import call > +import sys > +import logging > +import argparse > +import json > +from datetime import date > +import oval.definition.generator > +from oval.parser import dsa > +from oval.parser import wml > + > + > +dsaref = {} > + > +# TODO: these may need changed or reworked. > +DEBIAN_VERSION = {"wheezy" : "7.0", "jessie" : "8.2", "stretch" : "9.0", > + "sid" : "9.0", "etch" : "4.0", "squeeze":"6.0", > "lenny":"5.0"} > + > +def usage (prog = "parse-wml-oval.py"): > + """Print information about script flags and options""" > + > + print """usage: %s [vh] [-d <directory>]\t-d\twhich directory use for > + dsa definition search\t-v\tverbose mode\t-h\tthis help""" % prog > + > + > +def printdsas(dsaref): > + """ Generate and print OVAL Definitions for collected DSA information """ > + > + ovalDefinitions = oval.definition.generator.createOVALDefinitions > (dsaref) > + oval.definition.generator.printOVALDefinitions (ovalDefinitions) > + > + > +def parsedirs (directory, postfix, depth): > + """ Recursive search directory for DSA files contain postfix in their > names. > + For this files called oval.parser.dsa.parseFile() for extracting DSA > + information. > + """ > + for file in os.listdir (directory): > + > + path = "%s/%s" % (directory, file) > + logging.log (logging.DEBUG, "Checking %s (for %s at %s)" % (file, > postfix, depth)) > + > + if os.access(path, os.R_OK) and os.path.isdir (path) and not > os.path.islink (path) and file[0] != '.': > + logging.log(logging.DEBUG, "Entering directory " + path) > + parsedirs (path, postfix, depth-1) > + > + #Parse DSA data files > + if os.access(path, os.R_OK) and file.endswith(postfix) and file[0] > != '.' and file[0] != '#': > + result = dsa.parseFile(path) > + if result: > + if dsaref.has_key(result[0]): > + for (k, v) in result[1].iteritems(): > + dsaref[result[0]][k] = v > + else: > + dsaref[result[0]] = result[1] > + > + #Parse DSA wml descriptions > + if os.access(path, os.R_OK) and file.endswith(".wml") and file[0] != > '.' and file[0] != '#': > + result = wml.parseFile(path) > + if result: > + if dsaref.has_key(result[0]): > + for (k, v) in result[1].iteritems(): > + dsaref[result[0]][k] = v > + if not dsaref[result[0]].has_key("release"): > + dsaref[result[0]]['release']=result[2] > + else: > + dsaref[result[0]] = result[1] > + dsaref[result[0]]['release']=result[2] > + return 0 > + > + > +def parseJSON(json_data, id_num): > + """ > + Parse the JSON data and extract information needed for OVAL definitions > + :param id_num: int id number to start at for defintions > + :param json_data: Json_Data > + :return: > + """ > + today = date.today() > + logging.log(logging.DEBUG, "Start of JSON Parse.") > + d_num = id_num > + for package in json_data: > + logging.log(logging.DEBUG, "Parsing package %s" % package) > + for CVE in json_data[package]: > + logging.log(logging.DEBUG, "Getting releases for %s" % CVE) > + release = {} > + for rel in json_data[package][CVE]['releases']: > + if json_data[package][CVE]['releases'][rel]['status'] != \ > + 'resolved': > + fixed_v = '0' > + f_str = 'no' > + else: > + fixed_v = > json_data[package][CVE]['releases'][rel]['fixed_version'] > + f_str = 'yes' > + release.update({DEBIAN_VERSION[rel]: {u'all': { > + package: fixed_v}}}) > + > + dsaref.update({str(d_num): {"packages": package, > + 'description': "", > + 'vulnerable': "yes", > + 'date': str(today.isoformat()), > + 'fixed': f_str, 'moreinfo': "", > + 'release': release, 'secrefs': CVE}}) > + logging.log(logging.DEBUG, "Created entry in dsaref %s" % > d_num) > + d_num += 1 > + > + > +def get_json_data(json_file): > + """ > + Retrieves JSON formatted data from a file. > + :param json_file: > + :return: JSON data (dependent on the file loaded, usually a dictionary.) > + """ > + logging.log(logging.DEBUG, "Extracting JSON file %s" % json_file) > + with open(json_file, "r") as json_d: > + d = json.load(json_d) > + return d > + > + > +def main(args): > + """ > + Main function for parseJSON2Oval.py > + :param args: > + :return: > + """ > + > + if args['verbose']: > + logging.basicConfig(level=logging.DEBUG) > + else: > + logging.basicConfig(level=logging.WARNING) > + > + # unpack args > + > + json_file = args['JSONfile'] > + temp_file = args['tmp'] > + id_num = args['id'] > + > + if json_file: > + json_data = get_json_data(json_file) > + else: > + logging.log(logging.DEBUG, "Preparing to download JSONfile") > + if os.path.isfile(temp_file): > + logging.log(logging.WARNING, "Removing file %s" % temp_file) > + os.remove(temp_file) > + logging.log(logging.DEBUG, "Issuing wget for JSON file") > + args = ['wget', > 'https://security-tracker.debian.org/tracker/data/json', > + '-O', temp_file] > + call(args) > + logging.log(logging.DEBUG, "File %s received" % temp_file) > + json_data = get_json_data(temp_file) > + if os.path.isfile(temp_file): > + logging.log(logging.DEBUG, "Removing file %s" % temp_file) > + os.remove(temp_file) > + > + parseJSON(json_data, id_num) > + #parsedirs (opts['-d'], '.data', 2) > + > + printdsas(dsaref) > + > +if __name__ == "__main__": > + PARSER = argparse.ArgumentParser(description='Generates oval definitions > ' > + 'from the JSON file used to > ' > + 'build the Debian Security ' > + 'Tracker.') > + PARSER.add_argument('-v', '--verbose', help='Verbose Mode', > + action="store_true") > + PARSER.add_argument('-j', '--JSONfile', type=str, > + help='Local JSON file to use. This will use a local ' > + 'copy of the JSON file instead of downloading > from' > + ' it from the server. default=none', > default=None) > + PARSER.add_argument('-t', '--tmp', type=str, > + help='Temporary file to download JSON file to. > Warning:' > + ' if this file already exists it will be > removed ' > + 'prior to downloading the JSON file. default= ' > + './DebSecTrackTMP.t', > default='./DebSecTrackTMP.t') > + PARSER.add_argument('--id', type=int, > + help='id number to start defintions at. default=100', > + default=100) > + ARGS = vars(PARSER.parse_args()) > + main(ARGS) > + > + > +