[Secure-testing-commits] r2072 - lib/python
Florian Weimer
fw at costa.debian.org
Wed Sep 21 17:47:00 UTC 2005
Author: fw
Date: 2005-09-21 17:46:59 +0000 (Wed, 21 Sep 2005)
New Revision: 2072
Modified:
lib/python/bugs.py
lib/python/security_db.py
Log:
Add "FIXES:" and "FIXED-BY:" directives.
lib/python/bugs.py (PackageNote):
New attribute "bug_origin".
(PackageNote.writeDB):
No longer skipr writing when self.id has been set (so that writeDB
can be used for cloning notes). Write the bug_origin attribute.
(PackageNoteFromDB):
Read the bug_origin attribute.
(BugBase):
Initialize the xref_fixes and xref_fixedby attributes.
(BugBase.writeDB):
Write them.
(Bug):
Pass through xref_fixes and xref_fixedby in constructor.
(BugFroMDB):
Load them.
(FileBase):
New regexps re_xref_fixes_required, re_xref_fixes,
re_xref_fixedby_required, re_xref_fixedby.
(FileBase.__iter__):
Record FIXES: and FIXED-BY:.
lib/python/security_db.py (DB):
Bump schema version.
(DB.initSchema):
Add bug_origin column to the packages_notes table.
Add copy_notes column to bugs_xref.
(DB.readBugs):
Remove incremental reading. Add new code that copies package notes,
as requested by the FIXES: and FIXED-BY: directives.
Modified: lib/python/bugs.py
===================================================================
--- lib/python/bugs.py 2005-09-21 15:15:42 UTC (rev 2071)
+++ lib/python/bugs.py 2005-09-21 17:46:59 UTC (rev 2072)
@@ -67,6 +67,7 @@
self.urgency = urgency
self.bugs = []
self.package_kind = "unknown"
+ self.bug_origin = None
def affects(self, version, release=None):
"""Returns true if this package note affects the given version.
@@ -131,16 +132,9 @@
return map(lambda ver: (ver, versions[str(ver)]), l)
return (sort(vulnerable_versions), sort(fixed_versions))
- def writeDB(self, cursor, bug_name):
- """Writes the object to an SQLite database.
+ def writeDB(self, cursor, bug_name, bug_origin=''):
+ """Writes the object to an SQLite database."""
- If the id attibute is already set, it is assumed that the
- object has already been written.
- """
-
- if self.id is not None:
- return
-
if self.fixed_version:
v = str(self.fixed_version)
else:
@@ -150,10 +144,10 @@
else:
r = ''
cursor.execute("""INSERT INTO package_notes
- (bug_name, package, fixed_version, release, urgency)
- VALUES (?, ?, ?, ?, ?)""",
+ (bug_name, package, fixed_version, release, urgency, bug_origin)
+ VALUES (?, ?, ?, ?, ?, ?)""",
(bug_name, self.package, v, r,
- str(self.urgency)))
+ str(self.urgency), bug_origin))
for (rowid,) in cursor.execute('SELECT last_insert_rowid()'):
self.id = rowid
for b in self.bugs:
@@ -182,10 +176,10 @@
class PackageNoteFromDB(PackageNote):
def __init__(self, cursor, nid):
- for bug_name, package, fixed_version, release, urgency, package_kind\
- in cursor.execute\
+ for (bug_name, package, fixed_version, release, urgency,
+ package_kind, bug_origin) in cursor.execute\
("""SELECT bug_name, package, fixed_version, release, urgency,
- package_kind
+ package_kind, bug_origin
FROM package_notes WHERE id = ?""", (nid,)):
PackageNote.__init__(package, fixed_version, release, urgency)
self.id = nid
@@ -241,6 +235,8 @@
self.comments = comments
self.notes = []
self.xref = []
+ self.xref_fixes = []
+ self.xref_fixedby = []
self.not_for_us = False
def isFromCVE(self):
@@ -313,11 +309,26 @@
raise ValueError, \
"cross reference to %s appears multiple times" % x
+ # We use INSERT OR REPLACE below because the copy annotations
+ # must override plain cross-references.
+
+ for x in self.xref_fixes:
+ cursor.execute("""INSERT OR REPLACE INTO bugs_xref
+ (source, target, copy_notes) VALUES (?, ?, 1)""",
+ (self.name, x))
+
+ for x in self.xref_fixedby:
+ cursor.execute("""INSERT OR REPLACE INTO bugs_xref
+ (source, target, copy_notes) VALUES (?, ?, 1)""",
+ (x, self.name))
+ # Swap x and self.name because FIXED-BY: works in the other
+ # direction.
+
class Bug(BugBase):
"""Class for bugs for which we have some data."""
def __init__(self, fname, lineno, date, name, description, comments, notes,
- xref, not_for_us=False):
+ xref, xref_fixes, xref_fixedby, not_for_us=False):
assert len(notes) == 0 or isinstance(notes[0], PackageNote)
assert len(xref) == 0 or type(xref[0]) == types.StringType
assert type(not_for_us) == types.BooleanType
@@ -325,6 +336,8 @@
description, comments)
self.notes = notes
self.xref = xref
+ self.xref_fixes = xref_fixes
+ self.xref_fixedby = xref_fixedby
self.not_for_us = not_for_us
def mergeNotes(self):
@@ -388,7 +401,7 @@
Bug.__init__(self, data['source_file'], data['source_line'],
data['release_date'], name,
data['description'], comments=[],
- notes=[], xref=[],
+ notes=[], xref=[], xref_fixes=[], xref_fixedby=[],
not_for_us=not not data['not_for_us'])
for (x,) in cursor.execute\
('SELECT target FROM bugs_xref WHERE source = ?', (name,)):
@@ -401,18 +414,28 @@
self.comments.append((t, c))
# temporary list required because loadBugs needs the cursor
- for nid, package, fixed_version, release, urgency, package_kind \
- in list(cursor.execute
+ for (nid, package, fixed_version, release, urgency, package_kind,
+ bug_origin) in list(cursor.execute
("""SELECT id, package, fixed_version, release, urgency,
- package_kind
+ package_kind, bug_origin
FROM package_notes WHERE bug_name = ?""", (name,))):
n = PackageNote(package, fixed_version, release, urgency)
n.id = nid
n.bug_name = name
n.package_kind = package_kind
+ n.bug_origin = bug_origin
n.loadBugs(cursor)
self.notes.append(n)
+ for (target,) in cursor.execute(
+ "SELECT target FROM bugs_xref WHERE source = ? AND copy_notes",
+ (name,)):
+ self.xref_fixes.append(target)
+ for (target,) in cursor.execute(
+ "SELECT source FROM bugs_xref WHERE target = ? AND copy_notes",
+ (name,)):
+ self.xref_fixedby.append(target)
+
def getDebianBugs(self, cursor):
"""Returns a list of Debian bugs to which the bug report refers."""
return map(lambda (x,): x, cursor.execute(
@@ -465,6 +488,13 @@
re_xref_entry = re.compile('^(?:(?:CAN|CVE)-\d{4}-\d{4}'
+ r'|VU#\d{6}'
+ r'|DSA-\d+(?:-\d+)?|DTSA-\d+-\d+)$')
+ re_xref_entry_own = re.compile(
+ '^(?:(?:CAN|CVE)-\d{4}-\d{4}|DSA-\d+(?:-\d+)?|DTSA-\d+-\d+)$')
+
+ re_xref_fixes_required = re.compile(r'^FIXES')
+ re_xref_fixes = re.compile(r'^FIXES:\s+(.*?)\s*$')
+ re_xref_fixedby_required = re.compile(r'^FIXED-BY')
+ re_xref_fixedby = re.compile(r'^FIXED-BY:\s+(.*?)\s*$')
# temporary hack, until we know what "!" actually means.
re_package_required = re.compile(r'^(?:\[.*\]\s*)?[-!]')
@@ -562,6 +592,8 @@
not_for_us = None
xref = []
+ xref_fixes = []
+ xref_fixedby = []
pkg_notes = []
comments = []
cve_reserved = False
@@ -569,21 +601,38 @@
first_bug = 0
for (lineno, r) in record:
- if self.re_xref_required.match(r):
- match = self.re_xref.match(r)
- if match:
- (xref_string,) = match.groups()
- for x in self.re_whitespace.split(xref_string):
- if self.re_xref_entry.match(x):
- xref.append(x)
- else:
- self.raiseSyntaxError\
- ("invalid cross reference " + `x`, lineno)
- continue
+ def handle_xref(re_required, re_real, re_entry, target):
+ if re_required.match(r):
+ match = re_real.match(r)
+ if match:
+ (xref_string,) = match.groups()
+ for x in self.re_whitespace.split(xref_string):
+ if re_entry.match(x):
+ target.append(x)
+ else:
+ self.raiseSyntaxError\
+ ("invalid cross reference " + `x`,
+ lineno)
+ return True
+ else:
+ self.raiseSyntaxError(
+ "expected cross reference, got: " + `r`,
+ lineno)
else:
- self.raiseSyntaxError("expected cross reference, got: "
- + `r`, lineno)
-
+ return False
+
+ if handle_xref(self.re_xref_required, self.re_xref,
+ self.re_xref_entry, xref):
+ continue
+ if handle_xref(self.re_xref_fixes_required,
+ self.re_xref_fixes,
+ self.re_xref_entry_own, xref_fixes):
+ continue
+ if handle_xref(self.re_xref_fixedby_required,
+ self.re_xref_fixedby,
+ self.re_xref_entry_own, xref_fixedby):
+ continue
+
if self.re_package_required.match(r):
match = self.re_package.match(r)
if match:
@@ -658,7 +707,9 @@
yield self.finishBug(Bug(self.file.name, first_lineno,
date, record_name, description,
comments,
- notes=pkg_notes, xref=xref))
+ notes=pkg_notes, xref=xref,
+ xref_fixes=xref_fixes,
+ xref_fixedby=xref_fixedby))
else:
yield BugReservedCVE(self.file.name, first_lineno,
record_name, comments)
@@ -684,13 +735,18 @@
first_lineno)
yield self.finishBug(Bug(self.file.name, first_lineno, date,
record_name, description, comments,
- notes=[], xref=xref, not_for_us=True))
+ notes=[], xref=xref,
+ xref_fixes=xref_fixes,
+ xref_fixedby=xref_fixedby,
+ not_for_us=True))
else:
if not self.isUniqueName(record_name):
record_name = 'FAKE-%07d-%06d' % (first_bug, first_lineno)
yield self.finishBug(Bug(self.file.name, first_lineno, date,
record_name, description,
- comments, notes=pkg_notes, xref=xref))
+ comments, notes=pkg_notes, xref=xref,
+ xref_fixes=xref_fixes,
+ xref_fixedby=xref_fixedby))
def finishBug(self, bug):
"""Applies a transformation to the bug after it has been
Modified: lib/python/security_db.py
===================================================================
--- lib/python/security_db.py 2005-09-21 15:15:42 UTC (rev 2071)
+++ lib/python/security_db.py 2005-09-21 17:46:59 UTC (rev 2072)
@@ -93,7 +93,7 @@
self.db = apsw.Connection(name)
self.verbose = verbose
- self.schema_version = 9
+ self.schema_version = 10
c = self.cursor()
for (v,) in c.execute("PRAGMA user_version"):
@@ -195,7 +195,8 @@
fixed_version_id INTEGER NOT NULL DEFAULT 0,
release TEXT NOT NULL,
package_kind TEXT NOT NULL DEFAULT 'unknown',
- urgency TEXT NOT NULL)""")
+ urgency TEXT NOT NULL,
+ bug_origin TEXT NOT NULL DEFAULT '')""")
cursor.execute(
"""CREATE UNIQUE INDEX package_notes_bug
ON package_notes(bug_name, package, release)""")
@@ -226,7 +227,11 @@
(source TEXT NOT NULL,
target TEXT NOT NULL,
normalized_target TEXT NOT NULL DEFAULT '',
+ copy_notes INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (source, target))""")
+ cursor.execute(
+ """CREATE INDEX bugs_xref_normalized_target
+ ON bugs_xref(normalized_target)""")
cursor.execute("""CREATE TABLE bug_status
(bug_name TEXT NOT NULL,
@@ -539,50 +544,32 @@
if self.verbose:
print "readBugs:"
- def clear_db(filename):
- cursor.execute(
- """CREATE TEMPORARY TABLE bugs_to_delete
- (tbd TEXT NOT NULL PRIMARY KEY)""")
- cursor.execute(
- """INSERT INTO bugs_to_delete
- SELECT name FROM bugs WHERE source_file = ?""",
- (filename,))
+ def clear_db(cleared=[False]):
+ # Avoid clearing the database multiple times.
+ if cleared[0]:
+ return
+ else:
+ cleared[0] = True
+
+ cursor.execute("DELETE FROM debian_bugs")
+ cursor.execute("DELETE FROM bugs")
+ cursor.execute("DELETE FROM package_notes")
+ cursor.execute("DELETE FROM bugs_notes")
+ cursor.execute("DELETE FROM bugs_xref")
- cursor.execute(
- """DELETE FROM debian_bugs
- WHERE EXISTS (SELECT 1
- FROM package_notes AS p, bugs_to_delete AS b
- WHERE p.id = debian_bugs.note
- AND p.bug_name = b.tbd)""")
-
- cursor.execute("""DELETE FROM bugs
- WHERE EXISTS (SELECT * FROM bugs_to_delete
- WHERE tbd = name)""")
- cursor.execute("""DELETE FROM package_notes
- WHERE EXISTS (SELECT * FROM bugs_to_delete
- WHERE tbd = bug_name)""")
- cursor.execute("""DELETE FROM bugs_notes
- WHERE EXISTS (SELECT * FROM bugs_to_delete
- WHERE tbd = bug_name)""")
- cursor.execute("""DELETE FROM bugs_xref
- WHERE EXISTS (SELECT * FROM bugs_to_delete
- WHERE tbd = source)""")
-
# The *_status tables are regenerated anyway, no need to
# delete them here.
- cursor.execute("""DROP TABLE bugs_to_delete""")
-
self._clearVersions(cursor)
-
- def do_parse(source):
+
+ def do_parse(source, cleared=[False]):
errors = []
+
+ clear_db()
if self.verbose:
print " reading " + `source.name`
- clear_db(source.name)
-
for bug in source:
try:
bug.writeDB(cursor)
@@ -592,43 +579,46 @@
if errors:
raise InsertError(errors)
- def read_one(source):
- filename = source.name
+ def has_changed(filename):
current_print = self.filePrint(filename)
-
for (old_print,) in cursor.execute(
"SELECT inodeprint FROM inodeprints WHERE file = ?",
(filename,)):
if old_print == current_print:
return False
- do_parse(source)
- cursor.execute(
- "UPDATE inodeprints SET inodeprint = ? WHERE file = ?",
- (current_print, filename))
- return True
-
- # No inodeprints entry, load file and add one.
- do_parse(source)
- cursor.execute(
- "INSERT INTO inodeprints (file, inodeprint) VALUES (?, ?)",
- (filename, current_print))
+ else:
+ return True
return True
+ sources = ((bugs.CANFile, '/CAN/list'),
+ (bugs.CVEFile, '/CVE/list'),
+ (bugs.DSAFile, '/DSA/list'),
+ (bugs.DTSAFile, '/DTSA/list'))
+
unchanged = True
- if read_one(bugs.CANFile(path + '/CAN/list')):
- unchanged = False
- if read_one(bugs.CVEFile(path + '/CVE/list')):
- unchanged = False
- if read_one(bugs.DSAFile(path + '/DSA/list')):
- unchanged = False
- if read_one(bugs.DTSAFile(path + '/DTSA/list')):
- unchanged = False
-
+ for (_, name) in sources:
+ if has_changed(path + name):
+ unchanged = False
+ break
if unchanged:
if self.verbose:
print " finished (no changes)"
return
+ clear_db()
+
+ def read_one(source):
+ filename = source.name
+ current_print = self.filePrint(filename)
+
+ do_parse(source)
+ cursor.execute(
+ """INSERT OR REPLACE INTO inodeprints (inodeprint, file)
+ VALUES (?, ?)""", (current_print, filename))
+
+ for (cls, name) in sources:
+ read_one(cls(path + name))
+
errors = []
if self.verbose:
@@ -656,10 +646,11 @@
if self.verbose:
print " normalize CAN/CVE references"
-
+
+ cursor.execute("UPDATE bugs_xref SET normalized_target = target")
for source, target in list(cursor.execute\
("""SELECT source, target FROM bugs_xref
- WHERE normalized_target = ''""")):
+ WHERE target LIKE 'CAN-%' OR target LIKE 'CVE-%'""")):
if bugs.BugBase.re_cve_name.match(target):
can_target = 'CAN-' + target[4:]
cve_target = 'CVE-' + target[4:]
@@ -695,6 +686,64 @@
("%s: %d: reference to unknwown advisory %s"
% (b.source_file, b.source_line, target))
+ if self.verbose:
+ print " apply FIXES"
+
+ target_sources = {}
+ for source, target in list(cursor.execute(
+ """SELECT source, normalized_target
+ FROM bugs_xref WHERE copy_notes""")):
+ if target_sources.has_key(target):
+ target_sources[target][source] = True
+ else:
+ target_sources[target] = {source : True}
+
+ # Recursively collect all sources for each target. Add new
+ # sources until the set of sources stabilizes.
+ for sources in target_sources.values():
+ while 1:
+ old_size = len(sources.keys())
+ for src in sources.keys():
+ for s in target_sources.get(src, {}).keys():
+ sources[s] = True
+ if len(sources.keys()) == old_size:
+ break
+
+ # Copy all the notes from all sources.
+ for (target, sources) in target_sources.items():
+ sources = sources.keys()
+ for source in sources:
+ source_bug = bugs.BugFromDB(cursor, source)
+ for n in source_bug.notes:
+ # Only copy "real" notes, not notes which have
+ # already bee ncopied.
+ if n.bug_origin:
+ continue
+ if n.release:
+ rel = str(n.release)
+ else:
+ rel = ''
+ present = False
+ for (version, origin) in list(cursor.execute(
+ """SELECT fixed_version, bug_origin
+ FROM package_notes
+ WHERE bug_name = ? AND package = ? AND release = ?""",
+ (target, n.package, rel))):
+ if version <> str(n.fixed_version):
+ bug = bugs.BugFromDB(cursor, origin or target)
+ errors.append(
+ ("%s: %d: version %s for package %s "
+ + "conflicts with %s")
+ % (bug.source_file, bug.source_line,
+ version, n.package, bug_source.name))
+ errors.append("%s: %d: location of %s"
+ % (source_bug.source_file,
+ source_bug.source_line,
+ source_bug.name))
+ present = True
+ if not present:
+ n.writeDB(cursor, target, bug_origin=source)
+
if errors:
raise InsertError(errors)
More information about the Secure-testing-commits
mailing list