dependency-vulnerability-audit
verifiedd6f1b490-8f85-47d9-9d07-7af956a25371
Use when auditing dependencies for security issues — run pip-audit/npm audit/osv-scanner, triage by reachability and severity, and upgrade or pin without breaking the build.
Metadata
Skill file
# Dependency Vulnerability Audit
**Use when** auditing dependencies for known vulnerabilities, triaging findings, and deciding how to remediate. Not every finding is exploitable — triage by reachability and severity.
## Running the Scanners
```bash
# Python
pip install pip-audit
pip-audit # audits the current environment
pip-audit -r requirements.txt # audit a requirements file
pip-audit --format=json # machine-readable output
# JavaScript/Node
npm audit
npm audit --json
npm audit fix # auto-fix non-breaking (be careful)
# Go / Rust / general (OSV)
# https://github.com/google/osv-scanner
osv-scanner --lockfile=go.mod
osv-scanner --lockfile=Cargo.lock
osv-scanner -r . # scan recursively for lockfiles
# GitHub Dependabot (automatic)
# Enable in repo: Settings > Security > Dependabot alerts
# And Dependabot security updates to auto-open PRs
```
## Reading the JSON Output
```json
// pip-audit --format=json (abridged)
{
"dependencies": [
{
"name": "requests",
"version": "2.27.1",
"vulns": [
{
"id": "GHSA-xxxx-xxxx",
"aliases": ["CVE-2023-32681"],
"fix_versions": ["2.31.0"]
}
]
}
]
}
```
Key fields:
| Field | Meaning |
|-------|---------|
| `id` / `aliases` | Vulnerability identifier (CVE/GHSA) |
| `fix_versions` | Versions that patch it — upgrade target |
| `version` | Your current (vulnerable) version |
## Triaging: Severity × Reachability
Not every finding needs immediate action. Classify each:
```text
1. Is the vulnerable code PATH actually reachable in your app?
2. Is the dependency direct, transitive, or dev-only?
3. What's the severity × exploitability?
```
### The triage matrix
| Finding | Action |
|---------|--------|
| Direct dep, critical, reachable | **FIX NOW** — upgrade immediately |
| Transitive, critical, reachable | Upgrade the direct parent; pin if no fix |
| Direct dep, high, not reachable | Schedule upgrade; track it |
| Dev-only (test/build) | Lower priority; still fix when convenient |
| Transitive, low, not reachable | Document and track; don't panic |
### Checking reachability
```bash
# Is the vulnerable function actually called?
grep -rn "requests.post" src/ # the vulnerable function name
grep -rn "import requests" src/ # is the package even imported?
```
```python
# pip-audit shows a vuln in a transitive dep — find the chain
pip install pipdeptree
pipdeptree -r -p <vulnerable-package>
# Shows which of YOUR direct deps pulled it in
```
## The Upgrade Playbook
```bash
# 1. Try the direct upgrade to the fix version
pip install "requests>=2.31.0"
# 2. Update the lockfile
pip freeze > requirements.txt # or: pip-compile requirements.in
# 3. Run the full test suite
pytest tests/ -x
# 4. If tests pass, commit
git add requirements.txt
git commit -m "fix(deps): upgrade requests to 2.31.0 (CVE-2023-32681)"
```
### When the upgrade breaks the build
```bash
# 1. Check what broke
pytest tests/ -x -v # see the failure
# 2. Options:
# a. Upgrade to an INTERMEDIATE version
# b. Patch your code to work with the new version
# c. Pin/vendor if NO fix exists (last resort)
# 3. NEVER just revert and ignore — the vuln is still there
```
## Pinning and Vendoring (when no fix exists)
```text
When a dependency has a known vuln and NO fixed version:
1. Pin to the latest version (still vuln, but at least known)
2. Vendor the dependency and patch it locally
3. Isolate the vulnerable code behind a guard
4. Track the upstream issue and re-evaluate when fixed
```
```bash
# Pin exact version (no auto-upgrade)
# requirements.txt
requests==2.28.2 # == means exact pin
# Vendor a patched copy
pip download requests==2.28.2 -d vendor/
# Apply a local patch, and import from vendor/
```
## Dependabot Configuration
```yaml
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
ignore:
- dependency-name: "requests"
versions: ["<2.31.0"] # ignore known-bad versions
```
## Guardrails
- **Never blind mass-upgrade.** `pip install -U everything` is how you break the build in production.
- **Don't ignore transitive findings because they're "indirect".** They can be just as exploitable.
- **Don't revert a security upgrade because a test broke.** Fix the test or the code, not the security.
- **Verify the fix version actually fixes the CVE.** Read the advisory, don't assume.
- **Track ALL findings**, even the ones you defer. A triage without tracking is amnesia.
## Pitfalls
| Pitfall | Fix |
|---------|-----|
| Blind mass-upgrade breaking the build | Upgrade one dep at a time, run tests between |
| Ignoring transitive vulns ("it's indirect") | `pipdeptree -r` to find the chain; it's still your exposure |
| Reverting the upgrade because tests broke | Fix the code to work with the new version |
| Trusting `npm audit fix` blindly | It can change behavior; review the diff |
| Not tracking deferred findings | File an issue / add to a security backlog |
## Verify / Checklist
- [ ] Scanner run (`pip-audit` / `npm audit` / `osv-scanner`)
- [ ] Each finding triaged (severity × reachability × direct/transitive/dev)
- [ ] Reachable critical findings upgraded to the fix version
- [ ] Lockfile updated and committed
- [ ] Full test suite passes after each upgrade
- [ ] Deferred findings tracked in the backlog
- [ ] No `==` pins on vulnerable versions left un-remediated
Attached files
No attached files.