Evaluates packages, manages dependencies, and addresses supply chain security for npm/pip/cargo/bundler/Go. Use for auditing packages, reviewing lockfiles, checking vulnerabilities, comparing alternatives, assessing trustworthiness.
npx claudepluginhub andrew/managing-dependencies --plugin managing-dependenciesThis skill uses the workspace's default tool permissions.
**Before suggesting any package, verify it exists on the registry.** Check that the name, maintainer, and purpose match expectations. Do not hallucinate package names.
Scans project dependencies for vulnerabilities, outdated packages, abandoned libraries, and supply chain risks across npm/Yarn/pnpm, pip/Poetry/Pipenv, Cargo, Go, and Bundler ecosystems.
Audits project dependencies from package.json, requirements.txt, go.mod, Gemfile for CVEs, outdated packages, transitive issues, licenses, and supply chain risks. Provides severity assessments, remediation suggestions, and prioritized reports.
Intercepts pip, npm, go installs to audit package identity, vulnerabilities, suspicious signals, and enforce lockfile hash pinning before execution.
Share bugs, ideas, or general feedback.
Before suggesting any package, verify it exists on the registry. Check that the name, maintainer, and purpose match expectations. Do not hallucinate package names.
Decline to:
If verification fails (registry unreachable, metadata incomplete, provenance missing), default to refusal and explain why.
Check in order:
npm ls, pip show, bundle info, cargo treeWhen in doubt, don't add. PRs that only remove dependencies are usually good PRs.
Always clarify if a dependency is for development only (--save-dev, group :development, [tool.poetry.group.dev.dependencies]) to minimize the production attack surface.
For quick checks, visit the package's registry page and repo. When you need depth:
# Any ecosystem via ecosyste.ms
curl -s "https://packages.ecosyste.ms/api/v1/registries/<registry>/packages/<package>" | jq '{
dependent_repos: .dependent_repos_count,
dependent_packages: .dependent_packages_count,
latest: .latest_release_number,
latest_release: .latest_release_published_at,
created: .first_release_published_at,
maintainers: (.maintainers | length),
repo: .repository_url,
license: .normalized_licenses,
advisories: (.advisories | length),
archived: .repo_metadata.archived
}'
# See API Reference below for full registry list
Good signals:
Less reliable signals:
Red flags:
OpenSSF Scorecard - check project security practices:
scorecard --repo=github.com/owner/repo
# Or visit: https://securityscorecards.dev
Score below 5 warrants a closer look, though small or mature projects often score lower without being risky. If Maintained or Dangerous-Workflow scores are below 3, flag as higher risk.
Watch for these when verifying package names:
djang0 vs django, requets vs requestsloadsh vs lodash, electon vs electronpyp1 (one) vs pypi (letter i), Cyrillic а vs Latin across-env vs crossenv vs cross_env@angular-devkit/core vs @angulardevkit/corelodash-js, axios-api, express-utilsorg.fasterxml vs com.fasterxmlAlways copy package names from official docs rather than typing from memory.
AI coding assistants hallucinate package names regularly. Attackers register these names with malicious code.
Before installing any AI-suggested package:
npm view <package> or pip index versions <package>Hallucinated names are often repeatable across sessions, making them predictable targets for attackers.
When using both public and private registries, attackers can publish public packages matching your internal package names with high version numbers.
Defenses:
Use scoped/namespaced packages for internal code:
# npm - attackers can't register under your scope
@yourcompany/internal-utils
# Configure scope to route to private registry
# .npmrc
@yourcompany:registry=https://your-internal-registry.com
For pip, use --index-url for private registry, --extra-index-url for PyPI:
# Correct - private registry checked first
pip install --index-url https://private.example.com/simple \
--extra-index-url https://pypi.org/simple \
mypackage
Defensively register your internal package names on public registries with placeholder packages.
Check if a package has verified build provenance:
# npm - check for attestation
npm audit signatures
# PyPI - check for Sigstore attestation
curl -s "https://pypi.org/pypi/<package>/json" | jq '.urls[0].digests'
# Look for attestation bundle in release assets
# GitHub Actions - verify with gh
gh attestation verify <artifact> --owner <org>
Trusted publishing means the package was published directly from CI (GitHub Actions, GitLab CI) without maintainer credentials. The registry verifies the source via OIDC. npm, PyPI, and RubyGems support this.
If a package has provenance, you can verify the published artifact matches the source repo and commit. Support varies by ecosystem; absence of attestation doesn't mean insecure.
Applications: Use ranges in manifest, pin via lockfile.
Libraries: Use wide ranges. Don't force consumers to upgrade.
| Ecosystem | Exact | Flexible | Patch only |
|---|---|---|---|
| npm/Cargo | 1.0.0 | ^1.0.0 | ~1.0.0 |
| Bundler | = 1.0.0 | ~> 1.0 | ~> 1.0.0 |
| pip | ==1.0.0 | ~=1.0 | |
| Go | v1.2.3 | MVS | MVS |
Bundler's ~> is commonly misread: ~> 1.0 allows 1.x (minor updates), ~> 1.0.0 allows 1.0.x (patch only).
Go uses minimal version selection - it picks the minimum version satisfying all requirements. Don't vendor specific versions or use replace directives to simulate ranges.
Always commit lockfiles.
Detect ecosystem:
package-lock.json / yarn.lock / pnpm-lock.yaml / bun.lock → npmGemfile.lock → BundlerCargo.lock → Cargopoetry.lock / uv.lock / requirements.txt with hashes → Pythongo.sum → GoIn CI, install from lockfile (don't regenerate):
npm ci # not npm install
pip install --require-hashes -r requirements.txt
bundle install --frozen
cargo build --locked
uv sync --frozen
go mod verify
Review lockfile changes in PRs:
resolved URLs (lockfile injection attack)Regenerate on conflict rather than manually merging:
# npm
rm package-lock.json && npm install
# Bundler
rm Gemfile.lock && bundle install
# Cargo
rm Cargo.lock && cargo generate-lockfile
# Poetry
rm poetry.lock && poetry lock
# uv
rm uv.lock && uv lock
Run in CI and before adding dependencies:
# npm
npm audit
# Bundler (install bundler-audit gem first)
bundle audit check --update
# pip (install pip-audit first)
pip-audit
# Cargo (install cargo-audit first)
cargo audit
# Go
govulncheck ./...
First check reachability: is the vulnerable code path actually called by your usage? Many CVEs affect features you don't use.
Options in order of preference:
Copy dependencies into your repo for airgapped environments, auditing, or when you need to patch upstream:
# Go - built-in
go mod vendor
go build -mod=vendor
# Ruby
bundle package --all
bundle install --local
# Python (pip download, then install offline)
pip download -r requirements.txt -d ./vendor
pip install --no-index --find-links=./vendor -r requirements.txt
# npm (less common, use npm-pack-all or similar)
Vendoring trades registry availability risk for increased repo size and manual update burden. Justified for airgapped builds, audited security-critical code, or long-term forks you maintain.
# npm
npx license-checker --summary
# pip
pip-licenses
# Bundler
bundle licenses
# Cargo
cargo license
Safe: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Unlicense
Review needed: LGPL, MPL-2.0
Copyleft (affects distribution): GPL, AGPL
No license = not open source - do not use.
Package managers execute code during install - a common attack vector:
preinstall, postinstall in package.jsonsetup.py runs during installextconf.rb for native extensionsAudit before allowing scripts:
# npm - install without running scripts, then review
npm install --ignore-scripts
# After reviewing, run scripts explicitly
npm rebuild
For high-security environments, consider disabling install scripts globally and allowlisting specific packages.
Actions are dependencies too. Pin to commit SHA, not tags:
# Bad - tag can be moved
- uses: actions/checkout@v4
# Good - immutable reference
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Use Dependabot or Renovate to keep pinned SHAs updated.
| Tool | Platform | Best for |
|---|---|---|
| Dependabot | GitHub only | Simple needs, free |
| Renovate | Multi-platform | Complex configs, monorepos |
| Snyk | Multi-platform | Security-first, vulnerability focus |
Safe auto-merge criteria (all must apply):
Never auto-merge:
Example Renovate config for conservative auto-merge:
{
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"matchDepTypes": ["devDependencies"],
"automerge": true,
"minimumReleaseAge": "3 days"
}
]
}
curl -s "https://packages.ecosyste.ms/api/v1/registries/<registry>/packages/<package>" | jq
Registries: npmjs.org, pypi.org, rubygems.org, crates.io, proxy.golang.org, nuget.org, repo1.maven.org, cocoapods.org, hub.docker.com
| Field | Threshold | Risk |
|---|---|---|
latest_release_published_at | >2 years ago | Possibly abandoned |
dependent_repos_count | <500 | Low adoption |
first_release_published_at | <90 days ago | Too new |
maintainers (length) | <2 | Bus factor risk |
versions_count | =1 | Immature |
repo_metadata.archived | true | Abandoned |
advisories | non-empty | Known vulnerabilities |
Found at .repo_metadata.metadata.development_distribution_score. Measures commit distribution across contributors (0-1).
curl -s "https://api.scorecard.dev/projects/github.com/<owner>/<repo>" | jq '{
score: .score,
maintained: (.checks[] | select(.name == "Maintained") | .score),
dangerous_workflow: (.checks[] | select(.name == "Dangerous-Workflow") | .score),
code_review: (.checks[] | select(.name == "Code-Review") | .score)
}'
Human-readable: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/<owner>/<repo>
Alternative for dependency graph analysis:
curl -s "https://api.deps.dev/v3/systems/<ecosystem>/packages/<package>" | jq
Ecosystems: npm, pypi, cargo, go, maven, nuget