Author: fw Date: 2005-12-23 13:15:25 +0000 (Fri, 23 Dec 2005) New Revision: 3129 Modified: bin/update-db lib/python/security_db.py Log: lib/python/security_db.py (DB.initSchema): Add index on package_notes(package) (no schema version bump needed). (DB.calculateDebsecan0): Renamed from DB.calculateDebsecan. (DB.calculateDebsecan1): New method which generates version 1 format (with pinning support wtc.). (DB.calculateDebsecan): Invokes both the version 0 and version 1 methods. bin/update-db: Adjust accordingly. Modified: bin/update-db ==================================================================--- bin/update-db 2005-12-23 13:11:59 UTC (rev 3128) +++ bin/update-db 2005-12-23 13:15:25 UTC (rev 3129) @@ -78,8 +78,7 @@ # debsecan data -for release in ('''', ''woody'', ''sarge'', ''etch''): - db.calculateDebsecan(release) +db.calculateDebsecan() # Everything worked well. Modified: lib/python/security_db.py ==================================================================--- lib/python/security_db.py 2005-12-23 13:11:59 UTC (rev 3128) +++ lib/python/security_db.py 2005-12-23 13:15:25 UTC (rev 3129) @@ -233,6 +233,9 @@ cursor.execute( """CREATE UNIQUE INDEX package_notes_bug ON package_notes(bug_name, package, release)""") + cursor.execute( + """CREATE INDEX package_notes_package + ON package_notes(package)""") cursor.execute("""CREATE TABLE debian_bugs (bug INTEGER NOT NULL, @@ -1229,8 +1232,8 @@ VALUES (?, ?, ?, ?)""", (bug_name, suite, status, pkgs)) - def calculateDebsecan(self, release): - """Create data for the debsecan tool.""" + def calculateDebsecan0(self, release): + """Create data for the debsecan tool (VERSION 0 format).""" c = self.cursor() @@ -1344,6 +1347,204 @@ c.execute("DROP TABLE vulnlist") + def calculateDebsecan1(self): + """Calculates debsecan data (release-independent, VERSION 1).""" + + c = self.cursor() + + result_start = [''VERSION 1''] + bug_to_index = {} + bug_to_remote_flag = {} + + def fill_bug_to_index(): + index = 0 + for (bug, desc, remote) in c.execute( + """SELECT DISTINCT p.bug_name, b.description, + (SELECT range_remote FROM nvd_data + WHERE cve_name = p.bug_name) + FROM package_notes AS p, bugs AS b + WHERE p.urgency <> ''unimportant'' + AND name NOT LIKE ''FAKE-0000000-%'' + AND b.name = p.bug_name + AND p.package_kind IN (''source'', ''binary'', ''unknown'') + ORDER BY p.bug_name"""): + if remote is None: + remote = ''?'' + elif remote: + remote = ''R'' + else: + remote = '' '' + + # Normalize FAKE-* names a bit. The line number (which + # makes the name unique) is completely useless for the + # client. + + if bug[0:5] == "FAKE-": + name = ''-''.join(bug.split(''-'')[0:2]) + else: + name = bug + + result_start.append("%s,,%s" % (name, desc)) + bug_to_index[bug] = index + bug_to_remote_flag[bug] = remote + index += 1 + result_start.append('''') + fill_bug_to_index() + + urgency_to_flag = {''low'' : ''L'', ''medium'' : ''M'', ''high'' : ''H'', + ''unknown'' : '' ''} + + vuln_list = [] + source_packages = {} + def fill_vuln_list(source_packages=source_packages): + for (bug, package) in list(c.execute( + """SELECT DISTINCT bug_name, package + FROM package_notes + WHERE bug_name NOT LIKE ''FAKE-0000000-%'' + AND package_kind IN (''source'', ''binary'', ''unknown'') + GROUP BY package, bug_name + ORDER BY package, bug_name""")): + + unstable_fixed = ''0'' + total_urgency = '''' + other_versions = {} + is_binary = False + is_unknown = False + fixed_releases = {} + for (release, kind, urgency, version) in list(c.execute( + """SELECT release, package_kind, urgency, fixed_version + FROM package_notes WHERE bug_name = ? AND package = ?""", + (bug, package))): + if total_urgency: + if urgency == ''unknown'': + total_urgency = urgency + elif total_urgency <> ''unknown'' \ + and bugs.internUrgency(urgency) \ + > bugs.internUrgency(total_urgency): + total_urgency = urgency + else: + total_urgency = urgency + + if kind == ''binary'': + is_binary = True + elif kind == ''source'': + source_packages[package] = True + else: + is_unknown = True + + if release == '''': + unstable_fixed = version + if version: + v_ref = debian_support.Version(version) + for (v,) in c.execute("""SELECT version + FROM source_packages WHERE name = ? + AND release = ''sid'' AND subrelease = ''''""", + (package,)): + if debian_support.Version(v) > v_ref: + fixed_releases[''sid''] = True + break + elif version is not None: + fixed_releases[release] = True + + # Collect newer versions in the same release + # (which are supposed to fix the same bug). + + v_ref = debian_support.Version(version) + for (v,) in c.execute("""SELECT fixed_version + FROM package_notes + WHERE package = ? AND release = ?""", + (package, release)): + if v is None: + continue + if debian_support.Version(v) > v_ref: + other_versions[v] = True + + for (v,) in c.execute("""SELECT version + FROM source_packages WHERE name = ? + AND release = ? AND subrelease IN ('''', ''security'')""", + (package, release)): + if debian_support.Version(v) > v_ref: + other_versions[v] = True + + # Check if the issue does not actually mark any packages + # as vulnerable. + if total_urgency == ''unimportant'' \ + or (unstable_fixed == ''0'' and len(other_versions) == 0): + continue + + if unstable_fixed is None: + unstable_fixed = '''' + bs_flag = ''S'' + if is_binary: + assert not is_unknown + bs_flag = ''B'' + elif is_unknown: + bs_flag = '' '' + + other_versions = other_versions.keys() + other_versions.sort() + other_versions = '' ''.join(other_versions) + + vuln_list.append(("%s,%d,%c%c%c" + % (package, bug_to_index[bug], + bs_flag, urgency_to_flag[total_urgency], + bug_to_remote_flag[bug]), + fixed_releases.keys(), + ",%s,%s" + % (unstable_fixed, other_versions))) + fill_vuln_list() + source_packages = source_packages.keys() + source_packages.sort() + + def store_value(name, value): + value = base64.encodestring(zlib.compress(value, 9)) + c.execute("""INSERT OR REPLACE INTO debsecan_data + VALUES (?, ?)""", (name, value)) + + def gen_release(release): + result = result_start[:] + + for (prefix, releases, suffix) in vuln_list: + if release in releases: + fixed = ''F'' + else: + fixed = '' '' + result.append(prefix + fixed + suffix) + result.append('''') + + for sp in source_packages: + bp_list = [] + for (bp,) in c.execute("""SELECT name FROM binary_packages + WHERE source = ? AND release = ? AND subrelease = '''' + ORDER BY name""", + (sp, release)): + bp_list.append(bp) + if bp_list <> [sp]: + # We intentionally store the empty list, it means + # that the source package is obsolete as a whole. + result.append("%s,%s" % (sp, '' ''.join(bp_list))) + result.append('''') + + store_value(''release/1/'' + release, ''\n''.join(result)) + + for release in (''sid'', ''etch'', ''sarge'', ''woody''): + gen_release(release) + + result = result_start + for (prefix, release, suffix) in vuln_list: + result.append(prefix + '' '' + suffix) + result.append('''') + result.append('''') + result.append('''') + store_value (''release/1/GENERIC'', ''\n''.join(result)) + + def calculateDebsecan(self): + """Calculate all debsecan data.""" + for release in ('''', ''woody'', ''sarge'', ''etch''): + self.calculateDebsecan0(release) + self.calculateDebsecan1() + + def getDebsecan(self, name): """Returns the debsecan data item NAME.""" for (data,) in self.cursor().execute(