fix(scanner): resolve Python CVEs against uv.lock/poetry.lock, not pyproject floor#80
Merged
Merged
Conversation
…project floor depsight queried OSV with the declared pyproject range floor for direct Python deps (e.g. jinja2>=3.1 resolves to the 3.1 floor), producing false positives when the lockfile actually resolves a safe version. Mirror the npm lockfile-resolution pattern (PR #79) for Python: parse uv.lock and poetry.lock [[package]] blocks, query OSV with the resolved version, fall back to the manifest floor when no lockfile lists the dep. PEP 503 name normalization is applied on both sides of the lookup so my_package and my-package match. Clears scaffoldkit's 6 floor false positives (jinja2, pydantic). The negative control keeps genuinely vulnerable resolved versions flagged. Refs: agent-tasks 2e68c0ab Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review finding: parsePythonLockfileContents used last-match-wins and stayed in the block across all sub-tables. poetry.lock writes sub-tables after the main table, so a [package.dependencies] constraint keyed literally `name` or `version` (e.g. `version = ">=2.0"`) overwrote the captured block, queried OSV with a garbage version, and silently hid the package's real CVEs. Switch to first-match-wins (the main table lists name/version before any sub-table). uv.lock inline-table arrays were already safe via the `^` anchor. Adds two negative-control tests: poetry.lock sub-table keys named name/version must not corrupt the block, and uv.lock inline-table dependency entries must not register as standalone packages. Refs: agent-tasks 2e68c0ab
LanNguyenSi
added a commit
that referenced
this pull request
Jun 25, 2026
Bump version 0.5.0 -> 0.5.1 and add the CHANGELOG entry for the changes since v0.5.0: a scanner-correctness pass that matches CVEs against the resolved lockfile version instead of the manifest floor for npm (#79) and Python (uv.lock / poetry.lock, #80), plus a dashboard hero screenshot in the README (#81). Refs: depsight v0.5.1 scanner-correctness pass Co-authored-by: Lan Nguyen Si <nguyen-si@publicplan.de> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extend depsight's lockfile-resolved CVE matching (shipped for npm in #79) to Python. The scanner now queries OSV with the version resolved in
uv.lock/poetry.lockinstead of the declaredpyproject.tomlrange floor, and falls back to the floor when no lockfile lists the dependency.Why
For direct Python deps depsight sent the declared semver floor to OSV (e.g.
jinja2>=3.1queried3.1), so any advisory above the floor but at or below the resolved version showed up as a false positive. scaffoldkit reported 6 such CVEs (jinja2 x5, pydantic x1) while itsuv.lockresolves jinja2 3.1.6 and pydantic 2.13.4, both at or above every applicable fix. This is the Python tail of the npm fix in #79.How
lib/manifests/python.ts:normalizePythonPackageName(PEP 503 canonical form),discoverPythonLockfilePaths(root plus co-located lockfiles),parsePythonLockfileContents(one[[package]]TOML state machine covering both uv.lock and poetry.lock, lowest-version-wins, bounded to the main table so sub-tables cannot corrupt it), andfetchPythonLockfileResolutions.lib/cve/osv.ts: the python branch ofcollectDepsparallel-fetches the lockfile resolutions with a.catch(() => new Map())fail-safe (a lockfile error degrades to the floor, never aborts the scan) and uses the resolved version or the floor per dep, keyed on the canonical name.No new runtime dependency: parsing is regex line-by-line, matching the existing convention (rust.ts). No schema migration.
Tests
tests/unit/python-lockfile.test.ts(new): pure-parser unit tests for PEP 503 normalization, lowest-version-wins, partial blocks, plus two negative controls (poetry.lock sub-table keys namedname/versiondo not corrupt the block; uv.lock inline-table dependency arrays do not register as standalone packages).tests/unit/osv-lockfile.test.ts: a python block mirroring the npm scenarios, including a resolved-vulnerable negative control that keeps a genuinely vulnerable resolved version flagged.tsc --noEmitclean, lint clean, 250/250 tests pass.Scope
Python only. yarn.lock / pnpm-lock.yaml are carved out to follow-up
3d20798a(no repo in the corpus uses them; pnpm would need a YAML parser decision).Verification after deploy
Rescan scaffoldkit: expect 6 to 0. Negative control: repos whose lockfile resolves a genuinely vulnerable version stay flagged.
Refs: agent-tasks 2e68c0ab