[Secure-testing-commits] r2015 - lib/python
Florian Weimer
fw at costa.debian.org
Fri Sep 16 08:14:54 UTC 2005
Author: fw
Date: 2005-09-16 08:14:54 +0000 (Fri, 16 Sep 2005)
New Revision: 2015
Modified:
lib/python/bugs.py
lib/python/security_db.py
Log:
lib/python/security_db.py (DB):
Update schema versioning code. Replace table bugs_status with
bug_status. Add bug_name and release columns to source_package_status
and binary_package_status.
(DB.calculateVulnerabilities):
First attempt at bug status calculation. It's rather broken,
unfortunately.
lib/python/bugs.py (BugFromDB.getStatus):
New method, to get the results of the bug status calculation.
Modified: lib/python/bugs.py
===================================================================
--- lib/python/bugs.py 2005-09-16 05:02:08 UTC (rev 2014)
+++ lib/python/bugs.py 2005-09-16 08:14:54 UTC (rev 2015)
@@ -412,6 +412,16 @@
return result
+ def getStatus(self, cursor):
+ """Calculate bug status.
+
+ Returns list of tuples (RELEASE, STATUS, REASON)."""
+
+ return list(cursor.execute(
+ """SELECT release, status, reason
+ FROM bug_status WHERE bug_name = ?""",
+ (self.name,)))
+
class BugReservedCVE(BugBase):
"""Class for reserved CVE entries."""
def __init__(self, fname, lineno, name, comments=None):
Modified: lib/python/security_db.py
===================================================================
--- lib/python/security_db.py 2005-09-16 05:02:08 UTC (rev 2014)
+++ lib/python/security_db.py 2005-09-16 08:14:54 UTC (rev 2015)
@@ -96,7 +96,7 @@
'sarge' : 'stable',
'woody': 'oldstable'}
- self.schema_version = 5
+ self.schema_version = 8
c = self.cursor()
for (v,) in c.execute("PRAGMA user_version"):
@@ -139,6 +139,12 @@
"""Creates the database schema."""
cursor = self.cursor()
+ # Set the schema version to an invalid value which is
+ # different from zero. We can use this to detect a partially
+ # created schema.
+
+ cursor.execute("PRAGMA user_version = 1")
+
# This gives us better performance (it's usually the file
# system block size).
@@ -229,17 +235,21 @@
normalized_target TEXT NOT NULL DEFAULT '',
PRIMARY KEY (source, target))""")
- cursor.execute("""CREATE TABLE bugs_status
+ cursor.execute("""CREATE TABLE bug_status
(bug_name TEXT NOT NULL,
release TEXT NOT NULL,
- note INTEGER NOT NULL,
+ status TEXT NOT NULL
+ CHECK (status IN ('vulnerable', 'fixed', 'unknown',
+ 'partially-fixed', 'todo')),
reason TEXT NOT NULL,
- PRIMARY KEY (bug_name, release, note))""")
+ PRIMARY KEY (bug_name, release))""")
cursor.execute("""CREATE TABLE source_package_status
(note INTEGER NOT NULL,
package INTEGER NOT NULL,
vulnerable INTEGER NOT NULL,
+ bug_name TEXT NOT NULL,
+ release TEXT NOT NULL,
PRIMARY KEY (note, package))""")
cursor.execute(
"""CREATE INDEX source_package_status_package
@@ -249,16 +259,16 @@
(note INTEGER NOT NULL,
package INTEGER NOT NULL,
vulnerable INTEGER NOT NULL,
+ bug_name TEXT NOT NULL,
+ release TEXT NOT NULL,
PRIMARY KEY (note, package))""")
cursor.execute(
"""CREATE INDEX binary_package_status_package
ON binary_package_status(package)""")
-
- # Put this at the end. Any exception will leave the schema
- # version at 0, so we automatically recreate the schema once
- # the application is started after the underlying error has
- # been fixed.
-
+ cursor.execute(
+ """CREATE INDEX binary_package_status_bug_name
+ ON binary_package_status(bug_name)""")
+
cursor.execute("PRAGMA user_version = %d" % self.schema_version)
def filePrint(self, filename):
@@ -759,13 +769,13 @@
WHERE package_kind = 'unknown'
AND EXISTS (SELECT * FROM binary_packages AS p
WHERE p.name = package_notes.package)""")
- for (bug_name, s_package, b_package) in cursor.execute(
+ for (bug_name, s_package, b_package) in list(cursor.execute(
"""SELECT DISTINCT s.bug_name, s.package, b.package
FROM package_notes AS s, package_notes AS b, binary_packages AS p
WHERE s.bug_name = b.bug_name
AND s.package_kind = 'source'
AND b.package_kind = 'binary'
- AND p.name = b.package AND p.source = s.package"""):
+ AND p.name = b.package AND p.source = s.package""")):
b = bugs.BugFromDB(cursor, bug_name)
result.append("%s:%d: source and binary package annotations"
% (b.source_file, b.source_line))
@@ -774,10 +784,20 @@
result.append("%s:%d: binary package: %s"
% (b.source_file, b.source_line, b_package))
+ for (bug_name, pkg_name, release) in list(cursor.execute(
+ """SELECT DISTINCT bug_name, package, release FROM package_notes
+ WHERE package_kind = 'binary' AND release <> ''""")):
+ b = bugs.BugFromDB(cursor, bug_name)
+ result.append("%s:%d: binary package %s used with release %s"
+ % (b.source_file, b.source_line, `pkg_name`,
+ `release`))
+
if self.verbose:
print " remove old status"
cursor.execute("DELETE FROM source_package_status")
cursor.execute("DELETE FROM binary_package_status")
+ cursor.execute("DELETE FROM bug_status")
+
if self.verbose:
print " calculate package status"
print " source packages (unqualified)"
@@ -785,7 +805,8 @@
cursor.execute(
"""INSERT INTO source_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.version_id < n.fixed_version_id
+ n.fixed_version IS NULL OR p.version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, source_packages AS p
WHERE n.release = '' AND p.name = n.package""")
@@ -797,7 +818,8 @@
cursor.execute(
"""INSERT OR REPLACE INTO source_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.version_id < n.fixed_version_id
+ n.fixed_version IS NULL OR p.version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, source_packages AS p
WHERE p.name = n.package
AND p.release = n.release""")
@@ -807,28 +829,34 @@
cursor.execute(
"""INSERT INTO binary_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.source_version_id < n.fixed_version_id
+ n.fixed_version IS NULL
+ OR p.source_version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, binary_packages AS p
WHERE n.release = '' AND p.source = n.package""")
cursor.execute(
"""INSERT OR REPLACE INTO binary_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.source_version_id < n.fixed_version_id
+ n.fixed_version IS NULL
+ OR p.source_version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, binary_packages AS p
WHERE p.source = n.package AND p.release = n.release""")
- # Almost the same binary packages. We prefer source packages,
- # so we skip all notes which have already source packages
- # attached. (Of course, we do not have to add status
- # information for binary package separately.)
+ # Almost the samefor binary packages. We prefer interpreting
+ # package names as source packages, so we only process the
+ # notes which refer to binary packages. (Of course, we do not
+ # have to add status information for binary package
+ # separately.)
if self.verbose:
print " binary packages (unqualified)"
cursor.execute(
"""INSERT INTO binary_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.version_id < n.fixed_version_id
+ n.fixed_version IS NULL OR p.version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, binary_packages AS p
WHERE n.release = '' AND p.name = n.package
AND n.package_kind = 'binary'""")
@@ -838,114 +866,178 @@
cursor.execute(
"""INSERT OR REPLACE INTO binary_package_status
SELECT n.id, p.rowid,
- n.fixed_version IS NULL OR p.version_id < n.fixed_version_id
+ n.fixed_version IS NULL OR p.version_id < n.fixed_version_id,
+ n.bug_name, p.release
FROM package_notes AS n, binary_packages AS p
WHERE p.name = n.package AND p.release = n.release
AND n.package_kind = 'binary'""")
- return result
-
+ # Calculate the release-specific bug status.
if self.verbose:
- print " clearing old data"
- cursor.execute("DELETE FROM bugs_status")
+ print " calculate release status"
- def markVulnerable(bug, release, note, reason):
- cursor.execute("""INSERT INTO bugs_status
- (bug_name, release, note, reason) VALUES (?, ?, ?, ?)""",
- (bug.name, release, note, reason))
+ c = self.cursor()
- def calcVuln(bug):
- vulnerable = False
- note_found = False
- for n in bug.notes:
- # ignore all notes conditioned on releases.
- if n.release is not None: # assumes 'etch'
+ # Packages relevant for testing. This includes the packages
+ # from unstable.
+
+ package_by_release = {}
+ binary_packages_in_testing = {}
+ for x in ('etch', 'sid'):
+ package_by_release[x] = {}
+ for (pkg_name, release) in cursor.execute(
+ """SELECT name, release FROM binary_packages
+ WHERE release IN ('etch', 'sid')"""):
+ package_by_release[release][pkg_name] = True
+ binary_packages_in_testing[pkg_name] = True
+
+ for (bug_name,) in cursor.execute(
+ "SELECT name FROM bugs WHERE NOT not_for_us"):
+
+ # The algorith below roughly proceeds as follows:
+ #
+ # For each package:
+ # Is this package in testing/unstable? If not, exit.
+ #
+ # Differentiate between the following cases:
+ # For all architectures with security support, the
+ # package in testing is not vulnerable (fully fixed)
+ #
+ # For all architectures with security support, the
+ # package is not vulnerable in testing or
+ # testing-security, and there exists a package (on a
+ # security support architecture) which is vulnerable
+ # in testing (partially fixed)
+ #
+ # There exists an architecture with security support
+ # where the package is fixed in testing, and there
+ # exists a non-vulnerable architecture in testing
+ # (should not happen, partially-fixed)
+ #
+ # Same as the preceding case, but including
+ # test-security; this can actually happen
+ # (partially-fixed, secure-testing is out-of-date on
+ # some architectures)
+ #
+ # There exists an architecture with security support
+ # where the package is fixed in unstable, and all
+ # packages in testing are vulnerable, and the
+ # package is in testing (fixed in unstable)
+ #
+ # For some supported architecture the package is in
+ # testing, and vulnerable. It is
+ #
+ # The package is not in testing on any supported
+ # architecture,
+ #
+ # At least this is the plan. The code below probably does
+ # something slightly different. 8-(
+
+ available_archs = {}
+ vulnerable_in_other = {}
+ vulnerable_in_testing = {}
+ fixed_in_testing = {}
+ fixed_in_security = {}
+
+ def record_archs_per_package(dict, pkg, archs):
+ if not dict.has_key(pkg):
+ dict[pkg] = {}
+ for arch in archs.split(','):
+ dict[pkg][arch] = True
+
+ for (pkg_name, release, subrelease, archs, vulnerable) \
+ in c.execute(
+ """SELECT DISTINCT
+ p.name, p.release, p.subrelease, p.archs, vulnerable
+ FROM binary_package_status AS s, binary_packages AS p
+ WHERE s.bug_name = ? AND p.rowid = s.package""", (bug_name,)):
+ if not binary_packages_in_testing.has_key(pkg_name):
continue
- note_found = True
- v = self.getVersion(cursor, 'etch', n.package)
- if v is None:
- # Package is not in testing, go on.
- continue
- if n.affects(v):
- vulnerable = True
- markVulnerable(b, 'etch', n.id,
- "%s (%s) is vulnerable, %s"
- % (n.package, v, n.fixedVersion()))
- if bug.hasTODO():
- vulnerable = True
- markVulnerable(b, 'etch', 0, 'TODO items present')
- elif not note_found:
- # We found no matching note. Maybe all packages have
- # been removed?
- if bug.notes:
- for n in bug.notes:
- if self.releaseContainsPackage \
- (cursor, 'etch', n.package):
- markVulnerable(b, 'etch', 0,
- 'applicable package note for %s missing'
- % n.package)
- vulnerable = True
- else:
- vulnerable = True
- markVulnerable(b, 'etch', 0, 'status is unclear')
+ record_archs_per_package(available_archs, pkg_name, archs)
- return vulnerable
+ if release == 'etch':
+ if vulnerable:
+ record_archs_per_package(vulnerable_in_testing,
+ pkg_name, archs)
+ else:
+ if subrelease == '':
+ record_archs_per_package(fixed_in_testing,
+ pkg_name, archs)
+ record_archs_per_package(fixed_in_security,
+ pkg_name, archs)
+ elif subrelease == 'security':
+ record_archs_per_package(fixed_in_security,
+ pkg_name, archs)
+ elif vulnerable:
+ record_archs_per_package(vulnerable_in_other,
+ pkg_name, archs)
- # First handle the DSAs. Cache results in DSA_status (used
- # for CAN/CVE below).
+ def record(status, reason):
+ if status <> 'vulnerable':
+ ((has_todo,),) = c.execute(
+ """SELECT EXISTS (SELECT * FROM bugs_notes
+ WHERE bug_name = ? AND typ = 'TODO')""",
+ (bug_name,))
+ if has_todo:
+ status = 'todo'
+ reason = 'see notes below'
+
+ c.execute(
+ """INSERT INTO bug_status
+ (bug_name, release, status, reason)
+ VALUES (?, 'testing', ?, ?)""",
+ (bug_name, status, reason))
- if self.verbose:
- print " reading DSAs"
- bug_names = list(cursor.execute(
- """SELECT name FROM bugs
- WHERE name LIKE 'DSA-%' AND NOT not_for_us"""))
- DSA_status = {}
- if self.verbose:
- print " rating DSAs"
- for (bug_name,) in bug_names:
- b = bugs.BugFromDB(cursor, bug_name)
- DSA_status[bug_name] = calcVuln(b)
-
- # Process the CAN/CVE/FAKE entries. If an entry has no
- # package annotations, but it references a non-vulnerable DSA,
- # we assume that the current is not affect either.
+ if len(available_archs.keys()) == 0:
+ record('fixed',
+ 'package(s) neither in testing nor in unstable')
+ continue
- if self.verbose:
- print " reading other entries"
- bug_names = list(cursor.execute(
- """SELECT name FROM bugs
- WHERE (NOT not_for_us)
- AND NOT (name LIKE 'DSA-%' OR name LIKE 'DTSA-%')"""))
- if self.verbose:
- print " rating other entries"
- for (bug_name,) in bug_names:
- b = bugs.BugFromDB(cursor, bug_name)
- if b.notes:
- calcVuln(b)
+ totally_unfixed_packages = []
+ testing_missing_archs = {}
+ security_missing_archs = {}
+ for (pkg_name, archs) in vulnerable_in_other.items():
+ fixed_somewhere = False
+ for arch in archs.keys():
+ if fixed_in_testing.get(pkg_name, {}).has_key(arch):
+ fixed_somewhere = True
+ else:
+ testing_missing_archs[arch] = True
+ if fixed_in_security.get(pkg_name, {}).has_key(arch):
+ fixed_somewhere = True
+ else:
+ security_missing_archs[arch] = True
+ if not fixed_somewhere:
+ totally_unfixed_packages.append(pkg_name)
+
+ if totally_unfixed_packages:
+ totally_unfixed_packages.sort()
+ if len(totally_unfixed_packages) == 1:
+ record('vulnerable', 'package %s is vulnerable'
+ % totally_unfixed_packages[0])
+ else:
+ record('vulnerable', 'packages %s are vulnerable'
+ % ', '.join(totally_unfixed_packages))
continue
- if b.hasTODO():
- markVulnerable(b, 'etch', 0, 'TODO items present')
+ if security_missing_archs.keys():
+ record('partially-fixed',
+ 'fixed via testing-security, '
+ + 'but architectures out of date: '
+ + ', '.join(security_missing_archs.keys()))
continue
-
- dsa_found = False
- for x in b.xref:
- if x[0:4] == 'DSA-':
- dsa_found = True
- if DSA_status[x]:
- markVulnerable(b, 'etch', 0,
- 'vulnerability %s referenced' % x)
- break
- if not dsa_found:
- markVulnerable(b, 'etch', 0, 'status is unclear')
-
- if self.verbose:
- print " finished"
+ if testing_missing_archs.keys():
+ record('partially-fixed', 'fixed in testing-security')
+ continue
+
+ record('fixed', 'packages are not vulnerable')
+
return result
+
def check(self, cursor=None):
"""Runs a simple consistency check and prints the results."""
More information about the Secure-testing-commits
mailing list