StackPractices
intermediate By Mathias Paulenko

Third-Party Dependency Audit Template

A template for auditing third-party dependencies: license compliance, security vulnerabilities, maintenance health, and supply chain risk.

Topics: security

Use this template to evaluate libraries before adding them or during periodic audits. See the Web Application Security Guide for broader security practices.

Overview

Every third-party dependency is a security and maintenance liability. Dependencies ship with their own bugs, vulnerabilities, and licensing constraints. Without a structured audit process, teams accumulate libraries that are unmaintained, vulnerable, or legally incompatible with their product.

This isn’t hypothetical. The 2016 left-pad removal broke thousands of builds because an 11-line utility vanished from npm. In 2018, the event-stream package was handed to a new maintainer who shipped bitcoin-stealing code to millions of downloads. And Log4Shell in 2021 showed how a single transitive dependency can turn into a company-wide incident overnight. An audit won’t predict the next incident, but it does answer the questions that matter when one lands: what do we run, who keeps it alive, and how fast can we replace it.

This template covers:

  1. Security — known CVEs, SAST coverage, release signing
  2. Maintenance health — release cadence, contributor count, bus factor
  3. Supply chain risk — transitive dependencies, native code, corporate backing
  4. License compliance — SPDX identifiers, copyleft detection
  5. Decision — approved, approved with monitoring, or rejected

Template

# Dependency Audit: [Library Name]

## Overview
| Field | Value |
|-------|-------|
| **Library** | [name] v[x.y.z] |
| **Purpose** | [what problem it solves] |
| **Replaces** | [internal code / another library] |
| **Auditor** | [name] |
| **Date** | [YYYY-MM-DD] |

## Security

| Check | Result | Evidence |
|-------|--------|----------|
| Known CVEs | [none / list] | Snyk / OSV report link |
| SAST available | [yes / no] | Link to security audit |
| Bug bounty program | [yes / no] | Link |
| Release signing | [yes / no] | GPG / Sigstore verification |

## Maintenance Health

| Metric | Value | Threshold |
|--------|-------|-----------|
| Last release | [date] | < 12 months |
| Open issues | [count] | < 500 |
| Open PRs | [count] | < 100 |
| Contributors | [count] | > 2 (bus factor) |
| License | [SPDX identifier] | [approved list] |

## Supply Chain Risk

| Check | Result |
|-------|--------|
| Download count | [npm / PyPI stats] |
| Corporate backing | [yes / no — who] |
| Transitive dependencies | [count] |
| Native code / compiled binaries | [yes / no] |

## Decision

| Outcome | Conditions |
|---------|-----------|
| **Approved** | All checks pass |
| **Approved with monitoring** | Minor risks, track quarterly |
| **Rejected** | Critical risk, find alternative |

Filled Example

The example below uses illustrative data — always pull real numbers from the sources listed in “How to Run the Audit” before signing off on a decision.

# Dependency Audit: fast-json-stringify v3.2.0

## Overview
| Field | Value |
|-------|-------|
| **Library** | fast-json-stringify v3.2.0 |
| **Purpose** | 2x faster JSON serialization for API responses |
| **Replaces** | JSON.stringify() in hot paths |
| **Auditor** | Jane Doe |
| **Date** | 2026-07-15 |

## Security

| Check | Result | Evidence |
|-------|--------|----------|
| Known CVEs | None | Snyk report 2026-07-15 |
| SAST available | Yes | CodeQL workflow in repo |
| Bug bounty program | No | N/A |
| Release signing | Yes | Sigstore provenance |

## Maintenance Health

| Metric | Value | Threshold |
|--------|-------|-----------|
| Last release | 2026-06-20 | < 12 months PASS |
| Open issues | 34 | < 500 PASS |
| Open PRs | 5 | < 100 PASS |
| Contributors | 18 | > 2 PASS |
| License | MIT | Approved list PASS |

## Supply Chain Risk

| Check | Result |
|-------|--------|
| Download count | 12M/week (npm) |
| Corporate backing | Yes — NearForm |
| Transitive dependencies | 3 |
| Native code / compiled binaries | No |

## Decision

| Outcome | Conditions |
|---------|-----------|
| **Approved** | All checks pass. Review at next quarterly audit. |

When to Use

  • Before adding a new dependency — audit before installing, not after
  • Quarterly dependency review — schedule recurring audits for all dependencies
  • After a security incident — audit all dependencies in the affected ecosystem
  • Before a release — verify no new vulnerabilities were introduced
  • During due diligence — audit dependencies before acquiring or merging with another company
  • When a dependency is deprecated — audit alternatives before migrating
  • When inheriting a codebase — a new service, an acquisition, or a handover all start with the same question: what’s in the lockfile and is any of it already a problem

How to Run the Audit

The template is a record, not a process — the numbers in it come from a handful of tools you can run in under an hour per library.

  1. Pull the vulnerability report. Run npm audit --package <name> or pip-audit for Python, then cross-check on osv.dev — OSV aggregates GitHub Advisories, NVD, and ecosystem-specific databases, so it catches things npm audit misses. Record the CVE list (or “none”) with a link.
  2. Check maintenance health. The fastest single source is deps.dev — it shows release cadence, open issues, and dependency counts per version. Complement it with OpenSSF Scorecard (scorecard --repo=github.com/org/lib), which checks for branch protection, signed releases, and CI testing as a percentage score.
  3. Verify the license. The SPDX identifier on the registry page is the start, not the end — check the LICENSE file in the repo, and run license-checker (npm) or pip-licenses to catch transitive licenses that don’t match the top-level declaration.
  4. Measure the footprint. npm ls <name> or bundlephobia.com shows the transitive tree size. For frontend bundles this doubles as a size audit.
  5. Fill the decision row. Approved / approved with monitoring / rejected, with the condition written as a sentence a future reader can act on — “Approved with monitoring: single maintainer, re-audit in Q3” beats a bare checkmark.

A first-pass audit of a well-known library takes 20–30 minutes. Budget more for obscure packages — low download counts mean you can’t lean on community scrutiny, so read the actual source for anything that touches credentials, network, or the filesystem.

Calibrating the Thresholds

The numbers in the template are starting points for a typical production service, not universal constants. Tighten or loosen them based on where the library will sit:

ContextLast releaseContributorsTransitive depsNotes
Auth/payment/data path< 6 months> 5< 20Require signed releases
Public-facing API< 12 months> 2< 50Standard thresholds
Internal tooling< 24 months> 1< 100Tolerate slower cadence
Dev-only dependency< 24 monthsany< 100Vulnerabilities still matter — CI is a target

Two rules hold regardless of context: a single-maintainer library on a critical path is always “approved with monitoring” at best, and a copyleft license (GPL/AGPL) always routes to legal review no matter how healthy the project looks.

Lifecycle

Dependency audit lifecycle: pre-adoption audit, ongoing monitoring, quarterly review, then deprecation and replacement when the library is retired.

Phase 1: Pre-adoption audit

Run the full audit before adding the dependency. Check security, maintenance health, supply chain risk, and license compliance. Document the decision in an ADR if the dependency is critical.

Phase 2: Ongoing monitoring

Set up automated alerts for new vulnerabilities. Dependabot, Snyk, or GitHub security advisories notify you when a CVE is published for a dependency you use.

Phase 3: Quarterly review

Re-audit all dependencies quarterly. Check if maintainers are still active, if the library is still the best option, and if newer alternatives offer better security or performance.

Phase 4: Deprecation and replacement

When a dependency is deprecated or a better alternative emerges, plan the migration. Audit the replacement, create a migration plan, and remove the old dependency from the codebase.

When a Dependency Gets Rejected

A rejection is only useful if it produces a next step — otherwise the same library gets proposed again in six months. For each rejected dependency, record:

  • Why it failed — the specific check (CVE unpatched, GPL license, unmaintained since 2023), not just “security risk”.
  • The approved alternative — the library that passed the same audit, or “build it internally” with a size estimate.
  • Whether it’s already in the codebase — if the library slipped in before the audit existed, rejection means filing a removal ticket with an owner and a date, not just a note in a doc.

Keep rejected audits next to approved ones. They’re the institutional memory that stops the same evaluation from being re-run every planning cycle — and during due diligence, a documented rejection reads better than an undocumented omission.

The third outcome deserves the same discipline: “approved with monitoring” isn’t a weaker approval, it’s a commitment to re-check on a schedule. Write the monitoring condition into the decision row (what triggers re-audit, who owns it, when), put the date on the dependency review calendar, and treat a missed re-audit the same way you’d treat a missed review of any other control.

Red Flags

FlagAction
No release in 2+ yearsFind actively maintained alternative
Single maintainerHigh bus factor risk; consider forking or replacing
Copyleft license (GPL)Legal review required for commercial use
Native binaries without reproducible buildsSupply chain attack risk
> 100 transitive dependenciesEach is a new attack surface

Automation

Dependabot (GitHub)

Configure dependabot.yml to scan for vulnerabilities and open PRs automatically:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

Snyk

Run snyk test in CI to fail builds on high-severity vulnerabilities. Use snyk monitor to track new vulnerabilities over time — it snapshots the dependency tree so a CVE published against an already-deployed version still triggers an alert. snyk test --severity-threshold=high keeps PR checks from drowning in low-severity noise.

OWASP Dependency-Check

Self-hosted scanner that checks dependencies against the NVD database. Integrates with Maven, Gradle, and Jenkins.

Renovate Bot

Alternative to Dependabot with more configuration options: grouped updates, auto-merge rules, and multi-ecosystem support. Renovate’s packageRules can auto-merge patch-level updates after CI passes while holding majors for manual review — a reasonable middle ground between drowning in PRs and falling a year behind.

OpenSSF Scorecard in CI

Scorecard also runs as a GitHub Action on your own repo — it grades your project’s hygiene (branch protection, pinned actions, token permissions) rather than your dependencies. Pair it with osv-scanner for a lockfile scan that doesn’t depend on a specific package manager.

Troubleshooting the Audit Itself

The audit process has its own failure modes worth planning for:

  • npm audit noise. Most reported vulnerabilities live in dev dependencies or code paths you never execute. Triage by reachability — a prototype-pollution CVE in a build-only tool that never ships to the bundle is a lower priority than a header-injection bug in your HTTP client, even if both report as “high”. Document the triage decision in the audit so the next reviewer doesn’t redo it.
  • Unfixable transitive vulnerabilities. Sometimes the direct dependency is healthy but bundles a vulnerable version of something else, and no override fixes it without breaking the API. Options, in order: overrides/resolutions/constraints to force a patched version, a patch-package fix, or replacing the direct dependency. If none work, that’s a legitimate “rejected” with the specific blockers written down.
  • Peer dependency conflicts. A library requiring react@^17 when you’re on 18 blocks clean upgrades. Check whether the constraint is real (the library actually uses removed APIs) or just a stale peerDependencies range — the latter is common and often resolves with a PR upstream or --legacy-peer-deps as a documented stopgap.
  • Licenses hiding in transitive deps. The top-level package says MIT but a transitive dependency ships GPL. license-checker --production (npm) or pip-licenses catches these — run it on the production tree, not the dev one.

What Works

  • Audit before adding — retroactive audits discover problems too late
  • Automate scanning — Dependabot, Snyk, or OWASP Dependency-Check in CI. See the CI/CD Pipeline Guide for integrating security scans.
  • Pin versions — lockfiles prevent silent upgrades to compromised versions
  • Review license compatibility — AGPL in a SaaS backend is a legal risk
  • Track deprecation — libraries die slowly; monitor health quarterly
  • Document the decision — keep audit records alongside ADRs in docs/audits/
  • Set up alerts — subscribe to security advisories for critical dependencies

Common Mistakes

  • Adding dependencies for trivial functionality — “left-pad” incidents happen
  • Ignoring transitive dependencies — the dependency tree is your attack surface
  • Not updating after audit — a clean report today means nothing in 6 months
  • Blind trust in popular packages — popularity doesn’t equal security
  • Not checking license compatibility before shipping — legal risk surfaces in due diligence
  • Removing lockfiles to “simplify” — you lose reproducible builds and vulnerability pinning
  • Auditing only direct dependencies — npm audit reports the whole tree; a vulnerable transitive dep three levels deep still ships to production
  • Skipping dev-only dependencies — a compromised build tool runs in CI with your secrets

Variants

Frontend dependency audit

For frontend projects, add checks for: bundle size impact, tree-shakeability, CSS injection risk, and accessibility compliance. Tools: Bundlephobia, webpack-bundle-analyzer. Browser-facing code has a different threat model than server code — a compromised frontend dependency can skim form fields directly from the page, so weight “touches DOM or network” as heavily as CVE counts when scoring risk.

Container image audit

For Docker images, scan the base image and all installed packages. Tools: Trivy, Grype, Snyk Container. Check for root user, exposed ports, and secrets in layers. The container security recipe covers the image hardening side.

Monorepo dependency audit

For monorepos, audit shared dependencies across all packages. Use workspace-level lockfiles (pnpm-lock.yaml, a root poetry.lock). Track which packages consume each dependency to assess blast radius — tools like nx graph or turbo can list affected packages when a dependency changes, which turns “who uses this?” from a grep session into a query.

See Also

Frequently Asked Questions

How often should I audit dependencies?

New dependencies before adding. Existing dependencies quarterly. Critical dependencies monthly. After any security incident involving a dependency, audit all dependencies in the same ecosystem — a CVE in one logging library usually means its siblings deserve a look too. If a vulnerability was actively exploited, escalate through your incident response process rather than treating it as routine maintenance.

What is the difference between a security audit and a dependency audit?

A security audit focuses on vulnerabilities (CVEs, exploits). A dependency audit is broader: it covers security, maintenance health, supply chain risk, license compliance, and code quality. Security is one dimension of a full dependency audit.

How do I handle transitive dependencies with vulnerabilities?

Use npm audit or pip-audit to identify which transitive dependencies have vulnerabilities. If the direct dependency can't update the transitive one, replace the direct dependency or force a safe version — npm overrides, yarn resolutions, pip constraints file.

Should I pin exact versions or use ranges?

For applications: pin exact versions (package-lock.json, poetry.lock). For libraries: use ranges (^1.2.0, >=1.2,<2.0). Pinning in applications gives reproducible builds and prevents supply chain attacks via version substitution.

What license is safe for commercial use?

MIT, Apache-2.0, BSD, ISC, and Unlicense are generally safe — include the license text in your distribution and keep a record of approved licenses in your dependency policy. GPL requires legal review if you distribute the software, and AGPL is risky for SaaS even without distribution. Always confirm edge cases with your legal team.

Should I fork a library instead of using it directly?

Fork only if you need customization the upstream won't accept. Forking shifts maintenance burden to your team. Prefer contributing upstream or wrapping the library.

What is a transitive dependency and why does it matter?

A transitive dependency is a library that your dependency depends on. If you install package A, and A depends on B, then B is a transitive dependency. Transitive dependencies are part of your attack surface — a vulnerability in B affects you even though you only installed A. Lockfiles and SBOMs (Software Bill of Materials) help track them.

How do I handle a critical vulnerability in a dependency?
  1. Check if a patched version exists and upgrade immediately
  2. If no patch exists, check for a workaround or mitigation
  3. If no workaround exists, evaluate replacing the dependency
  4. If the vulnerability was exploited, open an incident per the Security Incident Response Template
Should I use an SBOM (Software Bill of Materials)?

Yes. An SBOM lists every component in your software, including transitive dependencies and their versions, so you can answer "are we affected?" in minutes when a new CVE drops. Generate one with cyclonedx or syft, and use provenance verification (Sigstore, GPG), npm ci instead of npm install, and a private registry proxy as the complementary controls that prevent bad packages from landing in the SBOM in the first place.

How often should I run automated dependency scans?

Run scans on every pull request via CI (e.g., Dependabot, Snyk). Schedule a full audit weekly. For critical projects, run daily scans. Review and triage findings weekly.