repomatic.deps.vulnerable_deps module¶
Vulnerability audit and remediation for locked dependencies.
Backs the audit command and the fix-vulnerable-deps job: queries the
advisory sources enabled in [tool.repomatic] vulnerable-deps.sources,
unions and deduplicates their findings into VulnerablePackage
records, and (--fix) upgrades each fixable package through uv.
Two advisory sources are consulted:
uv auditqueries the PyPA Advisory Database (OSV-backed).GitHub’s Dependabot alerts query the GitHub Advisory Database (GHSA).
Coverage diverges in practice: GHSA frequently lists a CVE before the PyPA
database mirrors it, and transitive lockfile vulnerabilities sometimes only
surface in GHSA. By unioning both sources, audit catches CVEs that either
database alone would miss.
- repomatic.deps.vulnerable_deps.AUDIT_HEADER_DEFS: tuple[tuple[str, str], ...] = (('Package', 'package'), ('Version', 'version'), ('Advisory', 'advisory'), ('Fixed', 'fixed'), ('Sources', 'sources'))¶
Column definitions for the
repomatic audittable.Lives beside the rows’ domain model so the columns and the fields they render cannot drift apart; the CLI derives its
--sort-bychoices from it.
- repomatic.deps.vulnerable_deps.MIN_UV_AUDIT_JSON_VERSION = <Version('0.11.15')>¶
Minimum
uvversion exposinguv audit --output-format json.The structured JSON output landed in uv 0.11.15 as a preview feature. Below this,
uv auditemits only human-readable text, so_run_uv_auditrefuses to run rather than silently scanning nothing.
- class repomatic.deps.vulnerable_deps.AdvisorySource(*values)[source]¶
Bases:
StrEnumWhere a vulnerability advisory was detected.
Each source has a distinct upstream database and ingestion pipeline, so coverage diverges in practice (e.g., GHSA frequently lists a CVE before the PyPA Advisory Database mirrors it). Tracking the source per
VulnerablePackagelets the union deduplicate by advisory ID while still attributing each entry to the database that produced it.- UV_AUDIT = 'uv-audit'¶
Detected by
uv audit(PyPA Advisory Database, OSV-backed).
- GITHUB_ADVISORIES = 'github-advisories'¶
Detected via the repository’s Dependabot alerts (GitHub Advisory Database).
- class repomatic.deps.vulnerable_deps.VulnerablePackage(name, current_version, advisory_id, advisory_title, fixed_version, advisory_url, aliases=<factory>, sources=<factory>, source_urls=<factory>)[source]¶
Bases:
objectA single vulnerability advisory for a Python package.
- aliases: set[str]¶
Alternate identifiers for the same advisory (CVE, GHSA, PYSEC, OSV).
Advisory databases cross-reference each other: the PyPA database (via
uv audit) keys records by OSV/PYSECIDs while listing the matchingGHSA/CVEIDs as aliases, and Dependabot keys byGHSAwhile listing theCVE.collect_vulnerable_packages()unions entries whose identifier sets overlap, so a shared alias deduplicates the same advisory reported under different primary IDs by different sources.
- sources: set[AdvisorySource]¶
Advisory databases that surfaced this entry.
A set rather than a single value because the same advisory can be reported by multiple sources after deduplication. Empty only for entries built without source attribution (test fixtures); every production code path records at least one source.
- source_urls: dict[AdvisorySource, str]¶
Per-source URL pointing to the advisory page in each database.
Each source has its own canonical URL even when reporting the same advisory ID (PyPA’s
osv.devpage vs. GitHub’s/advisories/page), so the rendered table can link the source name to the database that actually surfaced it.
- repomatic.deps.vulnerable_deps.parse_uv_audit_json(output)[source]¶
Parse
uv audit --output-format jsonoutput into vulnerability records.The structured contract avoids the regex fragility of scraping human-readable lines, and exposes the advisory
aliases(cross-referenced CVE/GHSA/PYSEC IDs) that letcollect_vulnerable_packages()deduplicate the same advisory across sources.- Parameters:
output (
str) – stdout fromuv audit --output-format json.- Return type:
- Returns:
A list of
VulnerablePackageentries (empty when the audit found nothing).- Raises:
RuntimeError – when the output is unusable as JSON (empty, malformed, or carrying an unrecognized
schema.version). Raising rather than returning an empty list keeps the scanner from silently passing when the preview schema changes under it.
- repomatic.deps.vulnerable_deps.format_vulnerability_table(vulns)[source]¶
Format vulnerability data as a markdown table.
Includes a
Sourcescolumn listing the advisory databases that surfaced each entry, so reviewers can see which database (PyPA Advisory DB, GitHub Advisory DB, or both) detected the vulnerability.- Parameters:
vulns (
list[VulnerablePackage]) – List ofVulnerablePackageentries.- Return type:
- Returns:
A markdown string with a
## Vulnerabilitiesheading and table, or an empty string if no vulnerabilities are provided.
- repomatic.deps.vulnerable_deps.collect_vulnerable_packages(lock_path, repo=None, sources=None)[source]¶
Collect vulnerability advisories from all configured sources.
Queries each enabled advisory database, then deduplicates entries per package by advisory identity: two entries merge when their identifier sets (
advisory_idplusaliases) overlap, so the same advisory reported under a PYSEC/OSV ID byuv auditand a GHSA ID by Dependabot collapses into one. Merging preserves the union ofsourcesso the rendered table credits both databases when they agree.Current versions reported by
uv audittake precedence over the empty placeholder produced by the GHSA path, sinceuv auditreads the actual locked version while Dependabot alerts only carry the vulnerable range. When the GHSA path encounters a package thatuv auditdid not surface, the current version is filled in from the lock file.- Parameters:
lock_path (
Path) – Path to theuv.lockfile.repo (
str|None) – Repository inowner/repoformat. Required for theAdvisorySource.GITHUB_ADVISORIESsource; passNoneto skip it (the result then reflectsuv auditonly).sources (
list[AdvisorySource] |None) – Advisory databases to consult. Defaults to all known sources.
- Return type:
- Returns:
Deduplicated list of
VulnerablePackageentries.
- repomatic.deps.vulnerable_deps.fix_vulnerable_deps(lock_path, repo=None, sources=None)[source]¶
Detect vulnerable packages and upgrade them in the lock file.
Queries every advisory source enabled by sources (defaults to all), then upgrades each fixable package with
uv lock --upgrade-packageusing--exclude-newer-packageto bypass theexclude-newercooldown for security fixes. Also persists the exemptions inpyproject.tomlso that subsequentuv lock --upgraderuns (e.g. from thesync-uv-lockjob) do not downgrade the fixed packages back within the cooldown window.An upgrade that resolves to the versions already locked leaves the file byte-identical to how it was found, because uv writes the overrides it was handed into the lock’s
[options]table even when they change nothing. See the restore in step 5.- Parameters:
lock_path (
Path) – Path to theuv.lockfile.repo (
str|None) – Repository inowner/repoformat. Required whenAdvisorySource.GITHUB_ADVISORIESis among sources.sources (
list[AdvisorySource] |None) – Advisory databases to consult. Defaults to all known sources.
- Return type:
- Returns:
A tuple of
(has_fixes, diff_table).has_fixesisTruewhen at least one vulnerable package was upgraded.diff_tableis a markdown-formatted string with vulnerability details and version changes, or an empty string if no fixable vulnerabilities were found.
- repomatic.deps.vulnerable_deps.fetch_dependabot_alerts(repo)[source]¶
Fetch open
pip-ecosystem Dependabot alerts for a repository.Calls
GET /repos/{repo}/dependabot/alerts?state=open&ecosystem=pipvia theghCLI, then maps each alert into aVulnerablePackagetagged withAdvisorySource.GITHUB_ADVISORIES.Returns an empty list when the API is unreachable, the token lacks the
Dependabot alertspermission, or the repository has no open alerts. A network or auth failure must not break the autofix workflow: theuv auditsource is still consulted independently.- Parameters:
repo (
str) – Repository inowner/repoformat.- Return type:
- Returns:
List of
VulnerablePackageentries with a known fixed version. Alerts withoutfirst_patched_versionare skipped (no upgrade target).