DEV Community

Cover image for 179 security alerts, 24 upgrades: triaging vulnerable Python dependencies
Versyka
Versyka

Posted on AI-assisted

179 security alerts, 24 upgrades: triaging vulnerable Python dependencies

On October 15, a scan of an older Python data project came back with 179 security alerts.

That number makes people close the tab. But it's counting the wrong thing.

Alerts are per advisory, not per package

Each alert is one advisory against one package version. A package that's been pinned for a few years collects them:

Package Advisories One upgrade
nltk 40 3.8.1 → 3.10.3
mlflow 23 2.12 → 3.15
mistune 15 3.2.0 → 3.3.0
Pillow 12 9.5.0 → 12.3.0

Four upgrades close 90 alerts. Group all 179 by package and they come down to 24 upgrades.

Step 1: group by package

You can do this yourself with OSV.dev data. With pip-audit:

pip-audit -r requirements.txt -f json -o audit.json
python - <<'EOF'
import json, collections
deps = json.load(open("audit.json"))["dependencies"]
rows = []
for d in deps:
    if d.get("vulns"):
        fixes = [v for x in d["vulns"] for v in x.get("fix_versions", [])]
        rows.append((len(d["vulns"]), d["name"], d["version"], max(fixes) if fixes else "no fix yet"))
for n, name, ver, fix in sorted(rows, reverse=True):
    print(f"{n:4}  {name} {ver} -> {fix}")
EOF
Enter fullscreen mode Exit fullscreen mode

Sorted by count, the top of that list is your morning.

Step 2: push down what you never import

A surprising share of old dependencies aren't used by your app code at all: transitive leftovers, a tool someone tried once. They still matter, but they're not first.

A rough check for Python:

grep -RhoE "^\s*(import|from)\s+[a-zA-Z_][a-zA-Z0-9_]*" --include=*.py . \
  | awk '{print $2}' | sort -u
Enter fullscreen mode Exit fullscreen mode

Compare that with your requirements (watch for packages whose import name differs, like Pillow → PIL).

Step 3: flag the big jumps

Pillow 9.5.0 → 12.3.0 is three majors. That's not a version bump. Read the release notes before you merge it, and expect code changes.

Step 4: ship them as few PRs as possible

The non-breaking bumps can go together in one pull request per repository. The breaking ones each deserve their own, with tests run before and after.

That's the triage Versyka automates: alerts grouped into upgrades with every advisory linked, a "not imported" tag for packages your code never imports, a "big jump" tag for multi-major upgrades, version bumps as one tested PR per repository, and breaking upgrades rewritten and tested as migration PRs.

Top comments (0)