| 1 |
# security_db.py -- simple, CVE-driven Debian security bugs database
|
| 2 |
# Copyright (C) 2005 Florian Weimer <fw@deneb.enyo.de>
|
| 3 |
#
|
| 4 |
# This program is free software; you can redistribute it and/or modify
|
| 5 |
# it under the terms of the GNU General Public License as published by
|
| 6 |
# the Free Software Foundation; either version 2 of the License, or
|
| 7 |
# (at your option) any later version.
|
| 8 |
#
|
| 9 |
# This program is distributed in the hope that it will be useful,
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 12 |
# GNU General Public License for more details.
|
| 13 |
#
|
| 14 |
# You should have received a copy of the GNU General Public License
|
| 15 |
# along with this program; if not, write to the Free Software
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
| 17 |
|
| 18 |
"""This module implements a small database for tracking security bugs.
|
| 19 |
|
| 20 |
Note that the database is always secondary to the text files. The
|
| 21 |
database is only an implementation tool, and not used for maintaining
|
| 22 |
the data.
|
| 23 |
|
| 24 |
The data is kept in a SQLite 3 database.
|
| 25 |
|
| 26 |
FIXME: Document the database schema once it is finished.
|
| 27 |
"""
|
| 28 |
|
| 29 |
import apsw
|
| 30 |
import base64
|
| 31 |
import bugs
|
| 32 |
import cPickle
|
| 33 |
import cStringIO
|
| 34 |
import glob
|
| 35 |
import os
|
| 36 |
import os.path
|
| 37 |
import re
|
| 38 |
import sys
|
| 39 |
import types
|
| 40 |
import zlib
|
| 41 |
|
| 42 |
import debian_support
|
| 43 |
import dist_config
|
| 44 |
|
| 45 |
class InsertError(Exception):
|
| 46 |
"""Class for capturing insert errors.
|
| 47 |
|
| 48 |
The 'errors' member collects all error messages.
|
| 49 |
"""
|
| 50 |
|
| 51 |
def __init__(self, errors):
|
| 52 |
assert len(errors) > 0, errors
|
| 53 |
assert type(errors) == types.ListType, errors
|
| 54 |
assert type(errors[0])== types.StringType, errors
|
| 55 |
self.errors = errors
|
| 56 |
|
| 57 |
def __str__(self):
|
| 58 |
return self.errors[0] + ' [more...]'
|
| 59 |
|
| 60 |
def mergeLists(a, b):
|
| 61 |
"""Merges two lists."""
|
| 62 |
if type(a) == types.StringType:
|
| 63 |
if a == "":
|
| 64 |
a = []
|
| 65 |
else:
|
| 66 |
a = a.split(',')
|
| 67 |
if type(b) == types.StringType:
|
| 68 |
if b == "":
|
| 69 |
b = []
|
| 70 |
else:
|
| 71 |
b = b.split(',')
|
| 72 |
result = {}
|
| 73 |
for x in a:
|
| 74 |
result[x] = 1
|
| 75 |
for x in b:
|
| 76 |
result[x] = 1
|
| 77 |
result = result.keys()
|
| 78 |
result.sort()
|
| 79 |
return result
|
| 80 |
|
| 81 |
class NVDEntry:
|
| 82 |
"""A class for an entry in the nvd_data table.
|
| 83 |
Objects have the same fileds as the table."""
|
| 84 |
def __init__(self, row, description):
|
| 85 |
for x in range(len(row)):
|
| 86 |
setattr(self, description[x][0], row[x])
|
| 87 |
def rangeString(self):
|
| 88 |
result = []
|
| 89 |
if self.range_local:
|
| 90 |
result.append("local")
|
| 91 |
if self.range_remote:
|
| 92 |
result.append("remote")
|
| 93 |
if self.range_user_init:
|
| 94 |
result.append("user-initiated")
|
| 95 |
return ", ".join(result)
|
| 96 |
|
| 97 |
class SchemaMismatch(Exception):
|
| 98 |
"""Raised to indicate a schema mismatch.
|
| 99 |
|
| 100 |
The caller is expected to remove and regenerate the database."""
|
| 101 |
|
| 102 |
class DB:
|
| 103 |
"""Access to the security database.
|
| 104 |
|
| 105 |
This is a wrapper around an SQLite database object (which is
|
| 106 |
accessible as the "db" member.
|
| 107 |
|
| 108 |
Most operations need a special cursor object, which can be created
|
| 109 |
with a cursor object. The name "cursor" is somewhat of a
|
| 110 |
misnomer because these objects are quite versatile.
|
| 111 |
"""
|
| 112 |
|
| 113 |
def __init__(self, name, verbose=False):
|
| 114 |
self.name = name
|
| 115 |
self.db = apsw.Connection(name)
|
| 116 |
self.verbose = verbose
|
| 117 |
|
| 118 |
self.schema_version = 21
|
| 119 |
self._initFunctions()
|
| 120 |
|
| 121 |
c = self.cursor()
|
| 122 |
for (v,) in c.execute("PRAGMA user_version"):
|
| 123 |
if v == 0:
|
| 124 |
self.initSchema()
|
| 125 |
elif v == 20:
|
| 126 |
self._initSchema20()
|
| 127 |
elif v <> self.schema_version:
|
| 128 |
if self.verbose:
|
| 129 |
print "DB: schema version mismatch: expected %d, got %d" \
|
| 130 |
% (self.schema_version, v)
|
| 131 |
raise SchemaMismatch, `v`
|
| 132 |
# Database has been created at this point. Small race
|
| 133 |
# condition here (the already opened database might refer
|
| 134 |
# to an older file).
|
| 135 |
self.__stat = os.stat(self.name)
|
| 136 |
return
|
| 137 |
assert False
|
| 138 |
|
| 139 |
def __del__(self):
|
| 140 |
self.db.close()
|
| 141 |
|
| 142 |
def refresh(self):
|
| 143 |
"""Checks if the database file is still the same and reopens
|
| 144 |
it if necessary."""
|
| 145 |
current = os.stat(self.name)
|
| 146 |
if os.path.samestat(self.__stat, current):
|
| 147 |
return
|
| 148 |
self.__stat = current
|
| 149 |
self.db = apsw.Connection(self.name)
|
| 150 |
self._initFunctions()
|
| 151 |
|
| 152 |
def cursor(self):
|
| 153 |
"""Creates a new database cursor.
|
| 154 |
|
| 155 |
Also see the writeTxn method."""
|
| 156 |
return self.db.cursor()
|
| 157 |
|
| 158 |
def writeTxn(self):
|
| 159 |
"""Creates a cursor for an exclusive transaction.
|
| 160 |
|
| 161 |
No other process may modify the database at the same time.
|
| 162 |
After finishing the work, you should invoke the commit or
|
| 163 |
rollback methods below.
|
| 164 |
"""
|
| 165 |
c = self.cursor()
|
| 166 |
c.execute("BEGIN TRANSACTION EXCLUSIVE")
|
| 167 |
return c
|
| 168 |
|
| 169 |
def commit(self, cursor):
|
| 170 |
"""Makes the changes in the transaction permanent."""
|
| 171 |
cursor.execute("COMMIT")
|
| 172 |
|
| 173 |
def rollback(self, cursor):
|
| 174 |
"""Undos the changes in the transaction."""
|
| 175 |
cursor.execute("ROLLBACK")
|
| 176 |
|
| 177 |
def initSchema(self):
|
| 178 |
"""Creates the database schema."""
|
| 179 |
cursor = self.cursor()
|
| 180 |
|
| 181 |
# This gives us better performance (it's usually the file
|
| 182 |
# system block size). This must come first to be ffective.
|
| 183 |
|
| 184 |
cursor.execute("PRAGMA page_size = 4096")
|
| 185 |
|
| 186 |
# Set the schema version to an invalid value which is
|
| 187 |
# different from zero. We can use this to detect a partially
|
| 188 |
# created schema.
|
| 189 |
|
| 190 |
cursor.execute("PRAGMA user_version = 1")
|
| 191 |
|
| 192 |
cursor.execute("""CREATE TABLE inodeprints
|
| 193 |
(file TEXT NOT NULL PRIMARY KEY,
|
| 194 |
inodeprint TEXT NOT NULL,
|
| 195 |
parsed BLOB)""")
|
| 196 |
|
| 197 |
cursor.execute("""CREATE TABLE version_linear_order
|
| 198 |
(id INTEGER NOT NULL PRIMARY KEY,
|
| 199 |
version TEXT NOT NULL UNIQUE)""")
|
| 200 |
|
| 201 |
cursor.execute(
|
| 202 |
"""CREATE TABLE source_packages
|
| 203 |
(name TEXT NOT NULL,
|
| 204 |
release TEXT NOT NULL,
|
| 205 |
subrelease TEXT NOT NULL,
|
| 206 |
archive TEXT NOT NULL,
|
| 207 |
version TEXT NOT NULL,
|
| 208 |
version_id INTEGER NOT NULL DEFAULT 0,
|
| 209 |
PRIMARY KEY (name, release, subrelease, archive))""")
|
| 210 |
|
| 211 |
cursor.execute(
|
| 212 |
"""CREATE TABLE binary_packages
|
| 213 |
(name TEXT NOT NULL,
|
| 214 |
release TEXT NOT NULL,
|
| 215 |
subrelease TEXT NOT NULL,
|
| 216 |
archive TEXT NOT NULL,
|
| 217 |
version TEXT NOT NULL,
|
| 218 |
source TEXT NOT NULL,
|
| 219 |
source_version TEXT NOT NULL,
|
| 220 |
archs TEXT NOT NULL,
|
| 221 |
PRIMARY KEY (name, release, subrelease, archive, version, source,
|
| 222 |
source_version))""")
|
| 223 |
cursor.execute(
|
| 224 |
"""CREATE INDEX binary_packages_source
|
| 225 |
ON binary_packages(source)""")
|
| 226 |
|
| 227 |
cursor.execute("""CREATE TABLE package_notes
|
| 228 |
(id INTEGER NOT NULL PRIMARY KEY,
|
| 229 |
bug_name TEXT NOT NULL,
|
| 230 |
package TEXT NOT NULL,
|
| 231 |
fixed_version TEXT
|
| 232 |
CHECK (fixed_version IS NULL OR fixed_version <> ''),
|
| 233 |
fixed_version_id INTEGER NOT NULL DEFAULT 0,
|
| 234 |
release TEXT NOT NULL,
|
| 235 |
package_kind TEXT NOT NULL DEFAULT 'unknown',
|
| 236 |
urgency TEXT NOT NULL,
|
| 237 |
bug_origin TEXT NOT NULL DEFAULT '')""")
|
| 238 |
cursor.execute(
|
| 239 |
"""CREATE UNIQUE INDEX package_notes_bug
|
| 240 |
ON package_notes(bug_name, package, release)""")
|
| 241 |
cursor.execute(
|
| 242 |
"""CREATE INDEX package_notes_package
|
| 243 |
ON package_notes(package)""")
|
| 244 |
|
| 245 |
cursor.execute("""CREATE TABLE debian_bugs
|
| 246 |
(bug INTEGER NOT NULL,
|
| 247 |
note INTEGER NOT NULL,
|
| 248 |
PRIMARY KEY (bug, note))""")
|
| 249 |
|
| 250 |
cursor.execute("""CREATE TABLE bugs
|
| 251 |
(name TEXT NOT NULL PRIMARY KEY,
|
| 252 |
cve_status TEXT NOT NULL
|
| 253 |
CHECK (cve_status IN
|
| 254 |
('', 'CANDIDATE', 'ASSIGNED', 'RESERVED', 'REJECTED')),
|
| 255 |
not_for_us INTEGER NOT NULL CHECK (not_for_us IN (0, 1)),
|
| 256 |
description TEXT NOT NULL,
|
| 257 |
release_date TEXT NOT NULL,
|
| 258 |
source_file TEXT NOT NULL,
|
| 259 |
source_line INTEGER NOT NULL)""")
|
| 260 |
|
| 261 |
cursor.execute("""CREATE TABLE bugs_notes
|
| 262 |
(bug_name TEXT NOT NULL CHECK (typ <> ''),
|
| 263 |
typ TEXT NOT NULL CHECK (typ IN ('TODO', 'NOTE')),
|
| 264 |
release TEXT NOT NULL DEFAULT '',
|
| 265 |
comment TEXT NOT NULL CHECK (comment <> ''))""")
|
| 266 |
|
| 267 |
cursor.execute("""CREATE TABLE bugs_xref
|
| 268 |
(source TEXT NOT NULL,
|
| 269 |
target TEXT NOT NULL,
|
| 270 |
PRIMARY KEY (source, target))""")
|
| 271 |
cursor.execute("CREATE INDEX bugs_xref_target ON bugs_xref(target)")
|
| 272 |
|
| 273 |
cursor.execute("""CREATE TABLE bug_status
|
| 274 |
(bug_name TEXT NOT NULL,
|
| 275 |
release TEXT NOT NULL,
|
| 276 |
status TEXT NOT NULL
|
| 277 |
CHECK (status IN ('vulnerable', 'fixed', 'unknown', 'undetermined',
|
| 278 |
'partially-fixed', 'todo')),
|
| 279 |
reason TEXT NOT NULL,
|
| 280 |
PRIMARY KEY (bug_name, release))""")
|
| 281 |
|
| 282 |
cursor.execute("""CREATE TABLE source_package_status
|
| 283 |
(bug_name TEXT NOT NULL,
|
| 284 |
package INTEGER NOT NULL,
|
| 285 |
vulnerable INTEGER NOT NULL,
|
| 286 |
urgency TEXT NOT NULL,
|
| 287 |
PRIMARY KEY (bug_name, package))""")
|
| 288 |
cursor.execute(
|
| 289 |
"""CREATE INDEX source_package_status_package
|
| 290 |
ON source_package_status(package)""")
|
| 291 |
|
| 292 |
cursor.execute(
|
| 293 |
"CREATE TABLE removed_packages (name TEXT NOT NULL PRIMARY KEY)")
|
| 294 |
|
| 295 |
cursor.execute(
|
| 296 |
"""CREATE TABLE nvd_data
|
| 297 |
(cve_name TEXT NOT NULL PRIMARY KEY,
|
| 298 |
cve_desc TEXT NOT NULL,
|
| 299 |
discovered TEXT NOT NULL,
|
| 300 |
published TEXT NOT NULL,
|
| 301 |
severity TEXT NOT NULL,
|
| 302 |
range_local INTEGER,
|
| 303 |
range_remote INTEGER,
|
| 304 |
range_user_init INTEGER,
|
| 305 |
loss_avail INTEGER NOT NULL,
|
| 306 |
loss_conf INTEGER NOT NULL,
|
| 307 |
loss_int INTEGER NOT NULL,
|
| 308 |
loss_sec_prot_user INTEGER NOT NULL,
|
| 309 |
loss_sec_prot_admin INTEGER NOT NULL,
|
| 310 |
loss_sec_prot_other INTEGER NOT NULL)""")
|
| 311 |
|
| 312 |
cursor.execute(
|
| 313 |
"""CREATE TABLE debsecan_data
|
| 314 |
(name TEXT NOT NULL PRIMARY KEY,
|
| 315 |
data TEXT NOT NULL)""")
|
| 316 |
|
| 317 |
self._initNoDSA(cursor)
|
| 318 |
self._initViews(cursor)
|
| 319 |
|
| 320 |
cursor.execute("PRAGMA user_version = %d" % self.schema_version)
|
| 321 |
|
| 322 |
def _initSchema20(self):
|
| 323 |
cursor = self.db.cursor()
|
| 324 |
|
| 325 |
cursor.execute("PRAGMA user_version = 1")
|
| 326 |
self._initNoDSA(cursor)
|
| 327 |
self._initViews(cursor)
|
| 328 |
cursor.execute("DELETE FROM inodeprints WHERE file ='data/CVE/list'")
|
| 329 |
cursor.execute("PRAGMA user_version = %d" % self.schema_version)
|
| 330 |
|
| 331 |
def _initNoDSA(self, cursor):
|
| 332 |
cursor.execute(
|
| 333 |
"""CREATE TABLE package_notes_nodsa
|
| 334 |
(bug_name TEXT NOT NULL,
|
| 335 |
package TEXT NOT NULL,
|
| 336 |
release TEXT NOT NULL,
|
| 337 |
reason TEXT NOT NULL,
|
| 338 |
comment TEXT NOT NULL,
|
| 339 |
PRIMARY KEY (bug_name, package, release))
|
| 340 |
""")
|
| 341 |
|
| 342 |
def _initViews(self, cursor):
|
| 343 |
for view in ('testing_status', 'stable_status', 'oldstable_status'):
|
| 344 |
try:
|
| 345 |
cursor.execute('DROP VIEW ' + view)
|
| 346 |
except apsw.SQLError:
|
| 347 |
pass
|
| 348 |
|
| 349 |
cursor.execute(
|
| 350 |
"""CREATE VIEW testing_status AS
|
| 351 |
SELECT DISTINCT sp.name AS package, st.bug_name AS bug,
|
| 352 |
sp.archive AS section, st.urgency AS urgency,
|
| 353 |
st.vulnerable AS vulnerable,
|
| 354 |
(SELECT vulnerable
|
| 355 |
FROM source_packages AS sidp, source_package_status AS sidst
|
| 356 |
WHERE sidp.name = sp.name
|
| 357 |
AND sidp.release = 'sid' AND sidp.subrelease = ''
|
| 358 |
AND sidp.archive = sp.archive
|
| 359 |
AND sidst.bug_name = st.bug_name
|
| 360 |
AND sidst.package = sidp.rowid) AS unstable_vulnerable,
|
| 361 |
COALESCE((SELECT NOT vulnerable
|
| 362 |
FROM source_packages AS tsecp, source_package_status AS tsecst
|
| 363 |
WHERE tsecp.name = sp.name
|
| 364 |
AND tsecp.release = 'squeeze' AND tsecp.subrelease = 'security'
|
| 365 |
AND tsecp.archive = sp.archive
|
| 366 |
AND tsecst.bug_name = st.bug_name
|
| 367 |
AND tsecst.package = tsecp.rowid), 0) AS testing_security_fixed,
|
| 368 |
(SELECT range_remote FROM nvd_data
|
| 369 |
WHERE cve_name = st.bug_name) AS remote,
|
| 370 |
(EXISTS (SELECT * FROM package_notes_nodsa AS pnd
|
| 371 |
WHERE pnd.bug_name = st.bug_name
|
| 372 |
AND pnd.package = sp.name
|
| 373 |
AND pnd.release = 'squeeze')) AS no_dsa
|
| 374 |
FROM source_package_status AS st, source_packages AS sp
|
| 375 |
WHERE st.vulnerable > 0 AND sp.rowid = st.package
|
| 376 |
AND sp.release = 'squeeze' AND sp.subrelease = ''
|
| 377 |
ORDER BY sp.name, st.urgency, st.bug_name""")
|
| 378 |
|
| 379 |
for (name, nickname) in (('stable', 'lenny'), ('oldstable', 'etch'),):
|
| 380 |
cursor.execute(
|
| 381 |
"""CREATE VIEW %s_status AS
|
| 382 |
SELECT DISTINCT sp.name AS package, st.bug_name AS bug,
|
| 383 |
sp.archive AS section, st.urgency AS urgency,
|
| 384 |
st.vulnerable AS vulnerable,
|
| 385 |
(SELECT range_remote FROM nvd_data
|
| 386 |
WHERE cve_name = st.bug_name) AS remote,
|
| 387 |
(EXISTS (SELECT * FROM package_notes_nodsa AS pnd
|
| 388 |
WHERE pnd.bug_name = st.bug_name
|
| 389 |
AND pnd.package = sp.name
|
| 390 |
AND pnd.release = '%s')) AS no_dsa
|
| 391 |
FROM source_package_status AS st, source_packages AS sp
|
| 392 |
WHERE st.vulnerable > 0 AND sp.rowid = st.package
|
| 393 |
AND sp.release = '%s' AND sp.subrelease = ''
|
| 394 |
AND NOT COALESCE((SELECT NOT vulnerable
|
| 395 |
FROM source_packages AS secp, source_package_status AS secst
|
| 396 |
WHERE secp.name = sp.name
|
| 397 |
AND secp.release = '%s' AND secp.subrelease = 'security'
|
| 398 |
AND secp.archive = sp.archive
|
| 399 |
AND secst.bug_name = st.bug_name
|
| 400 |
AND secst.package = secp.rowid), 0)
|
| 401 |
ORDER BY sp.name, urgency_to_number(urgency), st.bug_name"""
|
| 402 |
% (name, nickname, nickname, nickname))
|
| 403 |
|
| 404 |
def _initFunctions(self):
|
| 405 |
"""Registers user-defined SQLite functions."""
|
| 406 |
|
| 407 |
def string_list_add(lst, *args):
|
| 408 |
for arg in args:
|
| 409 |
lst.append(arg)
|
| 410 |
def string_list_to_string(lst):
|
| 411 |
return ', '.join(lst)
|
| 412 |
def string_list_factory():
|
| 413 |
return ([], string_list_add, string_list_to_string)
|
| 414 |
self.db.createaggregatefunction("string_list", string_list_factory)
|
| 415 |
|
| 416 |
def string_set_add(lst, *args):
|
| 417 |
for arg in args:
|
| 418 |
for arch in arg.split(','):
|
| 419 |
lst[arch] = True
|
| 420 |
def string_set_to_archs(lst):
|
| 421 |
l = lst.keys()
|
| 422 |
l.sort()
|
| 423 |
return ','.join(l)
|
| 424 |
def string_set_factory():
|
| 425 |
return ({}, string_set_add, string_set_to_archs)
|
| 426 |
self.db.createaggregatefunction("string_set", string_set_factory)
|
| 427 |
|
| 428 |
urgencies = ['high', 'medium', 'low', 'unimportant']
|
| 429 |
def urgency_to_number(u):
|
| 430 |
try:
|
| 431 |
return urgencies.index(u)
|
| 432 |
except ValueError:
|
| 433 |
return 999
|
| 434 |
self.db.createscalarfunction("urgency_to_number", urgency_to_number, 1)
|
| 435 |
|
| 436 |
releases = ['potato', 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'sid']
|
| 437 |
def release_to_number(u):
|
| 438 |
try:
|
| 439 |
return releases.index(u)
|
| 440 |
except ValueError:
|
| 441 |
return -1
|
| 442 |
self.db.createscalarfunction("release_to_number", release_to_number, 1)
|
| 443 |
|
| 444 |
def release_name(release, subrelease, archive):
|
| 445 |
if archive <> 'main':
|
| 446 |
release = release + '/' + archive
|
| 447 |
if subrelease:
|
| 448 |
return "%s (%s)" % (release, subrelease)
|
| 449 |
else:
|
| 450 |
return release
|
| 451 |
self.db.createscalarfunction("release_name", release_name, 3)
|
| 452 |
|
| 453 |
self.db.createcollation("version", debian_support.version_compare)
|
| 454 |
|
| 455 |
def filePrint(self, filename):
|
| 456 |
"""Returns a fingerprint string for filename."""
|
| 457 |
|
| 458 |
st = os.stat(filename)
|
| 459 |
# The "1" is a version number which can be used to trigger a
|
| 460 |
# re-read if the code has changed in an incompatible way.
|
| 461 |
return `(st.st_size, st.st_ino, st.st_mtime, 1)`
|
| 462 |
|
| 463 |
def _parseFile(self, cursor, filename):
|
| 464 |
current_print = self.filePrint(filename)
|
| 465 |
|
| 466 |
def do_parse(packages):
|
| 467 |
if self.verbose:
|
| 468 |
print " reading " + `filename`
|
| 469 |
|
| 470 |
re_source = re.compile\
|
| 471 |
(r'^([a-zA-Z0-9.+-]+)(?:\s+\(([a-zA-Z0-9.+:~-]+)\))?$')
|
| 472 |
|
| 473 |
data = {}
|
| 474 |
for pkg in packages:
|
| 475 |
pkg_name = None
|
| 476 |
pkg_version = None
|
| 477 |
pkg_arch = None
|
| 478 |
pkg_source = None
|
| 479 |
pkg_source_version = None
|
| 480 |
for (name, contents) in pkg:
|
| 481 |
if name == "Package":
|
| 482 |
pkg_name = contents
|
| 483 |
elif name == "Version":
|
| 484 |
pkg_version = contents
|
| 485 |
elif name == "Source":
|
| 486 |
match = re_source.match(contents)
|
| 487 |
if match is None:
|
| 488 |
raise SyntaxError(('package %s references '
|
| 489 |
+ 'invalid source package %s') %
|
| 490 |
(pkg_name, `contents`))
|
| 491 |
(pkg_source, pkg_source_version) = match.groups()
|
| 492 |
elif name == "Architecture":
|
| 493 |
pkg_arch = contents
|
| 494 |
if pkg_name is None:
|
| 495 |
raise SyntaxError\
|
| 496 |
("package record does not contain package name")
|
| 497 |
if pkg_version is None:
|
| 498 |
raise SyntaxError\
|
| 499 |
("package record for %s does not contain version"
|
| 500 |
% pkg_name)
|
| 501 |
if pkg_arch is None:
|
| 502 |
raise SyntaxError\
|
| 503 |
("package record for %s lacks Architecture: field"
|
| 504 |
% pkg_name)
|
| 505 |
if pkg_name in data:
|
| 506 |
oversion = debian_support.Version(data[pkg_name][0])
|
| 507 |
if oversion < debian_support.Version(pkg_version):
|
| 508 |
data[pkg_name] = (pkg_version, pkg_arch,
|
| 509 |
pkg_source, pkg_source_version)
|
| 510 |
else:
|
| 511 |
data[pkg_name] = (pkg_version, pkg_arch,
|
| 512 |
pkg_source, pkg_source_version)
|
| 513 |
|
| 514 |
return data
|
| 515 |
|
| 516 |
def toString(data):
|
| 517 |
result = cStringIO.StringIO()
|
| 518 |
cPickle.dump(data, result)
|
| 519 |
return buffer(result.getvalue())
|
| 520 |
|
| 521 |
for (old_print, contents) in cursor.execute(
|
| 522 |
"SELECT inodeprint, parsed FROM inodeprints WHERE file = ?",
|
| 523 |
(filename,)):
|
| 524 |
if old_print == current_print:
|
| 525 |
return (True, cPickle.load(cStringIO.StringIO(contents)))
|
| 526 |
result = do_parse(debian_support.PackageFile(filename))
|
| 527 |
cursor.execute("""UPDATE inodeprints SET inodeprint = ?, parsed = ?
|
| 528 |
WHERE file = ?""", (current_print, toString(result), filename))
|
| 529 |
return (False, result)
|
| 530 |
|
| 531 |
# No inodeprints entry, load file and add one.
|
| 532 |
result = do_parse(debian_support.PackageFile(filename))
|
| 533 |
cursor.execute("""INSERT INTO inodeprints (file, inodeprint, parsed)
|
| 534 |
VALUES (?, ?, ?)""", (filename, current_print, toString(result)))
|
| 535 |
return (False, result)
|
| 536 |
|
| 537 |
def readPackages(self, cursor, directory):
|
| 538 |
"""Reads a directory of package files."""
|
| 539 |
|
| 540 |
if self.verbose:
|
| 541 |
print "readPackages:"
|
| 542 |
|
| 543 |
self._readSourcePackages(cursor, directory)
|
| 544 |
self._readBinaryPackages(cursor, directory)
|
| 545 |
|
| 546 |
if self.verbose:
|
| 547 |
print " finished"
|
| 548 |
|
| 549 |
def _readSourcePackages(self, cursor, directory):
|
| 550 |
"""Reads from directory with source package files."""
|
| 551 |
|
| 552 |
re_sources = re.compile(r'.*/([a-z-]+)_([a-z-]*)_([a-z-]+)_Sources$')
|
| 553 |
|
| 554 |
if self.verbose:
|
| 555 |
print " reading source packages"
|
| 556 |
|
| 557 |
for filename in glob.glob(directory + '/*_Sources'):
|
| 558 |
match = re_sources.match(filename)
|
| 559 |
if match is None:
|
| 560 |
raise ValueError, "invalid file name: " + `filename`
|
| 561 |
|
| 562 |
(release, subrelease, archive) = match.groups()
|
| 563 |
(unchanged, parsed) = self._parseFile(cursor, filename)
|
| 564 |
if unchanged:
|
| 565 |
continue
|
| 566 |
|
| 567 |
cursor.execute(
|
| 568 |
"""DELETE FROM source_packages
|
| 569 |
WHERE release = ? AND subrelease = ? AND archive = ?""",
|
| 570 |
(release, subrelease, archive))
|
| 571 |
self._clearVersions(cursor)
|
| 572 |
|
| 573 |
def gen():
|
| 574 |
for name in parsed.keys():
|
| 575 |
(version, archs, source, source_version) = parsed[name]
|
| 576 |
assert source is None
|
| 577 |
assert source_version is None
|
| 578 |
yield name, release, subrelease, archive, version
|
| 579 |
cursor.executemany(
|
| 580 |
"""INSERT INTO source_packages
|
| 581 |
(name, release, subrelease, archive, version)
|
| 582 |
VALUES (?, ?, ?, ?, ?)""",
|
| 583 |
gen())
|
| 584 |
|
| 585 |
def _readBinaryPackages(self, cursor, directory):
|
| 586 |
"""Reads from a directory with binary package files."""
|
| 587 |
|
| 588 |
re_packages \
|
| 589 |
= re.compile(
|
| 590 |
r'.*/([a-z-]+)_([a-z-]*)_([a-z-]+)_([a-z0-9-]+)_Packages$')
|
| 591 |
|
| 592 |
if self.verbose:
|
| 593 |
print " reading binary packages"
|
| 594 |
|
| 595 |
# First check for any changes.
|
| 596 |
|
| 597 |
filenames = glob.glob(directory + '/*_Packages')
|
| 598 |
filenames.sort()
|
| 599 |
changed = False
|
| 600 |
for filename in filenames:
|
| 601 |
changed = True
|
| 602 |
for (old_print,) in cursor.execute(
|
| 603 |
"SELECT inodeprint FROM inodeprints WHERE file = ?",
|
| 604 |
(filename,)):
|
| 605 |
if self.filePrint(filename) == old_print:
|
| 606 |
changed = False
|
| 607 |
if changed:
|
| 608 |
break
|
| 609 |
if not changed:
|
| 610 |
if self.verbose:
|
| 611 |
print " finished (no changes)"
|
| 612 |
return
|
| 613 |
|
| 614 |
# Real import. We have to re-read all Packages files even if
|
| 615 |
# only some of them have changed because the database only
|
| 616 |
# stores aggregated data, and there is no efficient way to
|
| 617 |
# handle updates of the records related to a single file.
|
| 618 |
|
| 619 |
packages = {}
|
| 620 |
unchanged = True
|
| 621 |
for filename in filenames:
|
| 622 |
match = re_packages.match(filename)
|
| 623 |
if match is None:
|
| 624 |
raise ValueError, "invalid file name: " + `filename`
|
| 625 |
|
| 626 |
(release, subrelease, archive, architecture) = match.groups()
|
| 627 |
(unch, parsed) = self._parseFile(cursor, filename)
|
| 628 |
unchanged = unchanged and unch
|
| 629 |
for name in parsed.keys():
|
| 630 |
(version, arch, source, source_version) = parsed[name]
|
| 631 |
if source is None:
|
| 632 |
source = name
|
| 633 |
if source_version is None:
|
| 634 |
source_version = version
|
| 635 |
if arch <> 'all' and arch <> architecture:
|
| 636 |
raise ValueError, ("invalid architecture %s for package %s"
|
| 637 |
% (arch, name))
|
| 638 |
key = (name, release, subrelease, archive, version,
|
| 639 |
source, source_version)
|
| 640 |
if packages.has_key(key):
|
| 641 |
packages[key][arch] = 1
|
| 642 |
else:
|
| 643 |
packages[key] = {arch : 1}
|
| 644 |
|
| 645 |
if unchanged:
|
| 646 |
if self.verbose:
|
| 647 |
print " finished (no changes)"
|
| 648 |
return
|
| 649 |
|
| 650 |
if self.verbose:
|
| 651 |
print " deleting old data"
|
| 652 |
cursor.execute("DELETE FROM binary_packages")
|
| 653 |
self._clearVersions(cursor)
|
| 654 |
|
| 655 |
l = packages.keys()
|
| 656 |
|
| 657 |
if len(l) == 0:
|
| 658 |
raise ValueError, "no binary packages found"
|
| 659 |
|
| 660 |
l.sort()
|
| 661 |
def gen():
|
| 662 |
for key in l:
|
| 663 |
archs = packages[key].keys()
|
| 664 |
archs.sort()
|
| 665 |
archs = ','.join(archs)
|
| 666 |
yield key + (archs,)
|
| 667 |
|
| 668 |
if self.verbose:
|
| 669 |
print " storing binary package data"
|
| 670 |
|
| 671 |
cursor.executemany(
|
| 672 |
"""INSERT INTO binary_packages
|
| 673 |
(name, release, subrelease, archive, version,
|
| 674 |
source, source_version, archs)
|
| 675 |
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
| 676 |
gen())
|
| 677 |
|
| 678 |
def readBugs(self, cursor, path):
|
| 679 |
if self.verbose:
|
| 680 |
print "readBugs:"
|
| 681 |
|
| 682 |
def clear_db(cleared=[False]):
|
| 683 |
# Avoid clearing the database multiple times.
|
| 684 |
if cleared[0]:
|
| 685 |
return
|
| 686 |
else:
|
| 687 |
cleared[0] = True
|
| 688 |
|
| 689 |
cursor.execute("DELETE FROM debian_bugs")
|
| 690 |
cursor.execute("DELETE FROM bugs")
|
| 691 |
cursor.execute("DELETE FROM package_notes")
|
| 692 |
cursor.execute("DELETE FROM bugs_notes")
|
| 693 |
cursor.execute("DELETE FROM bugs_xref")
|
| 694 |
cursor.execute("DELETE FROM package_notes_nodsa")
|
| 695 |
cursor.execute("DELETE FROM removed_packages")
|
| 696 |
|
| 697 |
# The *_status tables are regenerated anyway, no need to
|
| 698 |
# delete them here.
|
| 699 |
|
| 700 |
self._clearVersions(cursor)
|
| 701 |
|
| 702 |
def do_parse(source, cleared=[False]):
|
| 703 |
errors = []
|
| 704 |
|
| 705 |
clear_db()
|
| 706 |
|
| 707 |
if self.verbose:
|
| 708 |
print " reading " + `source.name`
|
| 709 |
|
| 710 |
for bug in source:
|
| 711 |
try:
|
| 712 |
bug.writeDB(cursor)
|
| 713 |
except ValueError, e:
|
| 714 |
errors.append("%s: %d: error: %s"
|
| 715 |
% (bug.source_file, bug.source_line, e))
|
| 716 |
if errors:
|
| 717 |
raise InsertError(errors)
|
| 718 |
|
| 719 |
cursor.executemany(
|
| 720 |
"INSERT OR IGNORE INTO removed_packages (name) VALUES (?)",
|
| 721 |
map(lambda x: (x,), source.removed_packages.keys()))
|
| 722 |
|
| 723 |
def has_changed(filename):
|
| 724 |
current_print = self.filePrint(filename)
|
| 725 |
for (old_print,) in cursor.execute(
|
| 726 |
"SELECT inodeprint FROM inodeprints WHERE file = ?",
|
| 727 |
(filename,)):
|
| 728 |
if old_print == current_print:
|
| 729 |
return False
|
| 730 |
else:
|
| 731 |
return True
|
| 732 |
return True
|
| 733 |
|
| 734 |
source_removed_packages = '/packages/removed-packages'
|
| 735 |
sources = ((bugs.CVEFile, '/CVE/list'),
|
| 736 |
(bugs.DSAFile, '/DSA/list'),
|
| 737 |
(bugs.DTSAFile, '/DTSA/list'),
|
| 738 |
(None, source_removed_packages))
|
| 739 |
|
| 740 |
unchanged = True
|
| 741 |
for (_, name) in sources:
|
| 742 |
if has_changed(path + name):
|
| 743 |
unchanged = False
|
| 744 |
break
|
| 745 |
if unchanged:
|
| 746 |
if self.verbose:
|
| 747 |
print " finished (no changes)"
|
| 748 |
return
|
| 749 |
|
| 750 |
clear_db()
|
| 751 |
|
| 752 |
def read_one(source):
|
| 753 |
filename = source.name
|
| 754 |
current_print = self.filePrint(filename)
|
| 755 |
|
| 756 |
do_parse(source)
|
| 757 |
cursor.execute(
|
| 758 |
"""INSERT OR REPLACE INTO inodeprints (inodeprint, file)
|
| 759 |
VALUES (?, ?)""", (current_print, filename))
|
| 760 |
|
| 761 |
for (cls, name) in sources:
|
| 762 |
if cls is None:
|
| 763 |
continue
|
| 764 |
read_one(cls(path + name))
|
| 765 |
|
| 766 |
if self.verbose:
|
| 767 |
print " update removed packages"
|
| 768 |
self.readRemovedPackages(cursor, path + source_removed_packages)
|
| 769 |
|
| 770 |
errors = []
|
| 771 |
|
| 772 |
if self.verbose:
|
| 773 |
print " check cross-references"
|
| 774 |
|
| 775 |
for (bug,) in cursor.execute(
|
| 776 |
"""SELECT DISTINCT target FROM bugs_xref
|
| 777 |
EXCEPT SELECT name FROM bugs"""):
|
| 778 |
if bug[0:3] == "VU#":
|
| 779 |
continue
|
| 780 |
errors.append("reference to unknown bug " + bug)
|
| 781 |
|
| 782 |
if self.verbose:
|
| 783 |
print " copy notes"
|
| 784 |
|
| 785 |
# Copy notes from DSA/DTSA to CVE.
|
| 786 |
|
| 787 |
old_source = ''
|
| 788 |
for source, target in list(cursor.execute(
|
| 789 |
"""SELECT source, target FROM bugs_xref
|
| 790 |
WHERE (source LIKE 'DTSA-%' OR source LIKE 'DSA-%')
|
| 791 |
AND target LIKE 'CVE-%'""")):
|
| 792 |
if source <> old_source:
|
| 793 |
source_bug = bugs.BugFromDB(cursor, source)
|
| 794 |
old_source = source
|
| 795 |
for n in source_bug.notes:
|
| 796 |
# We do not copy recursively.
|
| 797 |
assert not n.bug_origin
|
| 798 |
|
| 799 |
if n.release:
|
| 800 |
rel = str(n.release)
|
| 801 |
else:
|
| 802 |
rel = ''
|
| 803 |
present = False
|
| 804 |
|
| 805 |
for (version, note_id) in list(cursor.execute(
|
| 806 |
"""SELECT fixed_version, id
|
| 807 |
FROM package_notes
|
| 808 |
WHERE bug_name = ? AND package = ? AND release = ?""",
|
| 809 |
(target, n.package, rel))):
|
| 810 |
if version is None:
|
| 811 |
# The target is marked as unfixed. Our
|
| 812 |
# version cannot win.
|
| 813 |
present = True
|
| 814 |
continue
|
| 815 |
|
| 816 |
if (n.fixed_version is None
|
| 817 |
or n.fixed_version > debian_support.Version(version)):
|
| 818 |
# If our version is larger, it is the definitive one.
|
| 819 |
# Remove the existing entry in this case.
|
| 820 |
cursor.execute(
|
| 821 |
"DELETE FROM debian_bugs WHERE note = ?",
|
| 822 |
(note_id,))
|
| 823 |
cursor.execute(
|
| 824 |
"""DELETE FROM package_notes
|
| 825 |
WHERE bug_name = ? AND package = ?
|
| 826 |
AND release = ?""",
|
| 827 |
(target, n.package, rel))
|
| 828 |
else:
|
| 829 |
present = True
|
| 830 |
if not present:
|
| 831 |
n.writeDB(cursor, target, bug_origin=source)
|
| 832 |
|
| 833 |
if errors:
|
| 834 |
raise InsertError(errors)
|
| 835 |
|
| 836 |
if self.verbose:
|
| 837 |
print " finished"
|
| 838 |
|
| 839 |
def availableReleases(self, cursor=None):
|
| 840 |
"""Returns a list of tuples (RELEASE, ARCHIVE,
|
| 841 |
SOURCES-PRESENT, ARCHITECTURE-LIST)."""
|
| 842 |
if cursor is None:
|
| 843 |
cursor = self.cursor()
|
| 844 |
|
| 845 |
releases = {}
|
| 846 |
for r in cursor.execute(
|
| 847 |
"""SELECT DISTINCT release, subrelease, archive
|
| 848 |
FROM source_packages"""):
|
| 849 |
releases[r] = (True, [])
|
| 850 |
|
| 851 |
for (rel, subrel, archive, archs) in cursor.execute(
|
| 852 |
"""SELECT DISTINCT release, subrelease, archive, archs
|
| 853 |
FROM binary_packages"""):
|
| 854 |
key = (rel, subrel, archive)
|
| 855 |
if not releases.has_key(key):
|
| 856 |
releases[key] = (False, [])
|
| 857 |
releases[key][1][:] = mergeLists(releases[key][1], archs)
|
| 858 |
|
| 859 |
result = []
|
| 860 |
for ((rel, subrel, archive), (sources, archs)) in releases.items():
|
| 861 |
result.append((rel, subrel, archive, sources, archs))
|
| 862 |
result.sort()
|
| 863 |
|
| 864 |
return result
|
| 865 |
|
| 866 |
def getFunnyPackageVersions(self):
|
| 867 |
"""Returns a list of (PACKAGE, RELEASE, ARCHIVE, VERSION,
|
| 868 |
SOURCE-VERSION) tuples such that PACKAGE is both a source and
|
| 869 |
binary package, but the associated version numbers are
|
| 870 |
different."""
|
| 871 |
|
| 872 |
return list(self.db.cursor().execute(
|
| 873 |
"""SELECT DISTINCT name, release, archive, version, source_version
|
| 874 |
FROM binary_packages
|
| 875 |
WHERE name = source AND version <> source_version
|
| 876 |
ORDER BY name, release, archive"""))
|
| 877 |
|
| 878 |
def _clearVersions(self, cursor):
|
| 879 |
cursor.execute("DELETE FROM version_linear_order")
|
| 880 |
|
| 881 |
def _updateVersions(self, cursor):
|
| 882 |
"""Updates the linear version table."""
|
| 883 |
|
| 884 |
if self.verbose:
|
| 885 |
print "updateVersions:"
|
| 886 |
|
| 887 |
for x in cursor.execute("SELECT * FROM version_linear_order LIMIT 1"):
|
| 888 |
if self.verbose:
|
| 889 |
print " finished (no changes)"
|
| 890 |
return
|
| 891 |
|
| 892 |
if self.verbose:
|
| 893 |
print " reading"
|
| 894 |
|
| 895 |
versions = []
|
| 896 |
for (v,) in cursor.execute(
|
| 897 |
"""SELECT DISTINCT *
|
| 898 |
FROM (SELECT fixed_version FROM package_notes
|
| 899 |
WHERE fixed_version IS NOT NULL
|
| 900 |
UNION ALL SELECT version FROM source_packages)"""):
|
| 901 |
versions.append(debian_support.Version(v))
|
| 902 |
|
| 903 |
if self.verbose:
|
| 904 |
print " calculating linear order"
|
| 905 |
versions.sort()
|
| 906 |
|
| 907 |
if self.verbose:
|
| 908 |
print " storing linear order"
|
| 909 |
for v in versions:
|
| 910 |
cursor.execute(
|
| 911 |
"INSERT INTO version_linear_order (version) VALUES (?)",
|
| 912 |
(str(v),))
|
| 913 |
|
| 914 |
if self.verbose:
|
| 915 |
print " updating package notes"
|
| 916 |
cursor.execute(
|
| 917 |
"""UPDATE package_notes
|
| 918 |
SET fixed_version_id = (SELECT id FROM version_linear_order
|
| 919 |
WHERE version = package_notes.fixed_version)
|
| 920 |
WHERE fixed_version IS NOT NULL""")
|
| 921 |
|
| 922 |
if self.verbose:
|
| 923 |
print " updating source packages"
|
| 924 |
cursor.execute(
|
| 925 |
"""UPDATE source_packages
|
| 926 |
SET version_id = (SELECT id FROM version_linear_order
|
| 927 |
WHERE version = source_packages.version)""")
|
| 928 |
|
| 929 |
if self.verbose:
|
| 930 |
print " finished"
|
| 931 |
|
| 932 |
def calculateVulnerabilities(self, cursor):
|
| 933 |
"""Calculate vulnerable packages.
|
| 934 |
|
| 935 |
To each package note, a release-specific vulnerability status
|
| 936 |
is attached. Currently, only squeeze/testing is processed.
|
| 937 |
|
| 938 |
Returns a list strings describing inconsistencies.
|
| 939 |
"""
|
| 940 |
|
| 941 |
result = []
|
| 942 |
|
| 943 |
self._updateVersions(cursor)
|
| 944 |
|
| 945 |
if self.verbose:
|
| 946 |
print "calculateVulnerabilities:"
|
| 947 |
print " checking version consistency in package notes"
|
| 948 |
|
| 949 |
# The following does not work because stable->security ->
|
| 950 |
# testing -> unstable propagation is no longer available.
|
| 951 |
if False:
|
| 952 |
# Ignore squeeze/testing because stable issues may be
|
| 953 |
# fast-tracked into testing, bypassing unstable.
|
| 954 |
for (bug_name, pkg_name, rel, unstable_ver, rel_ver) \
|
| 955 |
in list(cursor.execute(
|
| 956 |
"""SELECT a.bug_name, a.package, b.release,
|
| 957 |
a.fixed_version, b.fixed_version
|
| 958 |
FROM package_notes a, package_notes b
|
| 959 |
WHERE a.bug_name = b.bug_name AND a.package = b.package
|
| 960 |
AND a.release = '' AND b.release NOT IN ('', 'squeeze')
|
| 961 |
AND a.fixed_version IS NOT NULL
|
| 962 |
AND a.fixed_version_id < b.fixed_version_id""")):
|
| 963 |
b = bugs.BugFromDB(cursor, bug_name)
|
| 964 |
result.append("%s:%d: inconsistent versions for package %s"
|
| 965 |
% (b.source_file, b.source_line, pkg_name))
|
| 966 |
result.append("%s:%d: unstable: %s"
|
| 967 |
% (b.source_file, b.source_line, unstable_ver))
|
| 968 |
result.append("%s:%d: release %s: %s"
|
| 969 |
% (b.source_file, b.source_line, `rel`, rel_ver))
|
| 970 |
|
| 971 |
if self.verbose:
|
| 972 |
print " checking source packages"
|
| 973 |
cursor.execute(
|
| 974 |
"""UPDATE package_notes SET package_kind = 'unknown'
|
| 975 |
WHERE package_kind IN ('source', 'binary')""")
|
| 976 |
cursor.execute(
|
| 977 |
"""UPDATE package_notes SET package_kind = 'source'
|
| 978 |
WHERE package_kind = 'unknown'
|
| 979 |
AND EXISTS (SELECT * FROM source_packages AS p
|
| 980 |
WHERE p.name = package_notes.package)""")
|
| 981 |
cursor.execute(
|
| 982 |
"""UPDATE package_notes SET package_kind = 'source'
|
| 983 |
WHERE package_kind = 'unknown'
|
| 984 |
AND EXISTS (SELECT * FROM removed_packages AS p
|
| 985 |
WHERE p.name = package_notes.package)""")
|
| 986 |
|
| 987 |
for (bug_name, package) in list(cursor.execute(
|
| 988 |
"""SELECT n.bug_name, n.package
|
| 989 |
FROM package_notes AS n
|
| 990 |
WHERE n.package_kind = 'itp'
|
| 991 |
AND ((EXISTS (SELECT * FROM source_packages
|
| 992 |
WHERE name = n.package))
|
| 993 |
OR (EXISTS (SELECT * FROM binary_packages
|
| 994 |
WHERE name = n.package)))""")):
|
| 995 |
b = bugs.BugFromDB(cursor, bug_name)
|
| 996 |
result.append("%s:%d: ITPed package %s is in the archive"
|
| 997 |
% (b.source_file, b.source_line, package))
|
| 998 |
|
| 999 |
if result:
|
| 1000 |
return result
|
| 1001 |
|
| 1002 |
if self.verbose:
|
| 1003 |
print " remove old status"
|
| 1004 |
cursor.execute("DELETE FROM source_package_status")
|
| 1005 |
cursor.execute("DELETE FROM bug_status")
|
| 1006 |
|
| 1007 |
if self.verbose:
|
| 1008 |
print " calculate package status"
|
| 1009 |
print " source packages (unqualified)"
|
| 1010 |
|
| 1011 |
cursor.execute(
|
| 1012 |
"""INSERT INTO source_package_status
|
| 1013 |
SELECT n.bug_name, p.rowid,
|
| 1014 |
CASE WHEN n.fixed_version == 'undetermined' THEN 2
|
| 1015 |
ELSE CASE WHEN n.fixed_version IS NULL THEN 1
|
| 1016 |
ELSE CASE WHEN p.version_id < n.fixed_version_id THEN 1
|
| 1017 |
ELSE 0 END END END,
|
| 1018 |
n.urgency
|
| 1019 |
FROM package_notes AS n, source_packages AS p
|
| 1020 |
WHERE n.release = '' AND p.name = n.package""")
|
| 1021 |
|
| 1022 |
# Release annotations always override previous results,
|
| 1023 |
# therefore we use INSERT OR REPLACE.
|
| 1024 |
|
| 1025 |
if self.verbose:
|
| 1026 |
print " source packages (qualified)"
|
| 1027 |
cursor.execute(
|
| 1028 |
"""INSERT OR REPLACE INTO source_package_status
|
| 1029 |
SELECT n.bug_name, p.rowid,
|
| 1030 |
CASE WHEN n.fixed_version == 'undetermined' THEN 2
|
| 1031 |
ELSE CASE WHEN n.fixed_version IS NULL THEN 1
|
| 1032 |
ELSE CASE WHEN p.version_id < n.fixed_version_id THEN 1
|
| 1033 |
ELSE 0 END END END,
|
| 1034 |
n.urgency
|
| 1035 |
FROM package_notes AS n, source_packages AS p
|
| 1036 |
WHERE p.name = n.package
|
| 1037 |
AND p.release = n.release""")
|
| 1038 |
|
| 1039 |
# assign nvd urgencies to those that have not yet been assigned
|
| 1040 |
if self.verbose:
|
| 1041 |
print " insert nvd urgencies"
|
| 1042 |
cursor.execute(
|
| 1043 |
"""REPLACE INTO source_package_status
|
| 1044 |
SELECT s.bug_name, s.package, s.vulnerable,
|
| 1045 |
CASE WHEN n.severity == 'Medium' THEN 'medium**'
|
| 1046 |
ELSE CASE WHEN n.severity == 'High' THEN 'high**'
|
| 1047 |
ELSE CASE WHEN n.severity == 'Low' THEN 'low**'
|
| 1048 |
ELSE 'not yet assigned' END END END
|
| 1049 |
FROM nvd_data AS n, source_package_status AS s
|
| 1050 |
WHERE s.bug_name == n.cve_name
|
| 1051 |
AND s.urgency == 'not yet assigned'""")
|
| 1052 |
cursor.execute(
|
| 1053 |
"""REPLACE INTO package_notes
|
| 1054 |
SELECT p.id, p.bug_name, p.package, p.fixed_version,
|
| 1055 |
p.fixed_version_id, p.release, p.package_kind,
|
| 1056 |
CASE WHEN n.severity == 'Medium' THEN 'medium'
|
| 1057 |
ELSE CASE WHEN n.severity == 'High' THEN 'high'
|
| 1058 |
ELSE CASE WHEN n.severity == 'Low' THEN 'low'
|
| 1059 |
ELSE 'not yet assigned' END END END,
|
| 1060 |
p.bug_origin
|
| 1061 |
FROM nvd_data AS n, package_notes AS p
|
| 1062 |
WHERE p.bug_name == n.cve_name
|
| 1063 |
AND p.urgency == 'not yet assigned'""")
|
| 1064 |
|
| 1065 |
# Calculate the release-specific bug status.
|
| 1066 |
|
| 1067 |
if self.verbose:
|
| 1068 |
print " calculate release status"
|
| 1069 |
|
| 1070 |
c = self.cursor()
|
| 1071 |
|
| 1072 |
for (bug_name,) in cursor.execute(
|
| 1073 |
"SELECT name FROM bugs WHERE NOT not_for_us"):
|
| 1074 |
|
| 1075 |
self._calcUnstable(c, bug_name)
|
| 1076 |
self._calcTesting(c, bug_name, 'testing', 'squeeze')
|
| 1077 |
self._calcTesting(c, bug_name, 'stable', 'lenny')
|
| 1078 |
self._calcTesting(c, bug_name, 'oldstable', 'etch')
|
| 1079 |
|
| 1080 |
return result
|
| 1081 |
|
| 1082 |
def _calcUnstable(self, cursor, bug_name):
|
| 1083 |
"""Update bug_status with bug_name for unstable."""
|
| 1084 |
|
| 1085 |
vulnerable_packages = []
|
| 1086 |
undetermined_packages = []
|
| 1087 |
unimportant_packages = []
|
| 1088 |
have_something = False
|
| 1089 |
for (package, vulnerable, urgency) in cursor.execute(
|
| 1090 |
"""SELECT DISTINCT sp.name, st.vulnerable, n.urgency
|
| 1091 |
FROM source_package_status AS st,
|
| 1092 |
source_packages AS sp, package_notes AS n
|
| 1093 |
WHERE st.bug_name = ? AND sp.rowid = st.package
|
| 1094 |
AND sp.release = 'sid'
|
| 1095 |
AND n.bug_name = st.bug_name AND n.package = sp.name
|
| 1096 |
ORDER BY sp.name""",
|
| 1097 |
(bug_name,)):
|
| 1098 |
have_something = True
|
| 1099 |
if vulnerable == 1:
|
| 1100 |
if urgency == 'unimportant':
|
| 1101 |
unimportant_packages.append( package )
|
| 1102 |
else:
|
| 1103 |
vulnerable_packages.append(package)
|
| 1104 |
elif vulnerable == 2:
|
| 1105 |
undetermined_packages.append(package)
|
| 1106 |
|
| 1107 |
if vulnerable_packages or undetermined_packages:
|
| 1108 |
pkgs = ""
|
| 1109 |
status = 'undetermined'
|
| 1110 |
if vulnerable_packages:
|
| 1111 |
status = 'vulnerable'
|
| 1112 |
if len(vulnerable_packages) == 1:
|
| 1113 |
pkgs += "package %s is vulnerable. " % vulnerable_packages[0]
|
| 1114 |
else:
|
| 1115 |
pkgs += ("packages %s are vulnerable. "
|
| 1116 |
% ', '.join(vulnerable_packages))
|
| 1117 |
if undetermined_packages:
|
| 1118 |
if len(undetermined_packages) == 1:
|
| 1119 |
pkgs += "package %s may be vulnerable but needs to be checked." % undetermined_packages[0]
|
| 1120 |
else:
|
| 1121 |
pkgs += ("packages %s may be vulnerable but need to be checked."
|
| 1122 |
% ', '.join(undetermined_packages))
|
| 1123 |
cursor.execute("""INSERT INTO bug_status
|
| 1124 |
(bug_name, release, status, reason)
|
| 1125 |
VALUES (?, 'unstable', ?, ?)""", (bug_name, status, pkgs))
|
| 1126 |
elif unimportant_packages:
|
| 1127 |
if len(unimportant_packages) == 1:
|
| 1128 |
pkgs = "package %s is vulnerable; however, the security impact is unimportant." % unimportant_packages[0]
|
| 1129 |
else:
|
| 1130 |
pkgs = "packages %s are vulnerable; however, the security impact is unimportant." % (', '.join(unimportant_packages))
|
| 1131 |
cursor.execute("""INSERT INTO bug_status
|
| 1132 |
(bug_name, release, status, reason)
|
| 1133 |
VALUES (?, 'unstable', 'fixed', ?)""", (bug_name, pkgs))
|
| 1134 |
else:
|
| 1135 |
if have_something:
|
| 1136 |
status = "not vulnerable."
|
| 1137 |
else:
|
| 1138 |
status = "not known to be vulnerable."
|
| 1139 |
cursor.execute("""INSERT INTO bug_status
|
| 1140 |
(bug_name, release, status, reason)
|
| 1141 |
VALUES (?, 'unstable', 'fixed', ?)""",
|
| 1142 |
(bug_name, status))
|
| 1143 |
|
| 1144 |
def _calcTesting(self, cursor, bug_name, suite, nickname):
|
| 1145 |
"""Update bug_status with bug_name for testing/stable."""
|
| 1146 |
|
| 1147 |
# Note that there is at most one source package per
|
| 1148 |
# note/release/subrelease triple, but we should check that
|
| 1149 |
# here.
|
| 1150 |
|
| 1151 |
status = {'' : {}, 'security' : {}}
|
| 1152 |
for (package, note, subrelease, vulnerable, urgency) in cursor.execute(
|
| 1153 |
"""SELECT DISTINCT sp.name, n.id, sp.subrelease,
|
| 1154 |
st.vulnerable, n.urgency
|
| 1155 |
FROM source_package_status AS st,
|
| 1156 |
source_packages AS sp, package_notes AS n
|
| 1157 |
WHERE st.bug_name = ? AND sp.rowid = st.package
|
| 1158 |
AND sp.release = ? AND sp.subrelease IN ('', 'security')
|
| 1159 |
AND n.bug_name = st.bug_name AND n.package = sp.name
|
| 1160 |
ORDER BY sp.name""",
|
| 1161 |
(bug_name, nickname)):
|
| 1162 |
status[subrelease][(package, note)] = (vulnerable,urgency)
|
| 1163 |
|
| 1164 |
# Check if any packages in plain testing are vulnerable, and
|
| 1165 |
# if all of those have been fixed in the security archive.
|
| 1166 |
fixed_in_security = True
|
| 1167 |
unfixed_pkgs = {}
|
| 1168 |
undet_pkgs = {}
|
| 1169 |
unimp_pkgs = {}
|
| 1170 |
for ((package, note), (vulnerable, urgency)) in status[''].items():
|
| 1171 |
if vulnerable == 1:
|
| 1172 |
if urgency == 'unimportant':
|
| 1173 |
unimp_pkgs[package] = True
|
| 1174 |
else:
|
| 1175 |
unfixed_pkgs[package] = True
|
| 1176 |
if status['security'].get((package, note), True):
|
| 1177 |
fixed_in_security = False
|
| 1178 |
elif vulnerable == 2:
|
| 1179 |
undet_pkgs[package] = True
|
| 1180 |
|
| 1181 |
unfixed_pkgs = unfixed_pkgs.keys()
|
| 1182 |
unfixed_pkgs.sort()
|
| 1183 |
undet_pkgs = undet_pkgs.keys()
|
| 1184 |
undet_pkgs.sort()
|
| 1185 |
unimp_pkgs = unimp_pkgs.keys()
|
| 1186 |
unimp_pkgs.sort()
|
| 1187 |
|
| 1188 |
pkgs = ""
|
| 1189 |
result = "undetermined"
|
| 1190 |
if len(unfixed_pkgs) == 0 and len(undet_pkgs) == 0:
|
| 1191 |
if len(status[''].keys()) == 0:
|
| 1192 |
pkgs += "not known to be vulnerable."
|
| 1193 |
else:
|
| 1194 |
pkgs += "not vulnerable."
|
| 1195 |
result = "fixed"
|
| 1196 |
if len(unfixed_pkgs) > 0:
|
| 1197 |
if len(unfixed_pkgs) == 1:
|
| 1198 |
pkgs += "package " + unfixed_pkgs[0] + " is "
|
| 1199 |
else:
|
| 1200 |
pkgs += "packages " + ", ".join(unfixed_pkgs) + " are "
|
| 1201 |
if fixed_in_security:
|
| 1202 |
pkgs = "%sfixed in %s-security. " % (pkgs, suite)
|
| 1203 |
if suite == "stable":
|
| 1204 |
result = "fixed"
|
| 1205 |
else:
|
| 1206 |
result = "partially-fixed"
|
| 1207 |
else:
|
| 1208 |
pkgs += "vulnerable. "
|
| 1209 |
result = "vulnerable"
|
| 1210 |
if len(undet_pkgs) > 0:
|
| 1211 |
if len(undet_pkgs) == 1:
|
| 1212 |
pkgs += "package " + undet_pkgs[0] + " may be vulnerable but needs to be checked."
|
| 1213 |
else:
|
| 1214 |
pkgs += "packages " + ", ".join(undet_pkgs) + " may be vulnerable but need to be checked."
|
| 1215 |
if len(unimp_pkgs) > 0 and len(undet_pkgs) == 0 and len(unfixed_pkgs) == 0:
|
| 1216 |
result = "fixed"
|
| 1217 |
if len(unimp_pkgs) == 1:
|
| 1218 |
pkgs = "package %s is vulnerable; however, the security impact is unimportant." % unimp_pkgs[0]
|
| 1219 |
else:
|
| 1220 |
pkgs = "packages %s are vulnerable; however, the security impact is unimportant." % (', '.join(unimp_pkgs))
|
| 1221 |
|
| 1222 |
cursor.execute("""INSERT INTO bug_status
|
| 1223 |
(bug_name, release, status, reason)
|
| 1224 |
VALUES (?, ?, ?, ?)""",
|
| 1225 |
(bug_name, suite, result, pkgs))
|
| 1226 |
|
| 1227 |
def calculateDebsecan0(self, release):
|
| 1228 |
"""Create data for the debsecan tool (VERSION 0 format)."""
|
| 1229 |
|
| 1230 |
c = self.cursor()
|
| 1231 |
|
| 1232 |
c.execute("""CREATE TEMPORARY TABLE vulnlist (
|
| 1233 |
name TEXT NOT NULL,
|
| 1234 |
package TEXT NOT NULL,
|
| 1235 |
note INTEGER NOT NULL,
|
| 1236 |
PRIMARY KEY (name, package)
|
| 1237 |
)""")
|
| 1238 |
|
| 1239 |
# Populate the table with the unstable vulnerabilities;
|
| 1240 |
# override them with the release-specific status.
|
| 1241 |
|
| 1242 |
c.execute("""INSERT INTO vulnlist
|
| 1243 |
SELECT bug_name, package, id FROM package_notes WHERE release = ''""")
|
| 1244 |
|
| 1245 |
if release:
|
| 1246 |
c.execute("""INSERT OR REPLACE INTO vulnlist
|
| 1247 |
SELECT bug_name, package, id FROM package_notes
|
| 1248 |
WHERE release = ?""", (release,))
|
| 1249 |
else:
|
| 1250 |
release = 'sid'
|
| 1251 |
|
| 1252 |
c.execute("""DELETE FROM vulnlist WHERE name LIKE 'TEMP-0000000-%'""")
|
| 1253 |
|
| 1254 |
urgency_to_flag = {'low' : 'L', 'medium' : 'M', 'high' : 'H',
|
| 1255 |
'not yet assigned' : ' '}
|
| 1256 |
|
| 1257 |
result = ["VERSION 0\n"]
|
| 1258 |
for (name, package, fixed_version, kind, urgency, remote, description,
|
| 1259 |
note_id) in list(c.execute("""SELECT
|
| 1260 |
vulnlist.name, vulnlist.package,
|
| 1261 |
COALESCE(n.fixed_version, ''),
|
| 1262 |
n.package_kind, n.urgency,
|
| 1263 |
(SELECT range_remote FROM nvd_data
|
| 1264 |
WHERE cve_name = vulnlist.name) AS remote,
|
| 1265 |
bugs.description,
|
| 1266 |
n.id
|
| 1267 |
FROM vulnlist, bugs, package_notes AS n
|
| 1268 |
WHERE bugs.name = vulnlist.name
|
| 1269 |
AND n.id = vulnlist.note
|
| 1270 |
ORDER BY vulnlist.package""")):
|
| 1271 |
if fixed_version == '0' or urgency == 'unimportant' \
|
| 1272 |
or kind not in ('source', 'binary', 'unknown'):
|
| 1273 |
continue
|
| 1274 |
|
| 1275 |
# Normalize FAKE-* names a bit. The line number (which
|
| 1276 |
# makes the name unique) is completely useless for the
|
| 1277 |
# client.
|
| 1278 |
|
| 1279 |
if name[0:5] == 'TEMP-':
|
| 1280 |
name = '-'.join(name.split('-')[0:2])
|
| 1281 |
|
| 1282 |
# Determine if a fix is available for the specific
|
| 1283 |
# release.
|
| 1284 |
|
| 1285 |
fix_available = ' '
|
| 1286 |
if kind == 'source':
|
| 1287 |
fix_available_sql = """SELECT st.vulnerable
|
| 1288 |
FROM source_packages AS p, source_package_status AS st
|
| 1289 |
WHERE p.name = ?
|
| 1290 |
AND p.release = ?
|
| 1291 |
AND p.subrelease IN ('', 'security')
|
| 1292 |
AND st.bug_name = ?
|
| 1293 |
AND st.package = p.rowid
|
| 1294 |
ORDER BY p.version COLLATE version DESC"""
|
| 1295 |
else:
|
| 1296 |
fix_available_sql = ''
|
| 1297 |
|
| 1298 |
if fix_available_sql:
|
| 1299 |
for (v,) in c.execute(fix_available_sql,
|
| 1300 |
(package, release, name)):
|
| 1301 |
assert v is not None
|
| 1302 |
if not v:
|
| 1303 |
fix_available = 'F'
|
| 1304 |
break
|
| 1305 |
|
| 1306 |
if kind == 'source':
|
| 1307 |
kind = 'S'
|
| 1308 |
elif kind == 'binary':
|
| 1309 |
kind = 'B'
|
| 1310 |
else:
|
| 1311 |
kind = ' '
|
| 1312 |
|
| 1313 |
if remote is None:
|
| 1314 |
remote = '?'
|
| 1315 |
elif remote:
|
| 1316 |
remote = 'R'
|
| 1317 |
else:
|
| 1318 |
remote = ' '
|
| 1319 |
|
| 1320 |
result.append("%s,%c%c%c%c,%s,%s,%s\n"
|
| 1321 |
% (name,
|
| 1322 |
kind, urgency_to_flag[urgency], remote,
|
| 1323 |
fix_available,
|
| 1324 |
package, fixed_version, description))
|
| 1325 |
result = base64.encodestring(zlib.compress(''.join(result), 9))
|
| 1326 |
|
| 1327 |
c.execute(
|
| 1328 |
"INSERT OR REPLACE INTO debsecan_data (name, data) VALUES (?, ?)",
|
| 1329 |
('release/' + release, result))
|
| 1330 |
|
| 1331 |
c.execute("DROP TABLE vulnlist")
|
| 1332 |
|
| 1333 |
def calculateDebsecan1(self):
|
| 1334 |
"""Calculates debsecan data (release-independent, VERSION 1)."""
|
| 1335 |
|
| 1336 |
c = self.cursor()
|
| 1337 |
|
| 1338 |
result_start = ['VERSION 1']
|
| 1339 |
bug_to_index = {}
|
| 1340 |
bug_to_remote_flag = {}
|
| 1341 |
|
| 1342 |
def fill_bug_to_index():
|
| 1343 |
index = 0
|
| 1344 |
for (bug, desc, remote) in c.execute(
|
| 1345 |
"""SELECT DISTINCT p.bug_name, b.description,
|
| 1346 |
(SELECT range_remote FROM nvd_data
|
| 1347 |
WHERE cve_name = p.bug_name)
|
| 1348 |
FROM package_notes AS p, bugs AS b
|
| 1349 |
WHERE (p.bug_name LIKE 'CVE-%' OR p.bug_name LIKE 'TEMP-%')
|
| 1350 |
AND p.bug_name NOT LIKE 'TEMP-0000000-%'
|
| 1351 |
AND p.urgency <> 'unimportant'
|
| 1352 |
AND COALESCE(p.fixed_version, '') <> '0'
|
| 1353 |
AND p.package_kind IN ('source', 'binary', 'unknown')
|
| 1354 |
AND b.name = p.bug_name
|
| 1355 |
ORDER BY p.bug_name"""):
|
| 1356 |
if remote is None:
|
| 1357 |
remote = '?'
|
| 1358 |
elif remote:
|
| 1359 |
remote = 'R'
|
| 1360 |
else:
|
| 1361 |
remote = ' '
|
| 1362 |
|
| 1363 |
# Normalize FAKE-* names a bit. The line number (which
|
| 1364 |
# makes the name unique) is completely useless for the
|
| 1365 |
# client.
|
| 1366 |
|
| 1367 |
if bug[0:5] == 'TEMP-':
|
| 1368 |
name = '-'.join(bug.split('-')[0:2])
|
| 1369 |
else:
|
| 1370 |
name = bug
|
| 1371 |
|
| 1372 |
result_start.append("%s,,%s" % (name, desc))
|
| 1373 |
bug_to_index[bug] = index
|
| 1374 |
bug_to_remote_flag[bug] = remote
|
| 1375 |
index += 1
|
| 1376 |
result_start.append('')
|
| 1377 |
fill_bug_to_index()
|
| 1378 |
|
| 1379 |
urgency_to_flag = {'low' : 'L', 'medium' : 'M', 'high' : 'H',
|
| 1380 |
'not yet assigned' : ' '}
|
| 1381 |
|
| 1382 |
vuln_list = []
|
| 1383 |
source_packages = {}
|
| 1384 |
def fill_vuln_list(source_packages=source_packages):
|
| 1385 |
for (bug, package) in list(c.execute(
|
| 1386 |
"""SELECT DISTINCT bug_name, package
|
| 1387 |
FROM package_notes
|
| 1388 |
WHERE (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
|
| 1389 |
AND bug_name NOT LIKE 'TEMP-0000000-%'
|
| 1390 |
AND package_kind IN ('source', 'binary', 'unknown')
|
| 1391 |
GROUP BY package, bug_name
|
| 1392 |
ORDER BY package, bug_name""")):
|
| 1393 |
|
| 1394 |
# By default, unstable is unfixed even if there are
|
| 1395 |
# only release-specific annotations available. This
|
| 1396 |
# is slightly at odds with the web front end (see
|
| 1397 |
# data/latently-vulnerable) which does not normally
|
| 1398 |
# report unstable versions as vulnerable in this case.
|
| 1399 |
# However, in our tracking model, the main branch
|
| 1400 |
# (sid) cannot be non-vulnerable, while the
|
| 1401 |
# release-specific branches are.
|
| 1402 |
unstable_fixed = ''
|
| 1403 |
|
| 1404 |
total_urgency = ''
|
| 1405 |
other_versions = {}
|
| 1406 |
is_binary = False
|
| 1407 |
is_unknown = False
|
| 1408 |
fixed_releases = {}
|
| 1409 |
for (release, kind, urgency, version) in list(c.execute(
|
| 1410 |
"""SELECT release, package_kind, urgency, fixed_version
|
| 1411 |
FROM package_notes WHERE bug_name = ? AND package = ?""",
|
| 1412 |
(bug, package))):
|
| 1413 |
if not total_urgency:
|
| 1414 |
total_urgency = urgency
|
| 1415 |
elif total_urgency == 'unknown':
|
| 1416 |
if urgency <> 'unimportant':
|
| 1417 |
total_urgency = urgency
|
| 1418 |
elif urgency == 'unknown':
|
| 1419 |
if total_urgency == 'unimportant':
|
| 1420 |
total_urgency = 'unknown'
|
| 1421 |
elif bugs.internUrgency(urgency) \
|
| 1422 |
> bugs.internUrgency(total_urgency):
|
| 1423 |
total_urgency = urgency
|
| 1424 |
|
| 1425 |
if kind == 'binary':
|
| 1426 |
is_binary = True
|
| 1427 |
elif kind == 'source':
|
| 1428 |
source_packages[package] = True
|
| 1429 |
else:
|
| 1430 |
is_unknown = True
|
| 1431 |
|
| 1432 |
if release == '':
|
| 1433 |
unstable_fixed = version
|
| 1434 |
if version:
|
| 1435 |
v_ref = debian_support.Version(version)
|
| 1436 |
for (v,) in c.execute("""SELECT version
|
| 1437 |
FROM source_packages WHERE name = ?
|
| 1438 |
AND release = 'sid' AND subrelease = ''""",
|
| 1439 |
(package,)):
|
| 1440 |
if debian_support.Version(v) >= v_ref:
|
| 1441 |
fixed_releases['sid'] = True
|
| 1442 |
break
|
| 1443 |
elif version is not None:
|
| 1444 |
fixed_releases[release] = True
|
| 1445 |
|
| 1446 |
# Collect newer versions in the same release
|
| 1447 |
# (which are supposed to fix the same bug).
|
| 1448 |
|
| 1449 |
v_ref = debian_support.Version(version)
|
| 1450 |
for (v,) in c.execute("""SELECT fixed_version
|
| 1451 |
FROM package_notes
|
| 1452 |
WHERE package = ? AND release = ?""",
|
| 1453 |
(package, release)):
|
| 1454 |
if v is None:
|
| 1455 |
continue
|
| 1456 |
if debian_support.Version(v) >= v_ref:
|
| 1457 |
other_versions[v] = True
|
| 1458 |
|
| 1459 |
# The second part of this SELECT statement
|
| 1460 |
# covers binary-only NMUs.
|
| 1461 |
for (v,) in c.execute("""SELECT version
|
| 1462 |
FROM source_packages WHERE name = ?1
|
| 1463 |
AND release = ?2 AND subrelease IN ('', 'security')
|
| 1464 |
UNION ALL SELECT source_version
|
| 1465 |
FROM binary_packages WHERE source = ?1
|
| 1466 |
AND release = ?2 AND subrelease IN ('', 'security')""",
|
| 1467 |
(package, release)):
|
| 1468 |
if debian_support.Version(v) >= v_ref:
|
| 1469 |
other_versions[v] = True
|
| 1470 |
|
| 1471 |
if not total_urgency:
|
| 1472 |
total_urgency = 'unknown'
|
| 1473 |
|
| 1474 |
# Check if the issue does not actually mark any
|
| 1475 |
# packages as vulnerable. (If unstable_fixed == '0',
|
| 1476 |
# release-specific annotations cannot create
|
| 1477 |
# vulnerabilities, either.)
|
| 1478 |
if total_urgency == 'unimportant' or unstable_fixed == '0':
|
| 1479 |
continue
|
| 1480 |
|
| 1481 |
if unstable_fixed is None:
|
| 1482 |
unstable_fixed = ''
|
| 1483 |
bs_flag = 'S'
|
| 1484 |
if is_binary:
|
| 1485 |
assert not is_unknown
|
| 1486 |
bs_flag = 'B'
|
| 1487 |
elif is_unknown:
|
| 1488 |
bs_flag = ' '
|
| 1489 |
|
| 1490 |
other_versions = other_versions.keys()
|
| 1491 |
other_versions.sort()
|
| 1492 |
other_versions = ' '.join(other_versions)
|
| 1493 |
|
| 1494 |
vuln_list.append(("%s,%d,%c%c%c"
|
| 1495 |
% (package, bug_to_index[bug],
|
| 1496 |
bs_flag, urgency_to_flag[total_urgency],
|
| 1497 |
bug_to_remote_flag[bug]),
|
| 1498 |
fixed_releases.keys(),
|
| 1499 |
",%s,%s"
|
| 1500 |
% (unstable_fixed, other_versions)))
|
| 1501 |
fill_vuln_list()
|
| 1502 |
source_packages = source_packages.keys()
|
| 1503 |
source_packages.sort()
|
| 1504 |
|
| 1505 |
def store_value(name, value):
|
| 1506 |
value = base64.encodestring(zlib.compress(value, 9))
|
| 1507 |
c.execute("""INSERT OR REPLACE INTO debsecan_data
|
| 1508 |
VALUES (?, ?)""", (name, value))
|
| 1509 |
|
| 1510 |
def gen_release(release):
|
| 1511 |
result = result_start[:]
|
| 1512 |
|
| 1513 |
for (prefix, releases, suffix) in vuln_list:
|
| 1514 |
if release in releases:
|
| 1515 |
fixed = 'F'
|
| 1516 |
else:
|
| 1517 |
fixed = ' '
|
| 1518 |
result.append(prefix + fixed + suffix)
|
| 1519 |
result.append('')
|
| 1520 |
|
| 1521 |
for sp in source_packages:
|
| 1522 |
bp_list = []
|
| 1523 |
for (bp,) in c.execute("""SELECT name FROM binary_packages
|
| 1524 |
WHERE source = ? AND release = ? AND subrelease = ''
|
| 1525 |
ORDER BY name""",
|
| 1526 |
(sp, release)):
|
| 1527 |
bp_list.append(bp)
|
| 1528 |
if bp_list <> [sp]:
|
| 1529 |
# We intentionally store the empty list, it means
|
| 1530 |
# that the source package is obsolete as a whole.
|
| 1531 |
result.append("%s,%s" % (sp, ' '.join(bp_list)))
|
| 1532 |
result.append('')
|
| 1533 |
|
| 1534 |
store_value('release/1/' + release, '\n'.join(result))
|
| 1535 |
|
| 1536 |
for release in ('sid', 'etch', 'lenny', 'squeeze'):
|
| 1537 |
gen_release(release)
|
| 1538 |
|
| 1539 |
result = result_start
|
| 1540 |
for (prefix, release, suffix) in vuln_list:
|
| 1541 |
result.append(prefix + ' ' + suffix)
|
| 1542 |
result.append('')
|
| 1543 |
result.append('')
|
| 1544 |
result.append('')
|
| 1545 |
store_value ('release/1/GENERIC', '\n'.join(result))
|
| 1546 |
|
| 1547 |
def calculateDebsecan(self):
|
| 1548 |
"""Calculate all debsecan data."""
|
| 1549 |
for release in ('', 'etch', 'lenny', 'squeeze'):
|
| 1550 |
self.calculateDebsecan0(release)
|
| 1551 |
self.calculateDebsecan1()
|
| 1552 |
|
| 1553 |
def getDebsecan(self, name):
|
| 1554 |
"""Returns the debsecan data item NAME."""
|
| 1555 |
for (data,) in self.cursor().execute(
|
| 1556 |
"SELECT data FROM debsecan_data WHERE name = ?", (name,)):
|
| 1557 |
return base64.decodestring(data)
|
| 1558 |
else:
|
| 1559 |
return None
|
| 1560 |
|
| 1561 |
def updateNVD(self, cursor, data, incremental):
|
| 1562 |
"""Adds (and overwrites) NVD data stored in the database. This
|
| 1563 |
can be used for incremental updates if incremental is True."""
|
| 1564 |
if not incremental:
|
| 1565 |
cursor.execute("DELETE FROM nvd_data");
|
| 1566 |
cursor.executemany("INSERT OR REPLACE INTO nvd_data VALUES (?"
|
| 1567 |
+ (", ?" * (len(data[0]) - 1))
|
| 1568 |
+ ")", data)
|
| 1569 |
|
| 1570 |
def getNVD(self, cursor, cve_name):
|
| 1571 |
"""Returns a dictionary with NVD data corresponding to the CVE name,
|
| 1572 |
or None."""
|
| 1573 |
for row in cursor.execute("SELECT * FROM nvd_data WHERE cve_name = ?",
|
| 1574 |
(cve_name,)):
|
| 1575 |
return NVDEntry(row, cursor.getdescription())
|
| 1576 |
return None
|
| 1577 |
|
| 1578 |
def getSourcePackageVersions(self, cursor, pkg):
|
| 1579 |
"""A generator which returns tuples (RELEASE-LIST, VERSION),
|
| 1580 |
the available versions of the source package pkg."""
|
| 1581 |
|
| 1582 |
for (releases, version) in cursor.execute(
|
| 1583 |
"""SELECT string_list(release) AS releases, version
|
| 1584 |
FROM (SELECT release, version FROM source_packages
|
| 1585 |
WHERE name = ?
|
| 1586 |
ORDER BY release_to_number(release))
|
| 1587 |
GROUP BY version""", (pkg,)):
|
| 1588 |
yield releases.split(', '), version
|
| 1589 |
|
| 1590 |
def getBinaryPackageVersions(self, cursor, pkg):
|
| 1591 |
"""A generator which returns tuples (RELEASE-LIST,
|
| 1592 |
SOURCE-PACKAGE, VERSION, ARCH-LIST), the available versions of
|
| 1593 |
the binary package pkg."""
|
| 1594 |
|
| 1595 |
for (releases, source, version, archs) in cursor.execute(
|
| 1596 |
"""SELECT string_list(release) AS releases, source, version, archs
|
| 1597 |
FROM (SELECT release, source, version, string_set(archs) AS archs
|
| 1598 |
FROM binary_packages
|
| 1599 |
WHERE name = ?
|
| 1600 |
GROUP BY release, source, version
|
| 1601 |
ORDER BY release_to_number(release))
|
| 1602 |
GROUP BY source, version, archs""", (pkg,)):
|
| 1603 |
yield releases.split(', '), source, version, archs.split(',')
|
| 1604 |
|
| 1605 |
def getBinaryPackagesForSource(self, cursor, pkg):
|
| 1606 |
"""A generator which returns tuples (PACKAGES, RELEASE-LIST,
|
| 1607 |
VERSION), the available binary packages built from the source
|
| 1608 |
package pkg."""
|
| 1609 |
|
| 1610 |
for (packages, releases, version, archs) in cursor.execute(
|
| 1611 |
"""SELECT string_list(package) AS packages, releases, version,
|
| 1612 |
archs
|
| 1613 |
FROM (SELECT package, string_list(rel) AS releases, version, archs
|
| 1614 |
FROM (SELECT name AS package,
|
| 1615 |
release_name(release, subrelease, archive) AS rel,
|
| 1616 |
version, string_set(archs) AS archs
|
| 1617 |
FROM binary_packages
|
| 1618 |
WHERE source = ?
|
| 1619 |
GROUP BY name, release, subrelease, archive, version
|
| 1620 |
ORDER BY release_to_number(release), subrelease)
|
| 1621 |
GROUP BY package, version, archs
|
| 1622 |
ORDER BY package)
|
| 1623 |
GROUP BY releases, version, archs
|
| 1624 |
ORDER BY version COLLATE version""", (pkg,)):
|
| 1625 |
yield (packages.split(', '), releases.split(', '),
|
| 1626 |
archs.split(','), version)
|
| 1627 |
|
| 1628 |
def getSourcePackages(self, cursor, bug):
|
| 1629 |
"""A generator which returns tuples (SOURCE-PACKAGE,
|
| 1630 |
RELEASE-LIST, VERSION, VULNERABLE-FLAG) of source packages
|
| 1631 |
which are related to the given bug."""
|
| 1632 |
|
| 1633 |
for (package, releases, version, vulnerable) in cursor.execute(
|
| 1634 |
"""SELECT package, string_list(release), version, vulnerable
|
| 1635 |
FROM (SELECT p.name AS package,
|
| 1636 |
release_name(p.release, p.subrelease, p.archive) AS release,
|
| 1637 |
p.version AS version, s.vulnerable AS vulnerable
|
| 1638 |
FROM source_package_status AS s, source_packages AS p
|
| 1639 |
WHERE s.bug_name = ? AND p.rowid = s.package
|
| 1640 |
ORDER BY release_to_number(p.release), p.subrelease)
|
| 1641 |
GROUP BY package, version, vulnerable
|
| 1642 |
ORDER BY package, version COLLATE version""",
|
| 1643 |
(bug,)):
|
| 1644 |
yield package, releases.split(', '), version, vulnerable
|
| 1645 |
|
| 1646 |
def getBugsFromDebianBug(self, cursor, number):
|
| 1647 |
"""A generator which returns a list of tuples
|
| 1648 |
(BUG-NAME, URGENCY, DESCRIPTION)."""
|
| 1649 |
|
| 1650 |
return cursor.execute(
|
| 1651 |
"""SELECT DISTINCT bugs.name, package_notes.urgency,
|
| 1652 |
bugs.description
|
| 1653 |
FROM debian_bugs, package_notes, bugs
|
| 1654 |
WHERE debian_bugs.bug = ? AND package_notes.id = debian_bugs.note
|
| 1655 |
AND bugs.name = package_notes.bug_name
|
| 1656 |
ORDER BY bug_name""", (number,))
|
| 1657 |
|
| 1658 |
def isSourcePackage(self, cursor, pkg):
|
| 1659 |
"""Returns a true value if pkg is a source package."""
|
| 1660 |
((flag,),) = cursor.execute(
|
| 1661 |
"SELECT EXISTS (SELECT * FROM source_packages WHERE name = ?)",
|
| 1662 |
(pkg,))
|
| 1663 |
return flag
|
| 1664 |
|
| 1665 |
def isBinaryPackage(self, cursor, pkg):
|
| 1666 |
"""Returns a true value if pkg is a binary package."""
|
| 1667 |
((flag,),) = cursor.execute(
|
| 1668 |
"SELECT EXISTS (SELECT * FROM binary_packages WHERE name = ?)",
|
| 1669 |
(pkg,))
|
| 1670 |
return flag
|
| 1671 |
|
| 1672 |
def getBugsForSourcePackage(self, cursor, pkg, vulnerable):
|
| 1673 |
"""Returns a generator for a list of (BUG, DESCRIPTION) pairs
|
| 1674 |
which have the requested status. Only bugs affecting supported
|
| 1675 |
releases are returned."""
|
| 1676 |
return cursor.execute(
|
| 1677 |
"""SELECT DISTINCT name, description
|
| 1678 |
FROM (SELECT bugs.name AS name, bugs.description AS description,
|
| 1679 |
MAX(st.vulnerable
|
| 1680 |
AND COALESCE((SELECT st2.vulnerable FROM source_packages AS sp2,
|
| 1681 |
source_package_status AS st2
|
| 1682 |
WHERE sp2.name = sp.name AND sp2.release = sp.release
|
| 1683 |
AND sp2.subrelease = 'security' AND sp2.archive = sp.archive
|
| 1684 |
AND st2.package = sp2.rowid AND st2.bug_name = st.bug_name
|
| 1685 |
ORDER BY st2.vulnerable DESC), 1)) AS vulnerable
|
| 1686 |
FROM source_packages AS sp, source_package_status AS st, bugs
|
| 1687 |
WHERE sp.name = ?
|
| 1688 |
AND sp.release IN ('lenny', 'squeeze', 'sid')
|
| 1689 |
AND sp.subrelease <> 'security'
|
| 1690 |
AND st.package = sp.rowid
|
| 1691 |
AND bugs.name = st.bug_name
|
| 1692 |
AND st.urgency <> 'unimportant'
|
| 1693 |
GROUP BY bugs.name, bugs.description, sp.name)
|
| 1694 |
WHERE vulnerable = ?
|
| 1695 |
ORDER BY name""", (pkg, vulnerable))
|
| 1696 |
|
| 1697 |
def getTODOs(self, cursor=None, hide_check=False):
|
| 1698 |
"""Returns a list of pairs (BUG-NAME, DESCRIPTION)."""
|
| 1699 |
if cursor is None:
|
| 1700 |
cursor = self.cursor()
|
| 1701 |
if hide_check:
|
| 1702 |
return cursor.execute(
|
| 1703 |
"""SELECT DISTINCT bugs.name, bugs.description
|
| 1704 |
FROM bugs_notes, bugs
|
| 1705 |
WHERE bugs_notes.typ = 'TODO'
|
| 1706 |
AND bugs_notes.comment <> 'check'
|
| 1707 |
AND bugs.name = bugs_notes.bug_name
|
| 1708 |
ORDER BY name """)
|
| 1709 |
else:
|
| 1710 |
return cursor.execute(
|
| 1711 |
"""SELECT DISTINCT bugs.name, bugs.description
|
| 1712 |
FROM bugs_notes, bugs
|
| 1713 |
WHERE bugs_notes.typ = 'TODO'
|
| 1714 |
AND bugs.name = bugs_notes.bug_name
|
| 1715 |
ORDER BY name """)
|
| 1716 |
|
| 1717 |
def getBugXrefs(self, cursor, bug):
|
| 1718 |
"""Returns a generator for a list of bug names. The listed
|
| 1719 |
bugs refer to the given bug, or the bug refers to them."""
|
| 1720 |
|
| 1721 |
for (bug_name,) in cursor.execute(
|
| 1722 |
"""SELECT DISTINCT bug
|
| 1723 |
FROM (SELECT target AS bug
|
| 1724 |
FROM bugs_xref WHERE source = ?
|
| 1725 |
UNION ALL SELECT source AS bug
|
| 1726 |
FROM bugs_xref WHERE target = ?
|
| 1727 |
UNION ALL SELECT bug_origin AS bug FROM package_notes
|
| 1728 |
WHERE bug_name = ? AND bug_origin <> '')
|
| 1729 |
WHERE bug <> ?
|
| 1730 |
ORDER BY bug""", (bug, bug, bug, bug)):
|
| 1731 |
yield bug_name
|
| 1732 |
|
| 1733 |
def readRemovedPackages(self, cursor, filename):
|
| 1734 |
"""Reads a file of removed packages and stores it in the database.
|
| 1735 |
The original contents of the removed_packages table is preserved."""
|
| 1736 |
|
| 1737 |
f = file(filename)
|
| 1738 |
|
| 1739 |
re_package = re.compile(r'^\s*([a-z0-9]\S+)\s*$')
|
| 1740 |
|
| 1741 |
# Not very good error reporting, but changes to that file are
|
| 1742 |
# rare.
|
| 1743 |
|
| 1744 |
def gen():
|
| 1745 |
for line in f:
|
| 1746 |
if line == '':
|
| 1747 |
break
|
| 1748 |
if line[0] == '#' or line == '\n':
|
| 1749 |
continue
|
| 1750 |
match = re_package.match(line)
|
| 1751 |
if match:
|
| 1752 |
yield match.groups()
|
| 1753 |
else:
|
| 1754 |
raise ValueError, "not a package: " + `line`
|
| 1755 |
|
| 1756 |
cursor.executemany(
|
| 1757 |
"INSERT OR IGNORE INTO removed_packages (name) VALUES (?)", gen())
|
| 1758 |
|
| 1759 |
def getUnknownPackages(self, cursor):
|
| 1760 |
"""Returns a generator for a list of unknown packages.
|
| 1761 |
Each entry has the form (PACKAGE, BUG-LIST)."""
|
| 1762 |
|
| 1763 |
old_package = ''
|
| 1764 |
bugs = []
|
| 1765 |
for (package, bug_name) in cursor.execute(
|
| 1766 |
"""SELECT DISTINCT package, bug_name
|
| 1767 |
FROM package_notes WHERE package_kind = 'unknown'
|
| 1768 |
AND NOT EXISTS (SELECT * FROM removed_packages
|
| 1769 |
WHERE name = package)
|
| 1770 |
ORDER BY package, bug_name"""):
|
| 1771 |
if package <> old_package:
|
| 1772 |
if old_package:
|
| 1773 |
yield (old_package, bugs)
|
| 1774 |
old_package = package
|
| 1775 |
bugs = []
|
| 1776 |
bugs.append(bug_name)
|
| 1777 |
if old_package:
|
| 1778 |
yield (old_package, bugs)
|
| 1779 |
|
| 1780 |
def getFakeBugs(self, cursor=None):
|
| 1781 |
"""Returns a list of pairs (BUG-NAME, DESCRIPTION)."""
|
| 1782 |
|
| 1783 |
if cursor is None:
|
| 1784 |
cursor = self.cursor()
|
| 1785 |
|
| 1786 |
return list(cursor.execute(
|
| 1787 |
"""SELECT name, description FROM bugs
|
| 1788 |
WHERE name > 'TEMP-' AND name LIKE 'TEMP-%'
|
| 1789 |
ORDER BY name"""))
|
| 1790 |
|
| 1791 |
def getITPs(self, cursor):
|
| 1792 |
"""Returns a generator for a list of unknown packages.
|
| 1793 |
Each entry has the form (PACKAGE, BUG-LIST, DEBIAN-BUG-LIST)."""
|
| 1794 |
|
| 1795 |
# The "|| ''" is required to convert the string_set argument
|
| 1796 |
# to a string.
|
| 1797 |
for (package, bugs, debian_bugs) in cursor.execute(
|
| 1798 |
"""SELECT DISTINCT n.package, string_set(n.bug_name),
|
| 1799 |
string_set(db.bug || '')
|
| 1800 |
FROM package_notes AS n, debian_bugs AS db
|
| 1801 |
WHERE package_kind = 'itp'
|
| 1802 |
AND db.note = n.id
|
| 1803 |
GROUP BY n.package
|
| 1804 |
ORDER BY n.package"""):
|
| 1805 |
yield (package, bugs.split(','), map(int, debian_bugs.split(',')))
|
| 1806 |
|
| 1807 |
def getEffectiveVersion(self, release, pkg, purpose, cache=None, cursor=None):
|
| 1808 |
"""Retrieve the effective version of a source package in a release.
|
| 1809 |
|
| 1810 |
The effective version is the version that matches the recommended
|
| 1811 |
sources.list file for the intended purpose. For suitable values
|
| 1812 |
of purpose, see dist_config.
|
| 1813 |
"""
|
| 1814 |
# The cache is structured as a (RELEASE, PACKAGE) => VAL
|
| 1815 |
# dict, where VAL is either a dict PURPOSE => VERSION,
|
| 1816 |
# a VERSION, or None.
|
| 1817 |
if cache is not None:
|
| 1818 |
sp = (release, pkg)
|
| 1819 |
if cache.has_key(sp):
|
| 1820 |
d = cache[sp]
|
| 1821 |
if d.__class__ == dict:
|
| 1822 |
return d.get(purpose, None)
|
| 1823 |
else:
|
| 1824 |
return d
|
| 1825 |
|
| 1826 |
if cursor is None:
|
| 1827 |
cursor = self.cursor()
|
| 1828 |
|
| 1829 |
rel = dist_config.releases[release]
|
| 1830 |
purposes = rel['purpose']
|
| 1831 |
results = {}
|
| 1832 |
|
| 1833 |
Version = debian_support.Version
|
| 1834 |
for (part, ver) in cursor.execute(
|
| 1835 |
"""SELECT DISTINCT subrelease, version FROM source_packages
|
| 1836 |
WHERE release = ? AND name = ?""", (str(release), pkg)):
|
| 1837 |
ver = Version(ver)
|
| 1838 |
for (purpose, permitted) in purposes.items():
|
| 1839 |
if part not in permitted:
|
| 1840 |
continue
|
| 1841 |
if results.has_key(purpose):
|
| 1842 |
oldver = results[purpose]
|
| 1843 |
if ver <= oldver:
|
| 1844 |
continue
|
| 1845 |
results[purpose] = ver
|
| 1846 |
|
| 1847 |
if cache is not None:
|
| 1848 |
vers = set(map(str, results.values()))
|
| 1849 |
l = len(vers)
|
| 1850 |
if l == 1:
|
| 1851 |
for r in vers:
|
| 1852 |
cache[sp] = Version(r)
|
| 1853 |
elif l == 0:
|
| 1854 |
cache[sp] = None
|
| 1855 |
else:
|
| 1856 |
cache[sp] = results
|
| 1857 |
|
| 1858 |
return results.get(purpose, None)
|
| 1859 |
|
| 1860 |
def check(self, cursor=None):
|
| 1861 |
"""Runs a simple consistency check and prints the results."""
|
| 1862 |
|
| 1863 |
if cursor is None:
|
| 1864 |
cursor = self.cursor()
|
| 1865 |
|
| 1866 |
for (package, release, archive, architecture, source) in\
|
| 1867 |
cursor.execute(
|
| 1868 |
"""SELECT package, release, archive, architecture, source
|
| 1869 |
FROM binary_packages
|
| 1870 |
WHERE NOT EXISTS
|
| 1871 |
(SELECT *
|
| 1872 |
FROM source_packages AS sp
|
| 1873 |
WHERE sp.package = binary_packages.source
|
| 1874 |
AND sp.release = binary_packages.release
|
| 1875 |
AND sp.archive = binary_packages.archive)
|
| 1876 |
"""):
|
| 1877 |
print "error: binary package without source package"
|
| 1878 |
print " binary package:", package
|
| 1879 |
print " release:", release
|
| 1880 |
if archive:
|
| 1881 |
print " archive:", archive
|
| 1882 |
print " architecture:", architecture
|
| 1883 |
print " missing source package:", source
|
| 1884 |
|
| 1885 |
for (package, release, archive, architecture, version,
|
| 1886 |
source, source_version) \
|
| 1887 |
in cursor.execute("""SELECT binary_packages.package,
|
| 1888 |
binary_packages.release, binary_packages.archive,
|
| 1889 |
binary_packages.architecture,binary_packages.version,
|
| 1890 |
sp.package, sp.version
|
| 1891 |
FROM binary_packages, source_packages AS sp
|
| 1892 |
WHERE sp.package = binary_packages.source
|
| 1893 |
AND sp.release = binary_packages.release
|
| 1894 |
AND sp.archive = binary_packages.archive
|
| 1895 |
AND sp.version <> binary_packages.source_version"""):
|
| 1896 |
relation = cmp(debian_support.Version(version),
|
| 1897 |
debian_support.Version(source_version))
|
| 1898 |
assert relation <> 0
|
| 1899 |
if relation <= 0:
|
| 1900 |
print "error: binary package is older than source package"
|
| 1901 |
else:
|
| 1902 |
print "warning: binary package is newer than source package"
|
| 1903 |
print " binary package: %s (%s)" % (package, version)
|
| 1904 |
print " source package: %s (%s)" % (source, source_version)
|
| 1905 |
print " release:", release
|
| 1906 |
if archive:
|
| 1907 |
print " archive:", archive
|
| 1908 |
print " architecture:", architecture
|
| 1909 |
|
| 1910 |
def test():
|
| 1911 |
assert mergeLists('', '') == [], mergeLists('', '')
|
| 1912 |
assert mergeLists('', []) == []
|
| 1913 |
assert mergeLists('a', 'a') == ['a']
|
| 1914 |
assert mergeLists('a', 'b') == ['a', 'b']
|
| 1915 |
assert mergeLists('a,c', 'b') == ['a', 'b', 'c']
|
| 1916 |
assert mergeLists('a,c', ['b', 'de']) == ['a', 'b', 'c', 'de']
|
| 1917 |
|
| 1918 |
import os
|
| 1919 |
db_file = 'test_security.db'
|
| 1920 |
try:
|
| 1921 |
db = DB(db_file)
|
| 1922 |
except SchemaMismatch:
|
| 1923 |
os.unlink(db_file)
|
| 1924 |
db = DB(db_file)
|
| 1925 |
|
| 1926 |
cursor = db.writeTxn()
|
| 1927 |
db.readBugs(cursor, '../../data')
|
| 1928 |
db.commit(cursor)
|
| 1929 |
|
| 1930 |
b = bugs.BugFromDB(cursor, 'CVE-2005-2491')
|
| 1931 |
assert b.name == 'CVE-2005-2491', b.name
|
| 1932 |
assert b.description == 'Integer overflow in pcre_compile.c in Perl Compatible Regular ...', b.description
|
| 1933 |
assert len(b.xref) == 2, b.xref
|
| 1934 |
assert not b.not_for_us
|
| 1935 |
assert 'DSA-800-1' in b.xref, b.xref
|
| 1936 |
assert 'DTSA-10-1' in b.xref, b.xref
|
| 1937 |
assert tuple(b.comments) == (('NOTE', 'gnumeric/goffice includes one as well; according to upstream not exploitable in gnumeric,'),
|
| 1938 |
('NOTE', 'new copy will be included any way')),\
|
| 1939 |
b.comments
|
| 1940 |
|
| 1941 |
assert len(b.notes) == 4, len(b.notes)
|
| 1942 |
|
| 1943 |
for n in b.notes:
|
| 1944 |
assert n.release is None
|
| 1945 |
if n.package == 'pcre3':
|
| 1946 |
assert n.fixed_version == debian_support.Version('6.3-0.1etch1')
|
| 1947 |
assert tuple(n.bugs) == (324531,), n.bugs
|
| 1948 |
assert n.urgency == bugs.internUrgency('medium')
|
| 1949 |
elif n.package == 'python2.1':
|
| 1950 |
assert n.fixed_version == debian_support.Version('2.1.3dfsg-3')
|
| 1951 |
assert len(n.bugs) == 0, n.bugs
|
| 1952 |
assert n.urgency == bugs.internUrgency('medium')
|
| 1953 |
elif n.package == 'python2.2':
|
| 1954 |
assert n.fixed_version == debian_support.Version('2.2.3dfsg-4')
|
| 1955 |
assert len(n.bugs) == 0, n.bugs
|
| 1956 |
assert n.urgency == bugs.internUrgency('medium')
|
| 1957 |
elif n.package == 'python2.3':
|
| 1958 |
assert n.fixed_version == debian_support.Version('2.3.5-8')
|
| 1959 |
assert len(n.bugs) == 0, n.bugs
|
| 1960 |
assert n.urgency == bugs.internUrgency('medium')
|
| 1961 |
else:
|
| 1962 |
assert False
|
| 1963 |
|
| 1964 |
assert bugs.BugFromDB(cursor, 'DSA-311').isKernelOnly()
|
| 1965 |
|
| 1966 |
if __name__ == "__main__":
|
| 1967 |
test()
|