Skip to content

feat: track direct vs transitive dependencies (v0.4.0)#4

Merged
pkuzco merged 1 commit into
mainfrom
feat/transitive-deps
May 26, 2026
Merged

feat: track direct vs transitive dependencies (v0.4.0)#4
pkuzco merged 1 commit into
mainfrom
feat/transitive-deps

Conversation

@pkuzco

@pkuzco pkuzco commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

The npm parser already scanned every package in package-lock.json, but treated everything as if it were a direct dep — so a CVE in a 5-level-deep transitive gave no clue which top-level package to update. This PR makes direct/transitive a first-class concept.

  • Adds isDirect: boolean and via?: string to Dependency and DependencyResult
  • npm: marks direct deps from packages[""].dependencies; runs BFS over the reverse-dep graph from package-lock.json to resolve the via direct dep for each transitive
  • PyPI: adds Pipfile.lock parser (JSON, preferred over requirements.txt when present); uses _meta.pipfile.packages to tag direct vs transitive
  • New --direct-only CLI flag and matching direct-only action input
  • Table output appends via <name> for flagged transitives so users know which direct dep to update
  • Markdown output adds a Path column (direct / via X / transitive)
  • 8 new tests; bumps version to 0.4.0

Smoke test

mkdir t && cd t && npm i express@4.18.2 lodash@4.17.21
ossrisk .

→ 69 deps detected (2 direct + 67 transitive). All transitives correctly attributed to express via the BFS path. Flagged transitives show via express in the details column.

ossrisk . --direct-only

→ 2 deps (express, lodash).

Why

Without this, a transitive CVE was both undetectable (when only package.json was used) and unactionable (when the lockfile was used, but the user couldn't tell where the vulnerable package came from). Pulling in Pipfile.lock does the same expansion for Python users.

Test plan

  • npm test — 8 new tests, 91 total, all green
  • npm run build — clean
  • Smoke test on real npm i tree confirms direct/transitive split and via accuracy
  • --direct-only correctly filters to top-level deps
  • CI runs against this branch

🤖 Generated with Claude Code

The npm parser already scanned every package in package-lock.json, but
treated them all as if they were direct deps — so a flagged CVE in a
5-level-deep transitive gave no clue which top-level package to update.

This change:

- Adds `isDirect` and `via` to Dependency / DependencyResult
- npm: marks direct deps from packages[""].dependencies; runs BFS over
  the reverse-dep graph from package-lock.json to resolve `via`
- PyPI: adds Pipfile.lock parser (JSON, preferred over requirements.txt
  when present); uses _meta.pipfile.packages to tag direct vs transitive
- New --direct-only CLI flag and direct-only action input
- Table output appends `via <name>` for flagged transitives
- Markdown output adds a Path column ("direct" / "via X" / "transitive")
- 8 new tests; bumps version to 0.4.0

Smoke-tested against a fresh `npm i express@4.18.2 lodash@4.17.21`
project: 69 deps detected, all transitives correctly attributed to
express via the BFS path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@pkuzco pkuzco merged commit f29c9eb into main May 26, 2026
2 checks passed
@pkuzco pkuzco deleted the feat/transitive-deps branch May 26, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant