Every npm install or pip install you run pulls code from strangers into your production systems. ENISA's Technical Advisory for Secure Use of Package Managers (March 2026) breaks dependency management into four stages: selecting, integrating, monitoring, and mitigating. Here's how to implement each stage with specific tools and configurations.
The Problem: Expanding Your Attack Surface
Your application's attack surface isn't just the code your team writes. When you add a package, you're adding its entire dependency tree—often hundreds of transitive dependencies you never explicitly chose. A vulnerability in any one of them becomes your vulnerability.
Package managers extend your software supply chain in ways that traditional security controls don't catch. Your SAST tools scan your code, but they don't evaluate whether that logging library you added last week is maintained by a single developer who hasn't committed in eight months. Your network monitoring catches unusual outbound connections, but it doesn't flag when a dependency update introduces a new network call to an unfamiliar domain.
The four-stage framework ENISA outlines gives you checkpoints throughout the dependency lifecycle, not just at initial selection.
Preparing for Implementation
Tooling Requirements:
- A software composition analysis (SCA) tool (Snyk, Dependabot, or OWASP Dependency-Check)
- Your package manager's lock file committed to version control (
package-lock.json,Pipfile.lock,go.sum) - CI/CD pipeline with the ability to fail builds on policy violations
- Access to your package registry (npm, PyPI, Maven Central, or internal mirror)
Policy Decisions:
- Minimum acceptable package health score (stars, recent commits, maintainer count)
- Maximum acceptable vulnerability severity in dependencies (typically: zero critical, zero high)
- License restrictions (GPL, AGPL, or custom licenses your legal team flags)
- Whether you'll allow packages with single maintainers
Team Alignment:
- Developers need write access to your dependency policy file
- Security team needs notification on new dependency additions
- DevOps needs authority to block deployments on SCA failures
Step-by-Step Implementation
Stage 1: Selecting Packages
Before adding any dependency, evaluate it against concrete criteria.
Create a Dependency Evaluation Checklist (store this in your wiki or as a GitHub issue template):
- [ ] Package has >1,000 weekly downloads (adjust for your ecosystem)
- [ ] Last commit within 90 days
- [ ] At least 2 active maintainers
- [ ] No critical or high vulnerabilities in latest version
- [ ] License compatible with our policy (check LICENSE file)
- [ ] Dependency tree <50 transitive dependencies
- [ ] No dependencies on packages with <100 weekly downloads
Automate the License Check with a tool like license-checker (npm) or pip-licenses (Python):
# npm example
npx license-checker --onlyAllow "MIT;Apache-2.0;BSD-3-Clause"
# Python example
pip-licenses --format=json --fail-on="GPL"
Add this to your pre-commit hooks or PR checks.
Review the Dependency Tree Before Installing:
# npm - see what you're actually adding
npm install --dry-run package-name
# Python - check dependencies first
pip show package-name
pip install --dry-run package-name 2>&1 | grep "Would install"
Stage 2: Integrating Packages
Lock down exactly what enters your codebase.
Use Lock Files Religiously. Never commit code without committing the corresponding lock file. This ensures everyone—including your CI/CD system—installs identical dependency versions.
Configure Your Package Manager for Security:
For npm, add this to .npmrc:
audit=true
audit-level=moderate
ignore-scripts=true
For Python, use pip-audit in your requirements workflow:
pip install pip-audit
pip-audit --requirement requirements.txt --fix
Set Up Registry Scanning. If you run an internal package mirror (Artifactory, Nexus), configure it to scan packages before they're available to developers:
- Enable vulnerability scanning on upload
- Block packages with critical vulnerabilities
- Require manual approval for packages from new publishers
Create a CI Check That Fails on Dependency Changes Without Security Review:
# GitHub Actions example
- name: Check for new dependencies
run: |
git diff origin/main -- package-lock.json | grep '"version"' && \
echo "New dependencies detected - security review required" && exit 1
Stage 3: Monitoring Dependencies
Vulnerabilities appear in existing dependencies constantly. You need automated, continuous monitoring.
Configure Automated Scanning in CI/CD:
# Example with Snyk
- name: Run Snyk to check for vulnerabilities
run: snyk test --severity-threshold=high --fail-on=all
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Set Up Scheduled Scans (not just on commits):
# Daily dependency scan
on:
schedule:
- cron: '0 9 * * *' # 9 AM daily
Monitor Package Health, Not Just Vulnerabilities. Create a weekly job that checks:
# Check for deprecated packages
npm outdated --depth=0
# Check for packages with no recent updates
# (custom script checking last commit date via registry API)
Subscribe to Security Advisories for your critical dependencies. For npm packages, enable GitHub's Dependabot alerts. For others, use tools like npm-check-updates or set up RSS feeds from security mailing lists.
Stage 4: Mitigating Vulnerabilities
When vulnerabilities appear, you need a defined response process.
Create a Vulnerability Response Runbook:
Triage (within 4 hours for critical, 24 hours for high):
- Is the vulnerable code path actually used in your application?
- Is the vulnerability exploitable in your environment?
- What's the CVSS score and attack vector?
Remediate (based on severity SLA):
- Update to patched version if available
- If no patch exists, evaluate alternatives or workarounds
- If neither exists, implement compensating controls
Verify:
- Re-run SCA scan to confirm vulnerability is resolved
- Test application functionality
- Check that the fix didn't introduce new vulnerabilities
Automate Updates Where Safe:
# Dependabot config example (.github/dependabot.yml)
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
# Auto-merge patch and minor updates
versioning-strategy: increase-if-necessary
For Vulnerabilities Without Patches, Document Your Compensating Controls:
- Input validation that prevents exploitation
- Network segmentation that limits blast radius
- Runtime monitoring that would detect exploitation attempts
Validation: How to Verify It Works
Test Your Selection Stage: Try to add a package with known vulnerabilities. Your pre-commit hooks or PR checks should block it.
Test Your Integration Stage: Delete your lock file and reinstall. You should get identical dependency versions. Your CI should fail if it doesn't.
Test Your Monitoring Stage: Introduce a package with a known vulnerability (in a test branch). Your CI/CD pipeline should fail within one build cycle.
Test Your Mitigation Stage: Run a tabletop exercise: "A critical vulnerability was just published in package X. Walk through your response." Time how long each step takes.
Measure These Metrics Monthly:
- Mean time to detect dependency vulnerabilities
- Mean time to remediate (by severity)
- Percentage of dependencies with updates available
- Number of dependencies with single maintainers
Maintenance and Ongoing Tasks
Weekly:
- Review SCA scan results from automated runs
- Check for deprecated packages in your dependency tree
- Review and merge automated dependency update PRs
Monthly:
- Audit dependencies for health metrics (maintainer activity, download trends)
- Review and update your package selection criteria based on incidents
- Check for licenses that have changed in existing dependencies
Quarterly:
- Test your vulnerability response runbook with a simulation
- Review your dependency policy against new compliance requirements
- Evaluate whether your SCA tool is catching vulnerabilities that others report
When Adding New Package Ecosystems:
- Extend your tooling to cover the new package manager
- Update your selection criteria for ecosystem-specific risks
- Train developers on ecosystem-specific security practices
The four-stage framework isn't a one-time implementation. It's a continuous cycle that adapts as your dependencies—and their risks—evolve.



