git clone https://github.com/Intense-Visions/harness-engineering
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/security-dependency-auditing" ~/.claude/skills/intense-visions-harness-engineering-security-dependency-auditing-0c9415 && rm -rf "$T"
agents/skills/codex/security-dependency-auditing/SKILL.mdDependency Auditing
Your application is 90% third-party code -- scan it for known vulnerabilities, lock it to exact versions, and have a strategy for when a critical CVE drops on a Friday afternoon
When to Use
- Setting up dependency vulnerability scanning for a project
- Responding to a CVE in a direct or transitive dependency
- Designing an update strategy that balances security with stability
- Auditing lockfile integrity to detect supply chain tampering
- Evaluating SCA (Software Composition Analysis) tools
- Meeting compliance requirements for dependency management
Threat Context
The 2021 Log4Shell vulnerability (CVE-2021-44228, CVSS 10.0) affected nearly every Java application through the ubiquitous Log4j library -- a single JNDI lookup string in any user input could trigger remote code execution. Organizations with dependency scanning and automated update pipelines patched within hours; those without took weeks or months, with some remaining vulnerable for over a year. The 2018 event-stream npm package compromise inserted a cryptocurrency-stealing payload targeting the Copay Bitcoin wallet into a transitive dependency used by millions of projects. The 2022 node-ipc sabotage by its own maintainer demonstrated that even trusted, widely-used packages can become intentionally malicious (the maintainer added code to overwrite files on systems with Russian or Belarusian IP addresses). Your application's security is the security of its weakest dependency, and most applications have hundreds of transitive dependencies that no developer has ever reviewed.
Instructions
-
Use lockfiles and verify their integrity.
(npm),package-lock.json
,yarn.lock
,pnpm-lock.yaml
(Python),Pipfile.lock
(Go),go.sum
(Ruby). Lockfiles pin exact versions and record integrity hashes (SHA-512 for npm, SHA-256 for Go). RunGemfile.lock
(notnpm ci
) in CI to install exactly what the lockfile specifies.npm install
deletesnpm ci
and installs from scratch using only the lockfile, failing ifnode_modules
andpackage-lock.json
are out of sync. This ensures reproducible builds and prevents silent dependency resolution changes.package.json -
Scan dependencies in CI. Run
,npm audit
,pip-audit
, or a commercial SCA tool (Snyk, Sonatype Nexus, Dependabot, Trivy) in every CI pipeline. Fail the build for critical and high severity vulnerabilities -- these represent known exploitable weaknesses with public proof-of-concept code. Triage medium and low severity vulnerabilities on a weekly cadence. Do not let vulnerability counts accumulate into the hundreds, which creates alert fatigue and masks genuine threats.bundle audit -
Understand transitive dependencies. A direct dependency with zero vulnerabilities can bring in a transitive dependency with critical vulnerabilities.
shows the full dependency tree.npm ls --all
scans the entire tree, not just direct dependencies. Audit the depth of your dependency tree: if your application has 5 direct dependencies but 500 transitive dependencies, your actual attack surface is 500 packages, each of which can introduce vulnerabilities, malicious code, or supply chain risk.npm audit -
Design an update strategy. Semantic versioning provides a heuristic: patch versions (1.2.x) are safe to auto-merge after CI passes, minor versions (1.x.0) are usually safe but need testing, major versions (x.0.0) require manual review for breaking changes. Use Dependabot or Renovate to automate PR creation for dependency updates. Group minor/patch updates into weekly PRs to reduce PR noise. Review major updates individually. Set auto-merge for patch updates that pass CI to reduce the backlog.
-
Maintain a vulnerability response playbook. When a critical CVE drops: (1) identify if the vulnerable package is in your dependency tree (
,npm ls <package>
,pip show <package>
), (2) check if a patched version exists, (3) if yes, update and deploy immediately, (4) if no patch exists, evaluate mitigations (WAF rules, input validation, feature flags to disable affected functionality), (5) monitor for exploit attempts in logs. The playbook should be documented, tested, and executable by any on-call engineer, not just the security team.go mod graph | grep <package> -
Pin and minimize dependencies. Fewer dependencies mean less attack surface. Before adding a dependency, evaluate: can we implement this functionality in 50 lines of code? Is the package actively maintained (commits in the last 6 months)? How many transitive dependencies does it bring? What is the package's security history? Use tools like bundlephobia (npm) to assess the weight. A 2-line utility function does not justify adding a package with 40 transitive dependencies.
Details
SCA Tool Comparison
| Tool | Type | Auto PRs | CI Integration | Best For |
|---|---|---|---|---|
| npm audit | Built-in | No | Yes | Basic JS/TS scanning |
| Snyk | Commercial/Free | Yes | Yes | Developer-friendly, comprehensive |
| Dependabot | GitHub-native | Yes | Yes | GitHub repos, automatic PRs |
| Renovate | Open source | Yes | Yes | Flexible, self-hostable, multi-PM |
| Trivy | Open source | No | Yes | Container and filesystem scanning |
| Sonatype Nexus | Enterprise | Yes | Yes | Policy engine, enterprise compliance |
Recommendation: Dependabot or Renovate for automated update PRs, plus Snyk or Trivy for vulnerability scanning in CI. The combination of automated PRs and CI scanning covers both proactive updating and reactive vulnerability detection.
Supply Chain Attack Taxonomy
- Typosquatting: Publishing
orexpress-js
to catch typos ofexpresss
. The malicious package has a similar name and may even re-export the real package's functionality while executing a payload.express - Dependency confusion: Publishing a public package with the same name as a private internal package. Package managers may prefer the public registry version, pulling in the attacker's code. Mitigate with scoped packages and registry configuration.
- Maintainer compromise: Stolen npm tokens, compromised GitHub accounts, or social engineering to gain publish access. The event-stream attack used this vector.
- Malicious update: A trusted package pushes a version containing malicious code. May be intentional (node-ipc) or result of compromised credentials.
- Build-time compromise: Modifying CI/CD scripts or build tools (Codecov attack).
Lockfile Manipulation Attacks
An attacker with PR access can modify the lockfile to point to a different package version or a malicious registry URL. The lockfile diff is often large, auto-generated, and skipped during code review. Lockfile-lint and lockfile-lint-api verify that all packages resolve to the expected registry. Include lockfile review in the code review process: review the
resolved URLs in the lockfile for unexpected registries, and reject PRs that
modify the lockfile without corresponding package.json changes.
Emergency Patching Workflow
- Security advisory received (via email, GitHub advisory, or CVE database alert)
- Check if the vulnerable package is in any service's dependency tree
- Identify all affected services and their deployment environments
- Check if a patched version is available
- If patched: update lockfile, run tests, deploy to staging, verify, deploy to production
- If no patch: implement WAF rule or configuration workaround, disable affected feature via feature flag, set daily reminder to check for patch
- Post-incident: add the package to enhanced monitoring, review whether the dependency is still necessary
Anti-Patterns
-
No lockfile in the repository. Without a lockfile,
resolves the latest version of every dependency at install time, meaning builds are non-reproducible and a malicious update is automatically pulled in on the next install. Always commit lockfiles to version control.npm install -
Ignoring audit warnings. "There are 47 moderate vulnerabilities" becomes background noise and is ignored entirely. This is how Log4Shell persists in production for months. Triage vulnerabilities: fix critical and high immediately, schedule medium and low for weekly review, document accepted risks with justification.
-
Updating dependencies only when something breaks. Waiting until a production incident forces a dependency update means you are always updating under pressure with no time for proper testing. Proactive weekly updates are less risky than emergency updates under a security incident.
-
Trusting download counts as a security signal. Popular packages get compromised too -- event-stream had millions of weekly downloads. Evaluate: maintenance activity (recent commits), security audit history, dependency depth (fewer transitive deps is better), and whether the package has multiple maintainers.
-
Using
in CI instead ofnpm install
.npm ci
can modify the lockfile ifnpm install
andpackage.json
diverge, producing a build with different dependency versions than what was tested locally.package-lock.json
installs exactly what the lockfile specifies and fails on any discrepancy. Usenpm ci
in CI for reproducible, auditable builds.npm ci