[Git][security-tracker-team/security-tracker][helmutg/vulnerable-enum] security_db.py: track source_package_status.vulnerable as an enum
Helmut Grohne (@helmutg)
helmutg at debian.org
Thu Aug 6 17:36:10 BST 2026
Helmut Grohne pushed to branch helmutg/vulnerable-enum at Debian Security Tracker / security-tracker
Commits:
4ef7958d by Helmut Grohne at 2026-08-06T18:35:56+02:00
security_db.py: track source_package_status.vulnerable as an enum
The vulnerable column of source_package_status can take three possible
values:
* 0 (fixed)
* 1 (affected)
* 2 (undetermined)
We can encapsulate this as an IntEnum on the Python side. Then we can
use those names in place of magic numbers. Additionally, we may add a
constraint to the database column. Since the old schema is compatible,
we do not incur a new schema version.
Reported-by: Emilio Pozuelo Monfort <pochu at debian.org>
- - - - -
2 changed files:
- bin/tracker_service.py
- lib/python/security_db.py
Changes:
=====================================
bin/tracker_service.py
=====================================
@@ -73,7 +73,7 @@ class BugFilter:
urg in ('medium', 'medium**')
filterhigh = not self.params['high_urgency'] and \
urg in ('high', 'high**')
- filterund = not self.params['undetermined_issues'] and vuln == 2
+ filterund = not self.params['undetermined_issues'] and vuln == security_db.Vulnerable.UNDETERMINED
filteruni = not self.params['unimportant_urgency'] \
and urg == 'unimportant'
filteruna = not self.params['unassigned_urgency'] \
@@ -424,10 +424,10 @@ data source.""")],
package = compose(
self.make_source_package_ref(url, package),
" (", self.make_pts_ref(url, package, 'PTS'), ")")
- if vulnerable == 1:
+ if vulnerable == security_db.Vulnerable.AFFECTED:
vuln = self.make_red('vulnerable')
version = self.make_red(version)
- elif vulnerable == 2:
+ elif vulnerable == security_db.Vulnerable.UNDETERMINED:
vuln = self.make_purple('undetermined')
version = self.make_purple(version)
else:
@@ -598,7 +598,7 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
def format_summary_entry(per_release):
if per_release is None:
return self.make_purple('unknown')
- if per_release.vulnerable == 1:
+ if per_release.vulnerable == security_db.Vulnerable.AFFECTED:
if per_release.state == 'no-dsa':
if per_release.reason:
text = 'vulnerable (no DSA, %s)' % per_release.reason
@@ -609,9 +609,9 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
text=hint)
else:
return self.make_red('vulnerable')
- if per_release.vulnerable == 2:
+ if per_release.vulnerable == security_db.Vulnerable.UNDETERMINED:
return self.make_purple('undetermined')
- assert per_release.vulnerable == 0
+ assert per_release.vulnerable == security_db.Vulnerable.FIXED
return self.make_green('fixed')
def gen_summary(bugs):
@@ -704,7 +704,7 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
if urgency.startswith('high'):
urgency = self.make_red(urgency)
- elif vulnerable == 2:
+ elif vulnerable == security_db.Vulnerable.UNDETERMINED:
urgency = self.make_purple(urgency)
else:
if no_dsa:
@@ -765,7 +765,7 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
if urgency.startswith('high'):
urgency = self.make_red(urgency)
- elif vulnerable == 2:
+ elif vulnerable == security_db.Vulnerable.UNDETERMINED:
urgency = self.make_purple(urgency)
yield (self.make_source_package_ref(url, pkg_name, title), self.make_xref(url, bug_name),
@@ -792,9 +792,10 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
(SELECT range_remote FROM nvd_data
WHERE cve_name = st.bug_name)
FROM source_package_status AS st, source_packages AS sp
- WHERE st.vulnerable AND sp.rowid = st.package
+ WHERE st.vulnerable != ? AND sp.rowid = st.package
AND sp.release = ? AND sp.subrelease = ''
- ORDER BY sp.name, st.bug_name COLLATE version""", (rel,)):
+ ORDER BY sp.name, st.bug_name COLLATE version""",
+ (security_db.Vulnerable.FIXED, rel,)):
if bf.urgencyFiltered(urgency, vulnerable):
continue
if bf.remoteFiltered(remote):
@@ -818,7 +819,7 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
if urgency.startswith('high'):
urgency = self.make_red(urgency)
- elif vulnerable == 2:
+ elif vulnerable == security_db.Vulnerable.UNDETERMINED:
urgency = self.make_purple(urgency)
yield self.make_source_package_ref(url, pkg_name, title), self.make_xref(url, bug_name), urgency, remote
@@ -900,7 +901,7 @@ to improve our documentation and procedures, so feedback is welcome.""")])])
if urgency.startswith('high'):
urgency = self.make_red(urgency)
- elif vulnerable == 2:
+ elif vulnerable == security_db.Vulnerable.UNDETERMINED:
urgency = self.make_purple(urgency)
if stable_later:
@@ -952,10 +953,11 @@ checker to find out why they have not entered testing yet."""),
"""SELECT DISTINCT sp.name, st.bug_name, sp.release,
bugs.description
FROM source_package_status AS st, source_packages AS sp, bugs
- WHERE st.vulnerable == 2 AND sp.rowid = st.package
+ WHERE st.vulnerable == ? AND sp.rowid = st.package
AND sp.release IN (""" + ",".join("?" * len(releases)) + """)
AND sp.subrelease = '' AND st.bug_name == bugs.name
- ORDER BY sp.name, st.bug_name COLLATE version""", releases):
+ ORDER BY sp.name, st.bug_name COLLATE version""",
+ (security_db.Vulnerable.UNDETERMINED, *releases)):
if old_bug == '':
old_bug = bug_name
@@ -996,11 +998,12 @@ checker to find out why they have not entered testing yet."""),
"""SELECT DISTINCT sp.name, st.bug_name, sp.release,
bugs.description
FROM source_package_status AS st, source_packages AS sp, bugs
- WHERE st.vulnerable > 0 AND sp.rowid = st.package
+ WHERE st.vulnerable != ? AND sp.rowid = st.package
AND sp.release IN (""" + ",".join("?" * len(releases)) + """)
AND st.urgency == 'unimportant'
AND sp.subrelease = '' AND st.bug_name == bugs.name
- ORDER BY sp.name, st.bug_name COLLATE version""", releases):
+ ORDER BY sp.name, st.bug_name COLLATE version""",
+ (security_db.Vulnerable.FIXED, *releases)):
if old_bug == '':
old_bug = bug_name
@@ -1237,8 +1240,8 @@ issue (or a bug has been created and is not recorded in this database).
In the second kind of names, there is a Debian bug for the issue, and the "''',
CODE("000000"), '''"part of the name is replaced with the
Debian bug number.'''),
- make_table(gen(1),title=H2('With unfixed issues'), caption=("Bug", "Description")),
- make_table(gen(0),title=H2('The rest'), caption=("Bug", "Description")),
+ make_table(gen(security_db.Vulnerable.AFFECTED),title=H2('With unfixed issues'), caption=("Bug", "Description")),
+ make_table(gen(security_db.Vulnerable.FIXED),title=H2('The rest'), caption=("Bug", "Description")),
])
def page_data_pts(self, path, params, url):
@@ -1248,10 +1251,10 @@ Debian bug number.'''),
(SELECT package, bug, urgency FROM stable_status
UNION ALL SELECT DISTINCT sp.name, st.bug_name, st.urgency
FROM source_package_status AS st, source_packages AS sp
- WHERE st.vulnerable AND st.urgency <> 'unimportant'
+ WHERE st.vulnerable != ? AND st.urgency <> 'unimportant'
AND sp.rowid = st.package AND sp.release = 'sid'
AND sp.subrelease = '') x WHERE urgency <> 'unimportant'
- GROUP BY package ORDER BY package"""):
+ GROUP BY package ORDER BY package""", (security_db.Vulnerable.FIXED,)):
data.append(pkg)
data.append(':')
data.append(str(bugs))
=====================================
lib/python/security_db.py
=====================================
@@ -31,6 +31,7 @@ import apsw
import bugs
from collections import defaultdict, namedtuple
import email.utils
+import enum
import functools
import json
import pickle
@@ -61,6 +62,15 @@ class InsertError(Exception):
def __str__(self):
return self.errors[0] + ' [more...]'
+
+class Vulnerable(enum.IntEnum):
+ """vulnerable column value for source_package_status table"""
+
+ FIXED = 0
+ AFFECTED = 1
+ UNDETERMINED = 2
+
+
def mergeLists(a, b):
"""Merges two lists."""
if isinstance(a, str):
@@ -190,7 +200,7 @@ def getBugsForSourcePackage(cursor, pkg):
# Compute state. Update state-seen flags for global state
# determination.
- if best_row.vulnerable:
+ if best_row.vulnerable != Vulnerable.FIXED:
if best_row.urgency == 'unimportant':
state = 'unimportant'
unimportant_seen = True
@@ -207,7 +217,7 @@ def getBugsForSourcePackage(cursor, pkg):
bug = BugForSourcePackageRelease(
best_row.release, best_row.subrelease, best_row.version,
- best_row.vulnerable, state, comment, reason)
+ Vulnerable(best_row.vulnerable), state, comment, reason)
releases[best_row.release] = bug
# Compute global_state.
@@ -442,7 +452,7 @@ class DB:
cursor.execute("""CREATE TABLE source_package_status
(bug_name TEXT NOT NULL,
package INTEGER NOT NULL REFERENCES source_packages(rowid),
- vulnerable INTEGER NOT NULL,
+ vulnerable INTEGER NOT NULL CHECK (vulnerable IN (0, 1, 2)),
urgency TEXT NOT NULL,
PRIMARY KEY (bug_name, package))""")
cursor.execute(
@@ -1432,12 +1442,12 @@ class DB:
ORDER BY sp.name""",
(bug_name,)):
have_something = True
- if vulnerable == 1:
+ if vulnerable == Vulnerable.AFFECTED:
if urgency == 'unimportant':
unimportant_packages.append( package )
else:
vulnerable_packages.append(package)
- elif vulnerable == 2:
+ elif vulnerable == Vulnerable.UNDETERMINED:
undetermined_packages.append(package)
if vulnerable_packages or undetermined_packages:
@@ -1485,7 +1495,7 @@ class DB:
# here.
status = {'' : {}, 'security' : {}, 'lts' : {}}
- for (package, note, subrelease, vulnerable, urgency) in cursor.execute(
+ for (package, note, subrelease, vulnerable_int, urgency) in cursor.execute(
"""SELECT DISTINCT sp.name, n.id, sp.subrelease,
st.vulnerable, n.urgency
FROM source_package_status AS st,
@@ -1495,7 +1505,7 @@ class DB:
AND n.bug_name = st.bug_name AND n.package = sp.name
ORDER BY sp.name""",
(bug_name, nickname)):
- status[subrelease][(package, note)] = (vulnerable,urgency)
+ status[subrelease][(package, note)] = (Vulnerable(vulnerable_int), urgency)
# Check if any packages in plain testing are vulnerable, and
# if all of those have been fixed in the security archive.
@@ -1504,7 +1514,7 @@ class DB:
undet_pkgs = {}
unimp_pkgs = {}
for ((package, note), (vulnerable, urgency)) in status[''].items():
- if vulnerable == 1:
+ if vulnerable == Vulnerable.AFFECTED:
if urgency == 'unimportant':
unimp_pkgs[package] = True
else:
@@ -1513,7 +1523,7 @@ class DB:
fixed_in_security = False
elif status['lts'].get((package, note), True):
fixed_in_security = False
- elif vulnerable == 2:
+ elif vulnerable == Vulnerable.UNDETERMINED:
undet_pkgs[package] = True
unfixed_pkgs = list(unfixed_pkgs.keys())
@@ -1641,8 +1651,7 @@ class DB:
if fix_available_sql:
for (v,) in c.execute(fix_available_sql,
(package, release, name)):
- assert v is not None
- if not v:
+ if Vulnerable(v) == Vulnerable.FIXED:
fix_available = 'F'
break
@@ -1907,7 +1916,7 @@ class DB:
nodsa_reason = defaultdict(lambda: defaultdict(dict))
next_point_update = defaultdict(lambda: defaultdict(set))
supported_releases = config.get_supported_releases()
- for (pkg, issue, desc, debianbug, release, subrelease, db_version, db_fixed_version, db_status, db_urgency, db_remote, db_nodsa, db_nodsa_reason, db_next_point_update) in self.cursor().execute(
+ for (pkg, issue, desc, debianbug, release, subrelease, db_version, db_fixed_version, db_vulnerable_int, db_urgency, db_remote, db_nodsa, db_nodsa_reason, db_next_point_update) in self.cursor().execute(
"""SELECT sp.name, st.bug_name,
(SELECT cve_desc FROM nvd_data
WHERE cve_name = st.bug_name),
@@ -1937,9 +1946,10 @@ class DB:
AND sp.release IN (""" + ",".join("?" * len(supported_releases)) + """)
ORDER BY sp.name, st.bug_name, sp.release, sp.subrelease""" , supported_releases):
+ db_vulnerable = Vulnerable(db_vulnerable_int)
### to ease debugging...:
#if issue in ('CVE-2012-6656','CVE-2014-8738','CVE-2013-6673') :
- # print pkg, issue, release, subrelease, db_version, db_fixed_version, db_status
+ # print pkg, issue, release, subrelease, db_version, db_fixed_version, db_vulnerable
if pkg not in packages:
packages.append(pkg)
if issue not in issues[pkg]:
@@ -1958,7 +1968,7 @@ class DB:
repositories[pkg][issue].append(repository)
version[pkg][issue][repository] = db_version
fixed_version[pkg][issue][repository] = db_fixed_version
- status[pkg][issue][repository] = db_status
+ status[pkg][issue][repository] = db_vulnerable
urgency[pkg][issue][repository] = db_urgency
if db_next_point_update:
next_point_update[pkg][issue].add(db_next_point_update)
@@ -2006,11 +2016,10 @@ class DB:
# keep looking for a real winner...
winner=suffix
repository=release+winner
- if status[pkg][issue][repository] == 0:
- # 1 = vulnerable, 2 = undetermined
+ if status[pkg][issue][repository] == Vulnerable.FIXED:
state = "resolved"
suite_fixed_version = fixed_version[pkg][issue][repository]
- elif status[pkg][issue][repository] == 2:
+ elif status[pkg][issue][repository] == Vulnerable.UNDETERMINED:
state = "undetermined"
else:
state = "open"
@@ -2130,7 +2139,7 @@ class DB:
releases = config.get_supported_releases()
values = [bug] + releases
- for (package, releases, version, vulnerable) in cursor.execute(
+ for (package, releases, version, vulnerable_int) in cursor.execute(
"""SELECT package, string_list(release), version, vulnerable
FROM (SELECT p.name AS package,
release_name(p.release, p.subrelease, p.archive) AS release,
@@ -2141,7 +2150,7 @@ class DB:
GROUP BY package, version, vulnerable
ORDER BY package, releasepart_to_number(release), subreleasepart_to_number(release), version COLLATE version""",
values):
- yield package, releases.split(', '), version, vulnerable
+ yield package, releases.split(', '), version, Vulnerable(vulnerable_int)
def getBugsFromDebianBug(self, cursor, number):
"""A generator which returns a list of tuples
@@ -2284,7 +2293,7 @@ class DB:
if old_package:
yield (old_package, bugs)
- def getFakeBugs(self, cursor=None, vulnerability=0):
+ def getFakeBugs(self, cursor=None, vulnerability=Vulnerable.FIXED):
"""Returns a list of pairs (BUG-NAME, DESCRIPTION)."""
if cursor is None:
View it on GitLab: https://salsa.debian.org/security-tracker-team/security-tracker/-/commit/4ef7958d5e84690fdc3f600241955800c50f1edf
--
View it on GitLab: https://salsa.debian.org/security-tracker-team/security-tracker/-/commit/4ef7958d5e84690fdc3f600241955800c50f1edf
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-security-tracker-commits/attachments/20260806/728ed97c/attachment-0001.htm>
More information about the debian-security-tracker-commits
mailing list