Review the dependencies a branch adds or changes for known CVEs, license compliance, supply-chain risk, and unnecessary additions, across Python (pip/Poetry), JS/TS (npm/pnpm), and Java/C# (Maven/Gradle/NuGet). Produces DEPENDENCY-COMMENTS.md anchored to SEC-DEP-* rules. Use when a PR touches a manifest or lockfile, or when the user says "dependency review", "check dependencies", "SCA", "supply chain", "are these packages safe", "license check".
Installs into .claude/skills of the current project.
Are you the author of Dependency Review?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/eranw2000-dependency-review)
---
model: fable
name: dependency-review
description: Review the dependencies a branch adds or changes for known CVEs, license compliance, supply-chain risk, and unnecessary additions, across Python (pip/Poetry), JS/TS (npm/pnpm), and Java/C# (Maven/Gradle/NuGet). Produces DEPENDENCY-COMMENTS.md anchored to SEC-DEP-* rules. Use when a PR touches a manifest or lockfile, or when the user says "dependency review", "check dependencies", "SCA", "supply chain", "are these packages safe", "license check".
---
# Dependency / Supply-Chain Review
You check what a change pulls into the supply chain. Anchored to SEC-DEP-01 to 05 in
`standards/security-standards.md` and banded per `standards/severity-taxonomy.md`.
## Inputs
1. The diff, focused on manifests and lockfiles: `requirements*.txt`, `pyproject.toml`,
`poetry.lock`, `package.json`, `package-lock.json`, `pnpm-lock.yaml`, `pom.xml`,
`build.gradle*`, `*.csproj`, `packages.lock.json`.
2. The available SCA tools: `pip-audit`, `npm audit` / `pnpm audit`, OWASP dependency-check,
`osv-scanner` if present. Run what is installed; if none is, say so and fall back to checking
versions against the OSV/GitHub advisory data you can reach.
## Process
### 1. Diff the dependency set
List every added, removed, and version-changed dependency, direct and transitive where the
lockfile shows it. Separate direct adds (a human chose them) from transitive churn (pulled by a
direct change).
### 1b. Every added name exists (SEC-DEP-05)
Run the direct adds from step 1 through the registry check before anything else, because a
name that was never published has no CVEs, no license and no download count, so every later
step would report it clean:
```bash
ci/check-package-exists.py pypi:<name> npm:<name> # the added names
ci/check-package-exists.py --requirements requirements.txt --package-json package.json
```
Exit 1 is a Blocker: the name is not on the registry, which is how an invented name looks
before an attacker registers it. A name listed as young, or listed as installed from a
configured registry, goes to step 6 as a Question; confirm the second kind on that registry.
Step 6 also asks whether any added name is one edit away from a well-known package; that one
is a reviewer's judgment. Exit 2 means a registry did not answer: record the name
as unconfirmed in the report's Limits section rather than as clean. Exit 2 also covers a
registry reply that is not a document about that name, and a manifest the script could not
read. For an `-r` or `-c` include at a URL, fetch that file and run the script on it.
### 2. Known vulnerabilities (SEC-DEP-01)
Run the SCA tool for each ecosystem present. For each finding, record the advisory ID
(CVE/GHSA/OSV), the affected version, the fixed version, and whether the vulnerable code path is
reachable from this project. Band by reachability and impact, not raw score: a critical CVE on a
reachable path is a Blocker; the same in an unused transitive dep is a Major or Nit. Always name
the fixed version in the fix.
### 2b. Known exploited (SEC-DEP-04)
Take the advisory ids from step 2 and run them through the catalogue:
```bash
ci/check-kev.py CVE-... CVE-... # ids you already have
pip-audit -r requirements.txt -f json | ci/check-kev.py --pip-audit -
```
A hit outranks its CVSS score. Record the CISA due date and the ransomware flag in the finding,
and band it per SEC-DEP-04: Blocker on a reachable path, Major with a dated reason off one.
The script exits 2, never 0, when it cannot read the catalogue. Treat that as "not checked" and
say so in the report's Limits section rather than letting an unreachable feed read as clean.
### 3. Pinning and provenance (SEC-DEP-02)
Are new dependencies pinned with a committed lockfile? Do they come from the official registry,
or from an arbitrary git ref / URL / tarball (supply-chain risk)? An unpinned or
unofficial-source dependency is a Major.
### 4. License compliance
Check each new dependency's license against the project's own allowlist where it has one, and
where it has none, report the license and raise the question rather than assuming. Copyleft (GPL/AGPL) in a
proprietary product, or a missing/unknown license, is a Major or Question depending on the
company policy. State the license you found.
### 5. Necessity and footprint (SEC-DEP-03)
Does a new dependency duplicate something already in the tree or in the standard library? Is it
a large dependency added for a tiny need? Raise as a Question ("do we need this, or can we use
X already present?"), not an automatic finding.
### 6. Typosquat / integrity smell
Flag names that are near-misses of popular packages, brand-new packages with very low download
counts pulled as direct deps, or a maintainer/scope change on an existing dep. These are
Questions that ask for a human look.
### 7. Write DEPENDENCY-COMMENTS.md
```markdown
# DEPENDENCY REVIEW, <project>
**Branch:** <branch>
**Reviewed at:** <YYYY-MM-DD HH:MM>
**Ecosystems:** <python|node|java|dotnet present>
**Verdict:** BLOCK | APPROVE WITH FIXES | APPROVE
## Dependency changes
- ADD <name>@<version> (direct), license <x>, <one line on why>
- CHANGE <name> <old> -> <new>
- (transitive churn summarized)
## Package names (SEC-DEP-05)
<"all on the registry", or each name not found, each young name with its first-published
date, each name installed from a configured registry and whether you confirmed it there, and
any name left unconfirmed because the run exited 2 (a registry gave no usable answer, or a
manifest or an include at a URL could not be read)>
## Known exploited (SEC-DEP-04)
<CVE, or "none listed", or "not checked: the catalogue was unreachable">
<for each: CISA due date, ransomware flag, reachable yes/no, and the band that follows>
## Blockers
### B-1: <name>@<version>, <CVE/GHSA>
- **Rule:** SEC-DEP-01
- **Advisory:** <id>, affects <range>, fixed in <version>
- **Reachable:** yes/no/unknown, with the call path if known.
- **Fix:** upgrade to <version> (or remove / replace).
## Majors / Questions
(pinning, license, necessity, typosquat smell)
## Limits
Which ecosystems had no SCA tool available, so were checked only by version lookup.
```
### 8. Verdict
- **BLOCK**: a known-vulnerable dependency on a reachable path with a fix available, or a
name that is not on its registry (SEC-DEP-05).
- **APPROVE WITH FIXES**: Majors (unpinned, license, non-reachable CVE).
- **APPROVE**: clean, with the limits section naming any ecosystem not fully scanned.
## Guardrails
- Do not upgrade dependencies yourself unless the user asks; recommend the target version.
- Name the fixed version for every CVE; "upgrade it" without a version is not a fix.
- Band by reachability, not by CVSS alone. Say when reachability is unknown.
- Respect `baseline.yml` for accepted advisories (with owner + expiry).