6a820cd6180a6d5e70313ecdb605d1c7bd3b1a66
[openembedded.git] / contrib / qa / oe_audit.py
1 #!/usr/bin/env python
2
3 import bb
4
5 def read_available(filename):
6     """
7     Parses the output of bitbake -s
8     minus the first few lines
9     """
10     f = open(filename)
11     packages = {}
12
13     for line in f:
14         # str.split can not be used as we have multiple whitespace
15         first_space = line.find(" ")
16         package = line[0:first_space]
17         rest = line[first_space+1:]
18         pv = rest.strip().split(" ")[0]
19
20         packages[package] = pv
21     return packages
22
23
24 def map_names(str):
25     """Map a FreeBSD name to OE"""
26     maps = {
27         "libpurple" : "pidgin",
28         "php4" : "php",
29         "php5" : "php",
30         "expat2" : "expat",
31         "freeciv-gtk" : "freeciv",
32         "pcre" : "libpcre",
33         "vim-gnome" : "vim",
34         "python23" : "python",
35         "python24" : "python",
36         "python25" : "python",
37         "python+ipv6" : "python",
38         "wget-devel" : "wget",
39         "xchat2" : "xchat",
40         "freetype" : "freetype",
41         "qemu-devel" : "qemu",
42         "krb5-beta" : "krb5",
43         "freeciv-gtk2": "freeciv",
44         "gtk" : "gtk+",
45         "wget+ipv6" : "wget",
46         "ja-gd" : "gd",
47         "openvpn-devel" : "openvpn",
48         "mpeg123-esound" : "mpeg123",
49         "mpeg123-nas" : "mpeg123",
50         "cdrtools-cjk" : "cdrtools",
51         "apache+mod_ssl+mod_deflate" : "apache2",
52         "apache+mod_ssl*" : "apache2",
53         "vim-gtk2" : "vim",
54         "zh-emacs" : "emacs",
55         "{ja-,}bugzilla" : "bugzilla",
56         "zh-tin" : "tin",
57         "mozilla+ipv6": "mozilla",
58         "mozilla-embeddded" : "mozilla",
59         "rar" : "unrar",
60         "libsndfile" : "libsndfile1",
61         "sylpheed-gtk2": "sylpheed",
62         "cdrtools-devel": "cdrtools",
63         "pine4-ssl": "pine",
64         "apache" : "apache2",
65         "ghostscript-gpl" : "gs",
66         "ghostscript-gnu-nox11" : "gs",
67         "ghostscript8" : "gs",
68         "ghostscript-afpl-nox11" : "gs",
69         "ghostscript-afpl" : "gs",
70         "isc-dhcp" : "dhcp",
71         "mplayer-gtk" : "mplayer",
72         "xerces-c2" : "xerces-c",
73         "libxml" : "libxml2",
74         "vim+ruby" : "vim",
75         "mplayer{,-gtk}{,-esound}" : "mplayer",
76         "proftpd-devel": "proftpd",
77         "neon28" : "neon",
78         "php4-dba" : "php",
79         "php5-{cgi,cli}" : "php",
80
81     }
82
83     try:
84         return maps[str]
85     except:
86         return str
87
88 def is_not_in_oe(name):
89     """Method to reject packages not in OE"""
90     not_in = [
91         # packages that we will never have...
92         "linux-firefox", "fr-linux-netscape", "linux-netscape-{communicator,navigator}",
93         "linux_base", "ja-netscape7", "{ja,ko}-netscape-{communicator,navigator}-linux", "zhTW-linux-mozillafirebird", "ja-linux-mozillafirebird-gtk1", "el-linux-mozillafirebird", "mozilla-firebird", "netscape7",
94         "acroread4", "acroread7", "acroread5",
95         "linux-openmotif", "linux-flock", "linux-jdk", "linux-curl", "linux-png", "linux-firefox-devel",
96
97         # packages that we don't have now but maybe will have in
98         # the future and blacklisting them here might be a problem
99         "openoffice.org-2-devel", "openoffice.org-2", "it-openoffice", "ca-openoffice","sl-openoffice-SI", "ja-openoffice",
100         "drupal4", "drupal5", "drupal6", "drupal-pubcookie",
101         "gpdf",
102         "nagios",
103         "kdenetwork", "ja-kdelibs", "kdegraphics", "kdepim", "kdebase4-runtime",
104         "xemacs-devel", "xemacs-devel-21.5", "xemacs-mule", "zh-xemacs", "zh-xemacs-mule",
105         "geeklog", "apach13-ssl", "nvidia-driver", "eGroupWare", "varnish", "heimdal",
106         "bugzilla", "agenda-snow-libs", "mozilla",
107     ]
108
109     return name in not_in
110
111 class freebsd_info:
112     """
113     Handles an entry like the one below:
114     vulnerability-test-port>=2000<2010.02.26|http://cvsweb.freebsd.org/ports/security/vulnerability-test-port/|Not vulnerable, just a test port (database: 2010-02-26)
115     """
116     def __init__(self, name, versions, link, kind):
117         self.name = name
118         self.versions = versions
119         self.link = link
120
121     @classmethod
122     def split_versions(self, input):
123         """
124         Split versions by <, >, >=, >=
125         """
126         versions = []
127         last_pos = 0
128
129         # Try to determine <, >, >=, <=
130         # we will have to carry stuff on to find the
131         # version..
132         i = 0
133         while i < len(input) - 1:
134             c1 = input[i]
135             c2 = input[i+1]
136             if c1 != '<' and c1 != '>' and c1 != '=':
137                 i = i + 1
138                 continue
139
140             # is a '=' coming behind it?
141             next = i + 1
142             if c2 == '=':
143                 next = next + 1
144
145             # submit
146             if last_pos != 0:
147                 versions.append((next_type, input[last_pos:i]))
148
149             # remember stuff
150             next_type = input[i:next]
151             last_pos = next
152             i = next
153
154         assert last_pos != 0
155         versions.append((next_type, input[last_pos:len(input)]))
156         return versions
157
158     def __repr__(self):
159         return "%s: %s" % (self.name, self.versions)
160
161 def create_infos(line):
162     split = line.split("|")
163     for i in range(0, len(split[0])):
164         c = split[0][i]
165         if c != '<' and c != '=' and c != '>':
166                 continue
167         name = map_names(split[0][0:i])
168         versions = freebsd_info.split_versions(split[0][i:])
169         break
170
171     if is_not_in_oe(name):
172         print "Not in oe %s" % name
173         return []
174
175     link = split[1]
176     kind = split[2]
177     return [freebsd_info(name, versions, link, kind)]
178
179 def read_auditfile(filename):
180     """
181     Read an uncompressed audit file from freebsd
182     """
183     f = open(filename)
184     packages = {}
185     for line in f:
186         if line.startswith("#"):
187             continue
188
189         infos = create_infos(line)
190         for info in infos:
191             try:
192                 packages[info.name].append(info)
193             except:
194                 packages[info.name] = []
195                 packages[info.name].append(info)
196     return packages
197
198
199 def strip_oe_version(oe_version):
200     """
201     We need to strip the package epoch... and the PR to compare
202     it to the FreeBSD versions. Also FreeBSD seems to use _N as
203     PR so we might need to do more..
204     """
205     split = oe_version.split(':', 1)
206     ver = split[1]
207
208     split = ver.rsplit('-r', 1)
209     ver = split[0]
210     return ver
211
212 def strip_bsd_version(bsd_version):
213     """
214     FreeBSD is adding ,1 for revisions.. remove that
215     """
216     split = bsd_version.rsplit(',', 1)
217     split = split[0]
218     split = split.rsplit('_', 1)
219     return split[0]
220
221 def compare_versions(oe, freebsd, not_known):
222     def handle_package(oe_name, bsd_name):
223         if not oe_name in oe:
224             if oe_name == bsd_name:
225                 print >> not_known, "%s is not in OE" % oe_name
226             return
227
228         oe_version = strip_oe_version(oe[oe_name])
229         for ver in freebsd[bsd_name]:
230             affected = True
231             str = []
232             for (cmp, vers) in ver.versions:
233                 bsd_ver = strip_bsd_version(vers)
234                 cmp_res = bb.utils.vercmp(('0', oe_version, 'r0'), ('0', bsd_ver, 'r0'))
235                 if cmp == '<':
236                     if cmp_res >= 0:
237                         affected = False
238                     pass
239                 elif cmp == '<=':
240                     if cmp_res > 0:
241                         affected = False
242                     pass
243                 elif cmp == '>':
244                     if cmp_res <= 0:
245                         affected = False
246                     pass
247                 elif cmp == '>=':
248                     if cmp_res < 0:
249                         affected = False
250                     pass
251                 elif cmp == '=':
252                     if cmp_res > 0:
253                         affected = False
254                 else:
255                     print cmp
256                     assert True
257
258                 str.append("%s %s %s %s" % (oe_name, oe_version, cmp, bsd_ver))
259             if affected:
260                 print " && ".join(str), ver.link
261
262     for package in freebsd.keys():
263         # handle the various versions of OE packages
264         handle_package(package, package)
265         handle_package("%s-native" % package, package)
266         handle_package("%s-full-native" % package, package)
267         handle_package("%s-sdk" % package, package)
268
269
270 # read the input data
271 oe_packages = read_available("available")
272 freebsd_vuln = read_auditfile("auditfile")
273 buggy = open("not_in_oe.bugs", "w+")
274
275 compare_versions(oe=oe_packages, freebsd=freebsd_vuln, not_known=buggy)