managing-dependencies
npx skills add https://github.com/andrew/managing-dependencies --skill managing-dependencies
Agent 安装分布
Skill 文档
Package Management
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:
- Suggest a package that cannot be verified to exist
- Add a dependency when stdlib provides equivalent functionality
- Run install scripts without explicit user approval
- Auto-merge updates that violate the safety criteria below
If verification fails (registry unreachable, metadata incomplete, provenance missing), default to refusal and explain why.
Before Adding a Dependency
Check in order:
- Standard library – Does the language already provide this? (e.g., date parsing, HTTP, JSON)
- Transitive cost – How many dependencies does it bring? Check with
npm ls,pip show,bundle info,cargo tree - Smaller alternative – Is there a focused package that does just what’s needed?
- Inline it – Can you write 20-50 lines instead of adding a dependency?
When 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.
Evaluating a Package
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:
- High dependent count (other packages trust it)
- Few direct dependencies
- Responsive issue tracker
- Multiple maintainers
- OSI-approved license
- Provenance attestation (see below)
Less reliable signals:
- GitHub stars (gameable)
- Download counts (gameable)
- Commit frequency (stable packages don’t need commits)
- Contributor count
Red flags:
- Package less than 90 days old
- Maintainer account created recently
- Name similar to popular package (typosquatting)
- No license or non-OSI license
- Vendors copies of common dependencies
- Very few downloads for claimed purpose
- Runs code on install (postinstall scripts, setup.py)
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.
Typosquatting Patterns
Watch for these when verifying package names:
- Character substitution:
djang0vsdjango,requetsvsrequests - Character omission:
loadshvslodash,electonvselectron - Homoglyphs:
pyp1(one) vspypi(letter i), Cyrillicаvs Latina - Delimiter variation:
cross-envvscrossenvvscross_env - Scope confusion:
@angular-devkit/corevs@angulardevkit/core - Combosquatting:
lodash-js,axios-api,express-utils - Namespace confusion (Maven):
org.fasterxmlvscom.fasterxml
Always copy package names from official docs rather than typing from memory.
AI-Suggested Packages (Slopsquatting)
AI coding assistants hallucinate package names regularly. Attackers register these names with malicious code.
Before installing any AI-suggested package:
- Verify it exists:
npm view <package>orpip index versions <package> - Check age and downloads: Very new + few downloads = suspect
- Cross-reference docs: Does the framework’s official docs mention it?
- Check for typosquatting: See patterns above
Hallucinated names are often repeatable across sessions, making them predictable targets for attackers.
Dependency Confusion
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.
Provenance and Attestation
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.
Version Constraints
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.
Lockfiles
Always commit lockfiles.
Detect ecosystem:
package-lock.json/yarn.lock/pnpm-lock.yaml/bun.lockâ npmGemfile.lockâ BundlerCargo.lockâ Cargopoetry.lock/uv.lock/requirements.txtwith hashes â Pythongo.sumâ Go
In 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:
- Watch for changes to
resolvedURLs (lockfile injection attack) - Large diffs can hide malicious additions
- Unexpected registry URL changes are red flags
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
Security Audits
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 ./...
When Vulnerability Has No Patch
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:
- Override transitive – Force newer version of vulnerable transitive dependency
- Fork and patch – Apply security fix to fork, reference fork in manifest
- Remove dependency – Find alternative or inline the functionality
- Accept risk – Document why it’s not exploitable in your context
Vendoring
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.
Check Licenses
# 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.
Install Scripts
Package managers execute code during install – a common attack vector:
- npm:
preinstall,postinstallin package.json - pip:
setup.pyruns during install - Ruby:
extconf.rbfor native extensions
Audit 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.
GitHub Actions
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.
Automated Updates
| 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):
- Patch updates only (not minor/major)
- Dev dependencies only
- Established, trusted packages
- All CI checks pass
- Package available 3+ days (catches quick reverts of bad releases)
Never auto-merge:
- Major version updates
- Production dependencies
- New packages not yet in your codebase
- Security-sensitive packages (crypto, auth)
Example Renovate config for conservative auto-merge:
{
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"matchDepTypes": ["devDependencies"],
"automerge": true,
"minimumReleaseAge": "3 days"
}
]
}
API Reference
ecosyste.ms
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
Risk Thresholds
| 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 |
Development Distribution Score
Found at .repo_metadata.metadata.development_distribution_score. Measures commit distribution across contributors (0-1).
- <0.15 – Single contributor dominance (high bus factor)
- 0.15-0.5 – Moderate distribution
- >0.5 – Well distributed
OpenSSF Scorecard API
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>
deps.dev API
Alternative for dependency graph analysis:
curl -s "https://api.deps.dev/v3/systems/<ecosystem>/packages/<package>" | jq
Ecosystems: npm, pypi, cargo, go, maven, nuget
About This Skill
- Repository: https://github.com/andrew/managing-dependencies
- Author: Andrew Nesbitt (https://nesbitt.io)
- License: CC0-1.0