Skip to content

Repository files navigation

pygen

CI Python License: GPL v3 Checked with mypy

Package vulnerability triage and remediation for Debian-family systems. pygen takes the packages actually installed on a host, compares their versions against published security notices to find what is genuinely unpatched, prioritises each issue with CVSS + EPSS + CISA KEV, assigns it to an owning team, and produces ready-to-run remediation reports.

It does real version-aware detection (a faithful pure-Python port of dpkg's version-comparison algorithm), not CVE-id string matching, so it does not over-report issues that are already fixed in the version you run.


Why

Generic "match the CVE feed" tooling over-reports: a CVE can appear in a feed but already be fixed in the exact package version installed. pygen answers the question an operator actually has:

Given what is installed on this host and this release, what is genuinely unpatched, how urgent is each item, who owns it, and what is the exact command to fix it?

Features

  • Version-aware detection. A package is vulnerable only when its installed version is strictly older than the fixed version for its release, decided by a faithful port of dpkg verrevcmp (epochs, ~ pre-release ordering, leading-zero normalisation). See version.py.
  • Risk-based prioritisation. Combines CVSS (severity), EPSS (30-day exploit probability) and CISA KEV (confirmed active exploitation) into a transparent 0-100 score and an SSVC-style action: Act / Attend / Track.
  • Ownership routing. A small YAML policy turns a flat CVE list into per-team work queues.
  • Reports for humans and machines. Markdown and HTML (per-owner remediation plans) plus JSON and CSV for pipelines and ticketing.
  • Offline / hermetic. Every feed can be a local snapshot; --offline never touches the network, so demos, CI and air-gapped hosts are deterministic.
  • Hardening companion. A CIS-aligned check of SSH, AppArmor and auditd configuration with pass/fail and concrete remediation.

Architecture

 dpkg-query ─┐
             │            ┌── EPSS (FIRST.org) ──┐
 USN feed ───┼─► match ──►│   KEV (CISA)         ├─► prioritise ─► assign ─► report
             │   (version │   CVSS               │   (SSVC-ish)    owner    md/html/
 release ────┘   compare) └──────────────────────┘                         json/csv
Stage Module
Collect installed packages / release sources/dpkg.py
Version comparison (core algorithm) version.py
Ingest security-notice database sources/usn.py
Match installed vs. fixed matching.py
Enrich (EPSS / KEV / CVSS) sources/ + prioritize.py
Assign owners ownership.py
Reports report/
Pipeline orchestration scan.py
Hardening companion hardening/

See docs/architecture.md for the data flow in detail.

Install

git clone https://github.com/PhinehasNarh/pygen.git
cd pygen
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"

Quickstart

Offline demo (works anywhere, no network)

Bundled example data shows a full, enriched report immediately:

pygen scan --offline --release jammy \
  --snapshot   examples/sample-dpkg.txt \
  --usn-db     examples/feeds/usn.json \
  --epss-file  examples/feeds/epss.json \
  --kev-file   examples/feeds/kev.json \
  --cve-file   examples/feeds/cve.json \
  --owners     examples/owners.yaml \
  --format md

Live, on a Debian/Ubuntu host

# Scan the running system against live feeds (cached for 24h).
pygen scan --owners examples/owners.yaml --format html --output report.html

# Or triage a remote host from your laptop by copying its package list:
dpkg-query -W -f='${Package}\t${Version}\t${Architecture}\n' > host.tsv   # on the server
pygen scan --snapshot host.tsv --release jammy --format md                # anywhere

The process exits 2 when any finding is Act, so it slots straight into CI or cron as a gate.

Sample output

## web-platform (5)

| Action | Risk | Sev | Package | Installed | Fixed | CVE | USN | EPSS | KEV |
|--------|-----:|-----|---------|-----------|-------|-----|-----|-----:|-----|
| Act    | 97.1 | high | `apache2` | 2.4.52-1ubuntu4.0 | 2.4.52-1ubuntu4.1 | CVE-2022-31813 | USN-5678-1 | 0.94321 | yes |
| Attend | 44.5 | high | `libssl3` | 3.0.2-0ubuntu1.5  | 3.0.2-0ubuntu1.8  | CVE-2023-0286  | USN-6001-1 | 0.0044  | no  |

Remediation:
\`\`\`bash
sudo apt-get install --only-upgrade apache2
sudo apt-get install --only-upgrade libssl3
\`\`\`

An HTML version is at examples/sample-report.html.

How prioritisation works

Each finding gets a transparent risk score in 0-100:

risk = cvss * 6   (0-60, intrinsic severity)
     + epss * 30  (0-30, 30-day exploit probability)
     + 10 if in CISA KEV

and an action by a simplified SSVC rule:

  • Act when in KEV, or EPSS ≥ 0.20, or CVSS ≥ 9.0, or severity critical.
  • Attend when EPSS ≥ 0.05, or CVSS ≥ 7.0, or severity high.
  • Track otherwise.

Every input is visible in the report, so a priority can always be explained rather than trusted blindly.

Triage effort saved

The summary reports estimated manual triage time eliminated: findings x minutes-per-finding (default 15 min, configurable with --minutes). Each finding otherwise costs an analyst time to look up the CVE, find the fixed version, confirm exposure and route it. The constant is stated up front so the figure is auditable, not hand-waved.

Hardening companion

# Evaluate a config file (portable, no host access):
pygen hardening --sshd-config /etc/ssh/sshd_config

# Or collect SSH + AppArmor + auditd facts from the live host:
pygen hardening

Checks a representative CIS subset (SSH daemon hardening, AppArmor enforcement, audit daemon) and prints pass/fail with remediation. Exits 1 on any failure.

Development

ruff check src tests      # lint
black --check src tests   # format
mypy                      # strict type-check
pytest --cov=pygen        # tests (hermetic, no network)
# or run all of the above:
./scripts/run_checks.sh

CI runs the same gate on Python 3.10-3.12 (see .github/workflows/ci.yml).

Roadmap

  • OVAL-based matching using published OVAL feeds.
  • SLA / vulnerability-aging tracking with breach alerts.
  • --diff mode to report only newly-introduced findings between scans.

Security

All feed data is treated as untrusted input: fixed-argument subprocess calls (no shell), cache-key sanitisation, strict CVE-id validation before URL use, Jinja2 autoescaping, and yaml.safe_load only. Reviewed with bandit (no medium/high findings) and pip-audit. See SECURITY.md.

License

GPL-3.0-or-later.

About

pygen is a package vulnerability triage and remediation for debian family systems

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages