changeset 40:1d9b2b06067e

Add a CPE parser (and tests)
author Benoît Allard <benoit.allard@greenbone.net>
date Mon, 29 Dec 2014 14:30:39 +0100
parents ba0eb65d4134
children bb1dd2a55643
files farolluz/parsers/cpe.py tests/testCPE.py
diffstat 2 files changed, 471 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/parsers/cpe.py	Mon Dec 29 14:30:39 2014 +0100
@@ -0,0 +1,259 @@
+"""\
+a cpe class to ease the creation of a producttree based on cpe
+
+This is based on:
+
+    NIST Interagency Report 7695
+    Common Platform Enumeration: Naming Specification Version 2.3
+
+CPE is a trademark of The MITRE Corporation.
+
+"""
+
+import re
+
+PCT_MAP ={'!': "%21", '"': "%22", '#': "%23", '$': "%24", '%': "%25", '&': "%26",
+          "'": "%27", '(': "%28", ')': "%29", '*': "%2a", '+': "%2b", ',': "%2c",
+          '/': "%2f", ':': "%3a", ';': "%3b", '<': "%3c", "=": "%3d", '>': "%3e",
+          '?': "%3f", '@': "%40", '[': "%5b", '\\': "%5c","]": "%5d", '^': "%5e",
+          '`': "%60", '{': "%7b", '|': "%7c", '}': "%7d", "~": "%7e"}
+
+PCT_MAP_i = dict((v, k) for k, v in PCT_MAP.iteritems())
+
+def pct_encode(c):
+    """ Returns the right percent-encoding of c """
+    if c in "-.":
+        return c
+    return PCT_MAP[c]
+    return {'!': "%21", '"': "%22", '#': "%23", '$': "%24", '%': "%25", '&': "%26",
+            "'": "%27", '(': "%28", ')': "%29", '*': "%2a", '+': "%2b", ',': "%2c",
+            "-": c, '.': c, '/': "%2f", ':': "%3a", ';': "%3b", '<': "%3c",
+            "=": "%3d", '>': "%3e", '?': "%3f", '@': "%40", '[': "%5b", '\\': "%5c",
+            "]": "%5d", '^': "%5e", '`': "%60", '{': "%7b", '|': "%7c", '}': "%7d",
+            "~": "%7e"}[c]
+
+def decode(s):
+    if s == '':
+        return ANY
+    if s == '-':
+        return NA
+    s = s.lower()
+    res = ""
+    idx = 0
+    embedded = False
+    while idx < len(s):
+        c = s[idx]
+        if c in ".-~":
+            res += "\\" + c
+            embedded = True
+        elif c != '%':
+            res += c
+            embedded = True
+        else:
+            form = s[idx:idx+3]
+            if form == "%01":
+                if (((idx == 0) or (idx == (len(s) - 3))) or
+                    ( not embedded and (s[idx-4:idx-1] == "%01")) or
+                    (embedded and (len(s) > idx + 6) and (s[idx+3:idx+6] == "%01"))):
+                    res += '?'
+                else:
+                    raise ValueError
+            elif form == "%02":
+                if (idx == 0) or (idx == len(s) - 3):
+                    res += '*'
+                else:
+                    raise ValueError
+            else:
+                res += '\\' + PCT_MAP_i[form]
+            embedded = True
+            idx += 2
+        idx += 1
+    return CPEAttribute(res)
+
+def unbind_value_fs(s):
+    if s == '*':
+        return ANY
+    if s == '-':
+        return NA
+    res = ""
+    idx = 0
+    embedded = False
+    while idx < len(s):
+        c = s[idx]
+        if re.match("[a-zA-Z0-9_]", c) is not None:
+            res += c
+            embedded = True
+        elif c == "\\":
+            res += s[idx:idx+2]
+            embedded = True
+            idx += 1
+        elif c == "*":
+            if (idx == 0) or (idx == (len(s) - 1)):
+                res += c
+                embedded = True
+            else:
+                raise ValueError
+        elif c == "?":
+            if (((idx == 0) or (idx == (len(s) - 1))) or
+                (not embedded and (s[idx - 1] == "?")) or
+                (embedded and (s[idx + 1] == "?"))):
+                res += c
+                embedded = False
+            else:
+                raise ValueError
+        else:
+            res += "\\" + c
+            embedded = True
+        idx += 1
+    return CPEAttribute(res)
+
+class CPEAttribute(object):
+    """ We need a special class to deal with ANY / NA / "string" """
+
+    def __init__(self, value=None, any=False, na=False):
+        self.any = any
+        self.na = na
+        self.value = value
+
+    def bind_for_URI(self):
+#        print self.any, self.na, self.value
+        if self.any:
+            return ""
+        if self.na:
+            return '-'
+        return self.transform_for_uri()
+
+    def transform_for_uri(self):
+        res = ""
+        idx = 0
+        while idx < len(self.value):
+            c = self.value[idx]
+            if re.match("[a-zA-Z0-9_]", c) is not None:
+                res += c
+            elif c == '\\':
+                idx += 1
+                c = self.value[idx]
+                res += pct_encode(c)
+            elif c == '?':
+                res += "%01"
+            elif c == '*':
+                res += "%02"
+            idx += 1
+        return res
+
+    def bind_for_fs(self):
+        if self.any:
+            return "*"
+        if self.na:
+            return "-"
+        return self.process_quoted_chars()
+
+    def process_quoted_chars(self):
+        res = ""
+        idx = 0
+        while idx < len(self.value):
+            c = self.value[idx]
+            if c != '\\':
+                res += c
+            else:
+                idx += 1
+                c = self.value[idx]
+                if c in ".-_":
+                    res += c
+                else:
+                    res += '\\' + c
+            idx += 1
+        return res
+
+ANY = CPEAttribute(any=True)
+NA = CPEAttribute(na=True)
+
+class CPE(object):
+
+    def __init__(self, part=None, vendor=None, product=None, version=None, update=None, edition=None, language=None, sw_edition=None, target_sw=None, target_hw=None, other=None):
+        self.part = part or CPEAttribute(any=True)
+        self.vendor = vendor or CPEAttribute(any=True)
+        self.product = product or CPEAttribute(any=True)
+        self.version = version or CPEAttribute(any=True)
+        self.update = update or CPEAttribute(any=True)
+        self.edition = edition or CPEAttribute(any=True)
+        self.language = language or CPEAttribute(any=True)
+        # Extended attributes:
+        self.sw_edition = sw_edition or CPEAttribute(any=True)
+        self.target_sw = target_sw or CPEAttribute(any=True)
+        self.target_hw = target_hw or CPEAttribute(any=True)
+        self.other = other or CPEAttribute(any=True)
+
+    def bind_to_URI(self):
+        uri = 'cpe:/'
+        uri += ':'.join(a.bind_for_URI() for a in (self.part, self.vendor, self.product, self.version, self.update))
+        # Special handling for edition
+        ed = self.edition.bind_for_URI()
+        sw_ed = self.sw_edition.bind_for_URI()
+        t_sw = self.target_sw.bind_for_URI()
+        t_hw = self.target_hw.bind_for_URI()
+        oth = self.other.bind_for_URI()
+        if sw_ed == "" and t_sw == "" and t_hw == "" and oth == "":
+            uri += ":" + ed
+        else:
+            uri += ":~" + '~'.join([ed, sw_ed, t_sw, t_hw, oth])
+        uri += ':' + self.language.bind_for_URI()
+        return uri.rstrip(':')
+
+    def unbind_URI(self, uri):
+        for idx, comp in enumerate(uri.split(':')):
+            if idx == 0:
+                continue
+            elif idx == 1:
+                self.part = decode(comp[1:])
+            elif idx == 2:
+                self.vendor = decode(comp)
+            elif idx == 3:
+                self.product = decode(comp)
+            elif idx == 4:
+                self.version = decode(comp)
+            elif idx == 5:
+                self.update = decode(comp)
+            elif idx == 6:
+                if comp == "" or comp[0] != '~':
+                    self.edition = decode(comp)
+                else:
+                    ed, sw_ed, t_sw, t_hw, oth = comp[1:].split('~')
+                    self.edition = decode(ed)
+                    self.sw_edition = decode(sw_ed)
+                    self.target_sw = decode(t_sw)
+                    self.target_hw = decode(t_hw)
+                    self.other = decode(oth)
+            elif idx == 7:
+                self.language = decode(comp)
+
+    def bind_to_fs(self):
+        fs = 'cpe:2.3:'
+        fs += ':'.join(a.bind_for_fs() for a in (self.part, self.vendor, self.product, self.version, self.update, self.edition, self.language, self.sw_edition, self.target_sw, self.target_hw, self.other))
+        return fs
+
+    def unbind_fs(self, fs):
+        for idx, v in enumerate(fs.split(':')):
+            v = unbind_value_fs(v)
+            if idx == 2:
+                self.part = v
+            elif idx == 3:
+                self.vendor = v
+            elif idx == 4:
+                self.product = v
+            elif idx == 5:
+                self.version = v
+            elif idx == 6:
+                self.update = v
+            elif idx == 7:
+                self.edition = v
+            elif idx == 8:
+                self.language = v
+            elif idx == 9:
+                self.sw_edition = v
+            elif idx == 10:
+                self.target_sw = v
+            elif idx == 11:
+                self.target_hw = v
+            elif idx == 12:
+                self.other = v
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testCPE.py	Mon Dec 29 14:30:39 2014 +0100
@@ -0,0 +1,212 @@
+import unittest
+
+from farolluz.parsers.cpe import CPE, CPEAttribute
+
+class testbindToURI(unittest.TestCase):
+    
+    def test_example1(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('microsoft'), product=CPEAttribute('internet_explorer'), version=CPEAttribute(r'8\.0\.6001'), update=CPEAttribute('beta'), edition=CPEAttribute(any=True))
+
+        self.assertEqual(cpe.bind_to_URI(), 'cpe:/a:microsoft:internet_explorer:8.0.6001:beta')
+    
+    def test_example2(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('microsoft'), product=CPEAttribute('internet_explorer'), version=CPEAttribute(r'8\.*'), update=CPEAttribute('sp?'))
+
+        self.assertEqual(cpe.bind_to_URI(), 'cpe:/a:microsoft:internet_explorer:8.%02:sp%01')
+
+    def test_example3(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('hp'), product=CPEAttribute('insight_diagnostics'), version=CPEAttribute(r'7\.4\.0\.1570'), update=CPEAttribute(na=True), sw_edition=CPEAttribute('online'), target_sw=CPEAttribute('win2003'), target_hw=CPEAttribute("x64"))
+
+        self.assertEqual(cpe.bind_to_URI(), 'cpe:/a:hp:insight_diagnostics:7.4.0.1570:-:~~online~win2003~x64~')
+
+    def test_example4(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('hp'), product=CPEAttribute('openview_network_manager'), version=CPEAttribute(r'7\.51'), target_sw=CPEAttribute('linux'))
+
+        self.assertEqual(cpe.bind_to_URI(), 'cpe:/a:hp:openview_network_manager:7.51::~~~linux~~')
+
+    def test_example5(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute(r'foo\\bar'), product=CPEAttribute(r'big\$money_manager_2010'), sw_edition=CPEAttribute('special'), target_sw=CPEAttribute('ipod_touch'), target_hw=CPEAttribute("80gb"))
+
+        self.assertEqual(cpe.bind_to_URI(), 'cpe:/a:foo%5cbar:big%24money_manager_2010:::~~special~ipod_touch~80gb~')
+
+class testunbindURI(unittest.TestCase):
+
+    def test_example1(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:microsoft:internet_explorer:8.0.6001:beta')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'microsoft')
+        self.assertEqual(cpe.product.value, 'internet_explorer')
+        self.assertEqual(cpe.version.value, r'8\.0\.6001')
+        self.assertEqual(cpe.update.value, 'beta')
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example2(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:microsoft:internet_explorer:8.%2a:sp%3f')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'microsoft')
+        self.assertEqual(cpe.product.value, 'internet_explorer')
+        self.assertEqual(cpe.version.value, r'8\.\*')
+        self.assertEqual(cpe.update.value, 'sp\?')
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example3(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:microsoft:internet_explorer:8.%02:sp%01')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'microsoft')
+        self.assertEqual(cpe.product.value, 'internet_explorer')
+        self.assertEqual(cpe.version.value, r'8\.*')
+        self.assertEqual(cpe.update.value, 'sp?')
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example4(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:hp:insight_diagnostics:7.4.0.1570::~~online~win2003~x64~')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'hp')
+        self.assertEqual(cpe.product.value, 'insight_diagnostics')
+        self.assertEqual(cpe.version.value, '7\.4\.0\.1570')
+        self.assertTrue(cpe.update.any)
+        self.assertTrue(cpe.edition.any)
+        self.assertEqual(cpe.sw_edition.value, 'online')
+        self.assertEqual(cpe.target_sw.value, 'win2003')
+        self.assertEqual(cpe.target_hw.value, 'x64')
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example5(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:hp:openview_network_manager:7.51:-:~~~linux~~')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'hp')
+        self.assertEqual(cpe.product.value, 'openview_network_manager')
+        self.assertEqual(cpe.version.value, '7\.51')
+        self.assertTrue(cpe.update.na)
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.sw_edition.any)
+        self.assertEqual(cpe.target_sw.value, 'linux')
+        self.assertTrue(cpe.target_hw.any)
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
+
+
+    def test_example6(self):
+        cpe = CPE()
+        self.assertRaises(KeyError, cpe.unbind_URI, 'cpe:/a:foo%5cbar:big%24money_2010%07:::~~special~ipod_touch~80gb~')
+
+    
+    def test_example7(self):
+        cpe = CPE()
+        cpe.unbind_URI('cpe:/a:foo~bar:big%7emoney_2010')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'foo\~bar')
+        self.assertEqual(cpe.product.value, 'big\~money_2010')
+        self.assertTrue(cpe.version.any)
+        self.assertTrue(cpe.update.any)
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example8(self):
+        cpe = CPE()
+        self.assertRaises(ValueError, cpe.unbind_URI, 'cpe:/a:foo:bar:12.%02.1234')
+
+class testbindFS(unittest.TestCase):
+
+    def test_example1(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('microsoft'), product=CPEAttribute('internet_explorer'), version=CPEAttribute(r'8\.0\.6001'), update=CPEAttribute('beta'), edition=CPEAttribute(any=True))
+
+        self.assertEqual(cpe.bind_to_fs(), 'cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*')
+
+    def test_example2(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('microsoft'), product=CPEAttribute('internet_explorer'), version=CPEAttribute(r'8\.*'), update=CPEAttribute('sp?'), edition=CPEAttribute(any=True))
+
+        self.assertEqual(cpe.bind_to_fs(), 'cpe:2.3:a:microsoft:internet_explorer:8.*:sp?:*:*:*:*:*:*')
+
+        cpe.version = CPEAttribute(r'8\.\*')
+
+        self.assertEqual(cpe.bind_to_fs(), 'cpe:2.3:a:microsoft:internet_explorer:8.\*:sp?:*:*:*:*:*:*')
+
+    def test_example3(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('hp'), product=CPEAttribute('insight'), version=CPEAttribute(r'7\.4\.0\.1570'), update=CPEAttribute(na=True), sw_edition=CPEAttribute("online"), target_sw=CPEAttribute("win2003"), target_hw=CPEAttribute("x64"))
+
+        self.assertEqual(cpe.bind_to_fs(), 'cpe:2.3:a:hp:insight:7.4.0.1570:-:*:*:online:win2003:x64:*')
+    
+    def test_example4(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute('hp'), product=CPEAttribute('openview_network_manager'), version=CPEAttribute(r'7\.51'), target_sw=CPEAttribute('linux'))
+
+        self.assertEqual(cpe.bind_to_fs(), 'cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*')
+
+    def test_example5(self):
+        cpe = CPE(part=CPEAttribute('a'), vendor=CPEAttribute(r'foo\\bar'), product=CPEAttribute(r'big\$money_2010'), sw_edition=CPEAttribute('special'), target_sw=CPEAttribute('ipod_touch'), target_hw=CPEAttribute("80gb"))
+
+        self.assertEqual(cpe.bind_to_fs(), r'cpe:2.3:a:foo\\bar:big\$money_2010:*:*:*:*:special:ipod_touch:80gb:*')
+
+class testunbind_fs(unittest.TestCase):
+
+    def test_example1(self):
+        cpe = CPE()
+        cpe.unbind_fs('cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'microsoft')
+        self.assertEqual(cpe.product.value, 'internet_explorer')
+        self.assertEqual(cpe.version.value, '8\.0\.6001')
+        self.assertEqual(cpe.update.value, "beta")
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.sw_edition.any)
+        self.assertTrue(cpe.target_sw.any)
+        self.assertTrue(cpe.target_hw.any)
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example2(self):
+        cpe = CPE()
+        cpe.unbind_fs('cpe:2.3:a:microsoft:internet_explorer:8.*:sp?:*:*:*:*:*:*')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'microsoft')
+        self.assertEqual(cpe.product.value, 'internet_explorer')
+        self.assertEqual(cpe.version.value, '8\.*')
+        self.assertEqual(cpe.update.value, "sp?")
+        self.assertTrue(cpe.edition.any)
+        self.assertTrue(cpe.sw_edition.any)
+        self.assertTrue(cpe.target_sw.any)
+        self.assertTrue(cpe.target_hw.any)
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
+
+    def test_example3(self):
+        cpe = CPE()
+        cpe.unbind_fs('cpe:2.3:a:hp:insight_diagnostics:7.4.0.1570:-:*:*:online:win2003:x64:*')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, 'hp')
+        self.assertEqual(cpe.product.value, 'insight_diagnostics')
+        self.assertEqual(cpe.version.value, '7\.4\.0\.1570')
+        self.assertTrue(cpe.update.na)
+        self.assertTrue(cpe.edition.any)
+        self.assertEqual(cpe.sw_edition.value, "online")
+        self.assertEqual(cpe.target_sw.value, "win2003")
+        self.assertEqual(cpe.target_hw.value, "x64")
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
+
+        self.assertRaises(ValueError, cpe.unbind_fs, 'cpe:2.3:a:hp:insight_diagnostics:7.4.*.1570:*:*:*:*:*:*')
+
+
+    def test_example4(self):
+        cpe = CPE()
+        cpe.unbind_fs(r'cpe:2.3:a:foo\\bar:big\$money:2010:*:*:*:special:ipod_touch:80gb:*')
+        self.assertEqual(cpe.part.value, 'a')
+        self.assertEqual(cpe.vendor.value, r'foo\\bar')
+        self.assertEqual(cpe.product.value, 'big\$money')
+        self.assertEqual(cpe.version.value, '2010')
+        self.assertTrue(cpe.update.any)
+        self.assertTrue(cpe.edition.any)
+        self.assertEqual(cpe.sw_edition.value, "special")
+        self.assertEqual(cpe.target_sw.value, "ipod_touch")
+        self.assertEqual(cpe.target_hw.value, "80gb")
+        self.assertTrue(cpe.other.any)
+        self.assertTrue(cpe.language.any)
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)