Author: fw
Date: 2005-09-13 14:08:22 +0000 (Tue, 13 Sep 2005)
New Revision: 1951

Added:
   bin/update-vulnerabilities
Modified:
   Makefile
   bin/update-packages
   lib/python/bugs.py
   lib/python/debian_support.py
   lib/python/security_db.py
Log:
First step towards calculating sets of vulnerable packages.  This is
currently directed towards testing (but does not yet process the
secure-testing archive).

A new table is added, so "make clean" is required.

The remaining problem (besides potential bugs in the code) is how to
deal with kernel updates, IOW how to detect them and ignore them.

bin/update-vulnerabilities:
  New script, updates the bugs_status table.

lib/python/bugs.py (PackageNote.affects):
  Fix all kinds of errors.  The code never ran before, it seems. 8-/
(PackageNote.fixedVersion):
  Add.
(BugBase.hasTODO):
  Add.
(BugReservedCVE, BugRejectedCVE):
  Mark as not-for-us.
(FileBase.rawRecords):
  Mark all un-annotated bugs after STOP: field as not-for-us.

lib/python/security_db.py (DB.initSchema):
  Add table bugs_status.
(DB.finishBugs):
  Run to completion even if there are conflicting CAN/CVE entries.
(DB.getVersion, calculateVulnerabilities):
  New methods.
(test):
  Update.

lib/python/debian_support.py (Version):
  Add a type check.

Makefile:
  Add stamps/calc-vulns target.

bin/update-packages:
  Fix typo in comment.


Modified: Makefile
===================================================================
--- Makefile    2005-09-13 13:53:07 UTC (rev 1950)
+++ Makefile    2005-09-13 14:08:22 UTC (rev 1951)
@@ -13,7 +13,8 @@
 PACKAGE_FILES = $(wildcard data/packages/*_Sources) \
        $(wildcard data/packages/*_Packages)
 
-all: stamps/bug-lists-imported stamps/packages-imported
+all: stamps/bug-lists-imported stamps/packages-imported \
+       stamps/calc-vulns
 
 stamps/bug-lists-imported: bin/update-bug-list-db \
        $(BUG_LISTS) $(PYTHON_MODULES)
@@ -30,6 +31,12 @@
        fi
        touch $@
 
+stamps/calc-vulns: stamps/bug-lists-imported stamps/packages-imported
+       if test -e stamps/packages-downloaded ; then \
+               $(PYTHON) bin/update-vulnerabilities ; \
+       fi
+       touch $@
+
 clean:
        -rm data/security.db
        -rm stamps/*-*

Modified: bin/update-packages
===================================================================
--- bin/update-packages 2005-09-13 13:53:07 UTC (rev 1950)
+++ bin/update-packages 2005-09-13 14:08:22 UTC (rev 1951)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-# This script download and imports Debian package files.
+# This script downloads and imports Debian package files.
 
 import errno
 import os

Added: bin/update-vulnerabilities
===================================================================
--- bin/update-vulnerabilities  2005-09-13 13:53:07 UTC (rev 1950)
+++ bin/update-vulnerabilities  2005-09-13 14:08:22 UTC (rev 1951)
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+# This script recalculates the vulnerability information in the
+# security database.
+
+import errno
+import os
+import os.path
+import string
+import sys
+
+def setup_paths():
+    check_file = 'lib/python/debian_support.py'
+    path = os.getcwd()
+    while 1:
+        if os.path.exists("%s/%s" % (path, check_file)):
+            sys.path = [path + '/lib/python'] + sys.path
+            return path
+        idx = string.rfind(path, '/')
+        if idx == -1:
+            raise ImportError, "could not setup paths"
+        path = path[0:idx]
+root_path = setup_paths()
+
+import security_db
+
+db_file = root_path + '/data/security.db'
+assert os.path.exists(db_file)
+db = security_db.DB(db_file)
+c = db.writeTxn()
+db.calculateVulnerabilities(c)
+db.commit(c)


Property changes on: bin/update-vulnerabilities
___________________________________________________________________
Name: svn:executable
   + *

Modified: lib/python/bugs.py
===================================================================
--- lib/python/bugs.py  2005-09-13 13:53:07 UTC (rev 1950)
+++ lib/python/bugs.py  2005-09-13 14:08:22 UTC (rev 1951)
@@ -76,7 +76,7 @@
 
         if type(version) == types.StringType:
             version = debian_support.Version(version)
-        if type(release) == types.ReleaseType:
+        if type(release) == types.StringType:
             release = Release(release)
 
         if release is None:
@@ -89,8 +89,15 @@
                 # If there's a release spec, it must match ours.
                 return False
         # Standard version comparison if the releases match.
-        return self.version is None or version < self.version
+        return self.fixed_version is None or version < self.fixed_version
 
+    def fixedVersion(self):
+        """ Returns a string noting that the bug was fixed, or 'unfixed'."""
+        if self.fixed_version:
+            return "fixed in %s" % self.fixed_version
+        else:
+            return "unfixed"
+
     def writeDB(self, cursor, bug_name):
         """Writes the object to an SQLite database.
 
@@ -209,6 +216,13 @@
         else:
             return ''
 
+    def hasTODO(self):
+        """Returns True if the bug has a TODO item."""
+        for (t, c) in self.comments:
+            if t == "TODO":
+                return True
+        return False
+
     def writeDB(self, cursor):
         """Writes the record to an SQLite3 database."""
 
@@ -302,6 +316,8 @@
         if comments is None:
             comments = []
         BugBase.__init__(self, fname, lineno, None, name, "RESERVED", comments)
+        # for-us bugs are upgraded to real Bug objects.
+        self.not_for_us = True
     def cveStatus(self):
         return 'RESERVED'
 
@@ -309,6 +325,8 @@
     """Class for rejected CVE entries."""
     def __init__(self, fname, lineno, name):
         BugBase.__init__(self, fname, lineno, None, name, "REJECTED", [])
+        # for-us bugs are upgraded to real Bug objects.
+        self.not_for_us = True
     def cveStatus(self):
         return 'REJECTED'
 
@@ -375,12 +393,12 @@
 
         self.getLine()
         record = []
+        after_stop = False
         while self.line:
             first_line = self.lineno
            
             if self.re_stop.match(self.line):
-                # Theoretically, we could stop here, but we want
-                # syntax checks for the remaining records, too.
+                after_stop = True
                 self.getLine()
                 continue
                 
@@ -408,6 +426,11 @@
                     break
             # line contains the next line at this point.
 
+            if after_stop and len(record) == 0:
+                # Patch in not-for-us field, so that bugs after STOP:
+                # are ignored.
+                record = [(first_line, 'NOTE: not-for-us (entry too old)')]
+
             yield (first_line, date, record_name, description, record)
 
     def __iter__(self):

Modified: lib/python/debian_support.py
===================================================================
--- lib/python/debian_support.py        2005-09-13 13:53:07 UTC (rev 1950)
+++ lib/python/debian_support.py        2005-09-13 14:08:22 UTC (rev 1951)
@@ -57,6 +57,7 @@
 
     def __init__(self, version):
         """Creates a new Version object."""
+        assert type(version) == types.StringType, `version`
         self.__asString = version
         self.__parsed = self.__parse(version)
 

Modified: lib/python/security_db.py
===================================================================
--- lib/python/security_db.py   2005-09-13 13:53:07 UTC (rev 1950)
+++ lib/python/security_db.py   2005-09-13 14:08:22 UTC (rev 1951)
@@ -148,6 +148,13 @@
          normalized_target TEXT NOT NULL DEFAULT '',
          PRIMARY KEY (source, target))""")
 
+        cursor.execute("""CREATE TABLE bugs_status
+        (bug_name TEXT NOT NULL,
+         release TEXT NOT NULL,
+         note INTEGER NOT NULL,
+         reason TEXT NOT NULL,
+         PRIMARY KEY (bug_name, release, note))""")
+         
     def updateSources(self, cursor, release, archive, packages):
         """Reads a Sources file and adds it to the database.
 
@@ -347,12 +354,12 @@
                 found = False
                 for (t,) in list(cursor.execute("""SELECT name FROM bugs
                 WHERE name IN (?, ?)""", (can_target, cve_target))):
-                    assert not found, t
                     cursor.execute("""UPDATE bugs_xref
                     SET normalized_target = ?
                     WHERE source = ? AND target = ?""",
                                    (t, source, target))
                     found = True
+                    break
                 if not found:
                     b = bugs.BugFromDB(cursor, source)
                     warnings.append\
@@ -375,8 +382,111 @@
                      % (b.source_file, b.source_line, target))
 
         return warnings
+
+    def getVersion(self, cursor, release, package):
+        """Returns the version number for package in release.
+
+        Package can be a source or binary package.  Binary package
+        versions take precedence.
+
+        Security updates etc. are not considered."""
+
+        versions = list(cursor.execute(
+            """SELECT version FROM binary_packages
+            WHERE package = ? AND release = ?""", (package, release)))
+        if versions:
+            return min(map(lambda (v,): debian_support.Version(v), versions))
+
+        versions = list(cursor.execute(
+            """SELECT version FROM source_packages
+            WHERE package = ? AND release = ?""", (package, release)))
+        if versions:
+            assert len(versions) == 1
+            return debian_support.Version(versions[0][0])
+        
+        return None
+
+    def calculateVulnerabilities(self, cursor):
+        """Calculate vulnerable packages.
+
+        To each package note, a release-specific vulnerability status
+        is attached.  Currently, only etch/testing is processed.
+        """
+
+        cursor.execute("DELETE FROM bugs_status")
+
+        def markVulnerable(bug, release, note, reason):
+            cursor.execute("""INSERT INTO bugs_status
+            (bug_name, release, note, reason) VALUES (?, ?, ?, ?)""",
+                           (bug.name, release, note, reason))
+
+        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:
+                    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:
+                vulnerable = True
+                markVulnerable(b, 'etch', 0, 'status is unclear')
+
+            return vulnerable
+
+        # First handle the DSAs.  Cache results in DSA_status (used
+        # for CAN/CVE below).
+
+        bug_names = list(cursor.execute(
+            "SELECT name FROM bugs WHERE name LIKE 'DSA-%'"))
+        DSA_status = {}
+        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.
+
+        bug_names = list(cursor.execute(
+            """SELECT name FROM bugs
+            WHERE (NOT not_for_us)
+            AND NOT (name LIKE 'DSA-%' OR name LIKE 'DTSA-%')"""))
+        for (bug_name,) in bug_names:
+            b = bugs.BugFromDB(cursor, bug_name)
+            if b.notes:
+                calcVuln(b)
+                continue
+
+            if b.hasTODO():
+                markVulnerable(b, 'etch', 0, 'TODO items present')
+                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')
+                
     def check(self, cursor=None):
         """Runs a simple consistency check and prints the results."""
 
@@ -442,8 +552,14 @@
     db.updatePackages(cursor, 'sarge', 'main', 'i386',
             debian_support.PackageFile(data_prefix
                                        + 'sarge_main_i386_Packages'))
+    db.updatePackages(cursor, 'sarge', 'main', 'ia64',
+            debian_support.PackageFile(data_prefix
+                                       + 'sarge_main_ia64_Packages'))
     db.commit(cursor)
 
+    assert str(db.getVersion(cursor, 'sarge', 'ale')) == '0.7.1-1', \
+           db.getVersion(cursor, 'sarge', 'ale')
+
     # db.check(cursor)
 
     cursor = db.writeTxn()


_______________________________________________
Secure-testing-commits mailing list
Secure-testing-commits@lists.alioth.debian.org
http://lists.alioth.debian.org/mailman/listinfo/secure-testing-commits

Reply via email to