Mercurial > farol > farolluz
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 |