comparison farolluz/parsers/cpe.py @ 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
children bb1dd2a55643
comparison
equal deleted inserted replaced
37:ba0eb65d4134 40:1d9b2b06067e
1 """\
2 a cpe class to ease the creation of a producttree based on cpe
3
4 This is based on:
5
6 NIST Interagency Report 7695
7 Common Platform Enumeration: Naming Specification Version 2.3
8
9 CPE is a trademark of The MITRE Corporation.
10
11 """
12
13 import re
14
15 PCT_MAP ={'!': "%21", '"': "%22", '#': "%23", '$': "%24", '%': "%25", '&': "%26",
16 "'": "%27", '(': "%28", ')': "%29", '*': "%2a", '+': "%2b", ',': "%2c",
17 '/': "%2f", ':': "%3a", ';': "%3b", '<': "%3c", "=": "%3d", '>': "%3e",
18 '?': "%3f", '@': "%40", '[': "%5b", '\\': "%5c","]": "%5d", '^': "%5e",
19 '`': "%60", '{': "%7b", '|': "%7c", '}': "%7d", "~": "%7e"}
20
21 PCT_MAP_i = dict((v, k) for k, v in PCT_MAP.iteritems())
22
23 def pct_encode(c):
24 """ Returns the right percent-encoding of c """
25 if c in "-.":
26 return c
27 return PCT_MAP[c]
28 return {'!': "%21", '"': "%22", '#': "%23", '$': "%24", '%': "%25", '&': "%26",
29 "'": "%27", '(': "%28", ')': "%29", '*': "%2a", '+': "%2b", ',': "%2c",
30 "-": c, '.': c, '/': "%2f", ':': "%3a", ';': "%3b", '<': "%3c",
31 "=": "%3d", '>': "%3e", '?': "%3f", '@': "%40", '[': "%5b", '\\': "%5c",
32 "]": "%5d", '^': "%5e", '`': "%60", '{': "%7b", '|': "%7c", '}': "%7d",
33 "~": "%7e"}[c]
34
35 def decode(s):
36 if s == '':
37 return ANY
38 if s == '-':
39 return NA
40 s = s.lower()
41 res = ""
42 idx = 0
43 embedded = False
44 while idx < len(s):
45 c = s[idx]
46 if c in ".-~":
47 res += "\\" + c
48 embedded = True
49 elif c != '%':
50 res += c
51 embedded = True
52 else:
53 form = s[idx:idx+3]
54 if form == "%01":
55 if (((idx == 0) or (idx == (len(s) - 3))) or
56 ( not embedded and (s[idx-4:idx-1] == "%01")) or
57 (embedded and (len(s) > idx + 6) and (s[idx+3:idx+6] == "%01"))):
58 res += '?'
59 else:
60 raise ValueError
61 elif form == "%02":
62 if (idx == 0) or (idx == len(s) - 3):
63 res += '*'
64 else:
65 raise ValueError
66 else:
67 res += '\\' + PCT_MAP_i[form]
68 embedded = True
69 idx += 2
70 idx += 1
71 return CPEAttribute(res)
72
73 def unbind_value_fs(s):
74 if s == '*':
75 return ANY
76 if s == '-':
77 return NA
78 res = ""
79 idx = 0
80 embedded = False
81 while idx < len(s):
82 c = s[idx]
83 if re.match("[a-zA-Z0-9_]", c) is not None:
84 res += c
85 embedded = True
86 elif c == "\\":
87 res += s[idx:idx+2]
88 embedded = True
89 idx += 1
90 elif c == "*":
91 if (idx == 0) or (idx == (len(s) - 1)):
92 res += c
93 embedded = True
94 else:
95 raise ValueError
96 elif c == "?":
97 if (((idx == 0) or (idx == (len(s) - 1))) or
98 (not embedded and (s[idx - 1] == "?")) or
99 (embedded and (s[idx + 1] == "?"))):
100 res += c
101 embedded = False
102 else:
103 raise ValueError
104 else:
105 res += "\\" + c
106 embedded = True
107 idx += 1
108 return CPEAttribute(res)
109
110 class CPEAttribute(object):
111 """ We need a special class to deal with ANY / NA / "string" """
112
113 def __init__(self, value=None, any=False, na=False):
114 self.any = any
115 self.na = na
116 self.value = value
117
118 def bind_for_URI(self):
119 # print self.any, self.na, self.value
120 if self.any:
121 return ""
122 if self.na:
123 return '-'
124 return self.transform_for_uri()
125
126 def transform_for_uri(self):
127 res = ""
128 idx = 0
129 while idx < len(self.value):
130 c = self.value[idx]
131 if re.match("[a-zA-Z0-9_]", c) is not None:
132 res += c
133 elif c == '\\':
134 idx += 1
135 c = self.value[idx]
136 res += pct_encode(c)
137 elif c == '?':
138 res += "%01"
139 elif c == '*':
140 res += "%02"
141 idx += 1
142 return res
143
144 def bind_for_fs(self):
145 if self.any:
146 return "*"
147 if self.na:
148 return "-"
149 return self.process_quoted_chars()
150
151 def process_quoted_chars(self):
152 res = ""
153 idx = 0
154 while idx < len(self.value):
155 c = self.value[idx]
156 if c != '\\':
157 res += c
158 else:
159 idx += 1
160 c = self.value[idx]
161 if c in ".-_":
162 res += c
163 else:
164 res += '\\' + c
165 idx += 1
166 return res
167
168 ANY = CPEAttribute(any=True)
169 NA = CPEAttribute(na=True)
170
171 class CPE(object):
172
173 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):
174 self.part = part or CPEAttribute(any=True)
175 self.vendor = vendor or CPEAttribute(any=True)
176 self.product = product or CPEAttribute(any=True)
177 self.version = version or CPEAttribute(any=True)
178 self.update = update or CPEAttribute(any=True)
179 self.edition = edition or CPEAttribute(any=True)
180 self.language = language or CPEAttribute(any=True)
181 # Extended attributes:
182 self.sw_edition = sw_edition or CPEAttribute(any=True)
183 self.target_sw = target_sw or CPEAttribute(any=True)
184 self.target_hw = target_hw or CPEAttribute(any=True)
185 self.other = other or CPEAttribute(any=True)
186
187 def bind_to_URI(self):
188 uri = 'cpe:/'
189 uri += ':'.join(a.bind_for_URI() for a in (self.part, self.vendor, self.product, self.version, self.update))
190 # Special handling for edition
191 ed = self.edition.bind_for_URI()
192 sw_ed = self.sw_edition.bind_for_URI()
193 t_sw = self.target_sw.bind_for_URI()
194 t_hw = self.target_hw.bind_for_URI()
195 oth = self.other.bind_for_URI()
196 if sw_ed == "" and t_sw == "" and t_hw == "" and oth == "":
197 uri += ":" + ed
198 else:
199 uri += ":~" + '~'.join([ed, sw_ed, t_sw, t_hw, oth])
200 uri += ':' + self.language.bind_for_URI()
201 return uri.rstrip(':')
202
203 def unbind_URI(self, uri):
204 for idx, comp in enumerate(uri.split(':')):
205 if idx == 0:
206 continue
207 elif idx == 1:
208 self.part = decode(comp[1:])
209 elif idx == 2:
210 self.vendor = decode(comp)
211 elif idx == 3:
212 self.product = decode(comp)
213 elif idx == 4:
214 self.version = decode(comp)
215 elif idx == 5:
216 self.update = decode(comp)
217 elif idx == 6:
218 if comp == "" or comp[0] != '~':
219 self.edition = decode(comp)
220 else:
221 ed, sw_ed, t_sw, t_hw, oth = comp[1:].split('~')
222 self.edition = decode(ed)
223 self.sw_edition = decode(sw_ed)
224 self.target_sw = decode(t_sw)
225 self.target_hw = decode(t_hw)
226 self.other = decode(oth)
227 elif idx == 7:
228 self.language = decode(comp)
229
230 def bind_to_fs(self):
231 fs = 'cpe:2.3:'
232 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))
233 return fs
234
235 def unbind_fs(self, fs):
236 for idx, v in enumerate(fs.split(':')):
237 v = unbind_value_fs(v)
238 if idx == 2:
239 self.part = v
240 elif idx == 3:
241 self.vendor = v
242 elif idx == 4:
243 self.product = v
244 elif idx == 5:
245 self.version = v
246 elif idx == 6:
247 self.update = v
248 elif idx == 7:
249 self.edition = v
250 elif idx == 8:
251 self.language = v
252 elif idx == 9:
253 self.sw_edition = v
254 elif idx == 10:
255 self.target_sw = v
256 elif idx == 11:
257 self.target_hw = v
258 elif idx == 12:
259 self.other = v
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)