fix: capture ${source:Version}, the number OSV ranges are actually stated in

OSV states Debian ranges in *source* package versions. A source package that
ships several binaries gives each its own upstream version, and the two are
unrelated numbers: `libldb2` is `2:2.11.0+samba4.22.11+dfsg-0+deb13u1` while its
source, samba, is `2:4.22.11+dfsg-…`.

A consumer that resolves the coordinate on `source_package` — which is what this
driver's `source:Package` is for — and then compares `version` is comparing ldb's
version against samba's range. dpkg reads `2.11.0` as older than the
`2:4.17.4+dfsg-1` that fixed CVE-2022-44640, so a Debian 13 host running samba
4.22.11, five releases past the fix, was reported vulnerable on four packages at
once.

This driver cannot fix that comparison. It is the only place that can supply the
number to make it with.

Empty when dpkg considers it equal to `Version`, and empty on a dpkg that does
not know the field, so it falls back to `Version` — which is the previous
behaviour and correct everywhere except the shape above.

`maxsplit` goes from 4 to 5 with the extra field. Summary stays last, so it keeps
whatever it contains.

**The fixture was carrying four fields against a format string asking for five.**
`source_package` had been silently receiving the description, and no assertion
looked at it. It now carries what dpkg-query actually returns, including a
package whose source version is a different number from its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-09-14 06:10:41 +02:00
co-authored by Claude Opus 5
parent 55635ab551
commit e8eadb46c6
2 changed files with 70 additions and 6 deletions
+20 -3
View File
@@ -835,11 +835,13 @@ class LinuxDriver(OSDriver):
def _get_packages_apt(self) -> list[PackageDict]:
out = self._send(
"dpkg-query -W -f='${Package}\\t${Version}\\t${Installed-Size}"
"\\t${source:Package}\\t${binary:Summary}\\n' 2>/dev/null"
"\\t${source:Package}\\t${source:Version}\\t${binary:Summary}\\n' 2>/dev/null"
)
packages: list[PackageDict] = []
for line in out.splitlines():
parts = line.split("\t", 4)
# Summary stays last and keeps whatever it contains: maxsplit must
# equal the number of tabs the format writes, not the field count.
parts = line.split("\t", 5)
if len(parts) < 2:
continue
name = parts[0].strip()
@@ -847,7 +849,21 @@ class LinuxDriver(OSDriver):
size = int(parts[2].strip()) * 1024 if len(parts) > 2 and parts[2].strip().isdigit() else 0
# Debian source package (e.g. openssh-server → openssh) for OSV matching.
source_package = parts[3].strip() if len(parts) > 3 and parts[3].strip() else name
description = parts[4].strip() if len(parts) > 4 else ""
# And its version, which is a different number from this package's.
#
# OSV states Debian ranges in *source* versions. A source package
# that ships several binaries gives each its own upstream version:
# libldb2 is 2:2.11.0+samba4.22.11+dfsg-… while its source, samba,
# is 2:4.22.11+dfsg-…. A consumer matching on source_package and
# comparing `version` compares two unrelated numbers — dpkg reads
# ldb's 2.11.0 as older than the 2:4.17.4+dfsg-1 that fixed
# CVE-2022-44640, and a host five releases past the fix was reported
# vulnerable on four packages at once.
#
# dpkg leaves this empty when it equals `Version`; so does an older
# dpkg that does not know the field at all.
source_version = parts[4].strip() if len(parts) > 4 and parts[4].strip() else version
description = parts[5].strip() if len(parts) > 5 else ""
packages.append({
"name": name,
"version": version,
@@ -856,6 +872,7 @@ class LinuxDriver(OSDriver):
"size": size,
"source": "apt",
"source_package": source_package,
"source_version": source_version,
})
return packages