Skip to main content
Security7 min readMarch 3, 2026

Dependency Vulnerability Management: Keeping Third-Party Code Safe

Manage dependency vulnerabilities effectively — npm audit, Dependabot, Software Bill of Materials, transitive dependencies, and building a sustainable update workflow for your team.

James Ross Jr.

James Ross Jr.

Strategic Systems Architect & Enterprise Software Developer

Dependency Vulnerability Management: Keeping Third-Party Code Safe

Every package in your node_modules directory is code you did not write and code you are responsible for. That directory on a typical Node.js project contains hundreds or thousands of packages — a tangled graph of direct and transitive dependencies, most of which your team has never reviewed. Some of those packages have known vulnerabilities. Some will develop vulnerabilities after you install them. Managing this effectively is a non-trivial ongoing responsibility.

Here is how I think about dependency security in a way that is sustainable.

Understanding Your Attack Surface

Before you can manage dependencies, understand what you have. Run an audit:

npm audit

This queries the npm advisory database against your installed packages and reports known vulnerabilities with severity levels. The output looks like:

found 3 vulnerabilities (1 low, 1 moderate, 1 high)

Follow up with:

npm audit --json | jq '.vulnerabilities | keys'

to get a list of affected packages. For each vulnerability, npm audit reports the severity, the affected package, the vulnerability description, the path in your dependency tree, and whether a fix is available.

The numbers that matter are high and critical severity vulnerabilities with available fixes. low severity vulnerabilities in deeply transitive dependencies where no fix is available are background noise — important to know about but not necessarily actionable today.

Running npm audit in CI

Every CI pipeline should include a dependency audit:

- name: Security audit
  run: npm audit --audit-level=high

--audit-level=high fails the build only for high and critical severity vulnerabilities. This is the right threshold to start with — it catches serious issues without generating noise from low-severity findings that may not be fixable.

The problem with npm audit is false positives and unavoidable vulnerabilities. A vulnerability in a package you use may only be exploitable in a different context than your usage, or the fix may not be available yet, or the fix may introduce breaking changes. For these cases, use an audit configuration file:

// .npmrc or audit-level configuration
{
  "auditLevel": "high",
  "ignore": []
}

For specific CVEs you have evaluated and determined do not affect your usage, document them in your audit CI step and skip those specific advisories — but document why you are skipping them. "This vulnerability is in the server-side rendering path of package X, and we only use it client-side" is a documented risk acceptance. "This is annoying so we are ignoring it" is not.

Dependabot: Automated Dependency Updates

Manual dependency updates do not happen consistently. A package has a security update available, someone notes it, it goes on the backlog, the backlog is never prioritized, six months later the vulnerability is exploited. This cycle is common and preventable.

GitHub Dependabot automatically creates pull requests for dependency updates. Configure it in .github/dependabot.yml:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
      - "automated"
    reviewers:
      - "your-team"
    # Group related updates to reduce PR noise
    groups:
      production-dependencies:
        dependency-type: "production"
      development-dependencies:
        dependency-type: "development"

Dependabot creates separate PRs for each dependency update. Your CI runs on these PRs. If tests pass, the PR can be merged. If they fail, the update has a compatibility issue that needs review.

The groups configuration batches related updates into single PRs, reducing PR noise. Production dependencies and development dependencies are grouped separately so you can apply different review standards — production dependency updates warrant more careful review than a development tool update.

Renovate as an Alternative

Renovate (by Mend, formerly WhiteSource) is a more configurable alternative to Dependabot. It supports grouping updates by category, scheduling automerge for specific package types, and detecting when updated packages have new major versions that require manual review.

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "schedule": ["on monday"],
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "matchCurrentVersion": "!/^0/",
      "automerge": true
    },
    {
      "matchPackagePatterns": ["*"],
      "matchUpdateTypes": ["major"],
      "labels": ["major-update"],
      "reviewersFromCodeOwners": true
    }
  ]
}

This configuration auto-merges patch updates for stable packages (non-v0) when CI passes, while requiring manual review for major updates. This reduces the overhead of dependency maintenance significantly — patch updates (usually bug fixes and security patches) merge automatically, while major updates that might have breaking changes get reviewed.

Evaluating Dependencies Before Adding Them

Vulnerability management is easier when you are selective about what you add. Before adding a new dependency, evaluate:

Maintenance status — is the package actively maintained? When was the last release? Are there open issues with no response? An unmaintained package will not receive security patches.

Popularity and community — a popular package with a large user base is more likely to have vulnerabilities discovered and patched quickly. Obscure packages with few users may have vulnerabilities that nobody has found or reported yet.

Dependencies of the dependency — installing one package installs all of its transitive dependencies. npm ls shows the dependency tree. Adding a package that pulls in 50 transitive dependencies adds 50 packages to your attack surface.

License — not security-related, but worth checking. MIT and Apache 2.0 are safe for most applications. GPL and LGPL have implications for open-source distribution.

Can you write it yourself? — for simple utilities (left-pad famously illustrates this), consider whether the package is simpler to implement directly. Fewer dependencies is fewer vulnerabilities.

Supply Chain Attacks

Beyond known vulnerabilities, supply chain attacks are an increasing threat. A malicious actor takes over a popular package (by compromising a maintainer's account, registering a typosquatted package name, or injecting malicious code into the build process of an open-source project) and publishes a version containing malicious code. Thousands of applications install the update and execute the malicious code.

This has happened with real packages: event-stream (2018), ua-parser-js (2021), node-ipc (2022), and several others. The impact can be credential theft, data exfiltration, or in the case of node-ipc, intentional data destruction.

Mitigation strategies:

Pin to exact versions. Use a lockfile (package-lock.json or yarn.lock) and commit it. Every install gets exactly the version that was tested.

Verify integrity. npm's lockfile includes integrity checksums. npm ci verifies integrity on install and fails if checksums do not match.

Monitor for unusual behavior. Tools like Socket Security analyze package changes and flag packages that add new network calls, file system access, or post-install scripts in new versions.

Review dependency changes in PRs. When Dependabot creates a PR for a dependency update, review what changed in the package. For high-risk packages (packages with broad system access or network capabilities), check the changelog and even the diff.

The Software Bill of Materials (SBOM)

An SBOM is a formal inventory of all components in your software, including their versions and licenses. Generating an SBOM makes it possible to quickly answer "are any of our applications using the affected package?" when a new vulnerability is announced.

Generate an SBOM for your application:

npm install -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --output-file sbom.json

Store SBOMs as build artifacts alongside your releases. When CVE-2026-XXXXX is announced affecting some-package below version 2.3.4, you can query your SBOMs to find affected applications in minutes.

SBOM generation is increasingly required for government and enterprise software procurement. Including it in your build pipeline now prepares you for that requirement.

The Practical Update Cadence

Security advisories and updates cannot be ignored indefinitely. The operational cadence I recommend:

  • Critical vulnerabilities with fixes: patch within 24-48 hours. Do not wait for a sprint cycle.
  • High severity with fixes: patch within one week.
  • High severity without fixes: document the risk, implement compensating controls if possible (WAF rules, network controls), and track the advisory for when a fix becomes available.
  • Moderate severity: include in next sprint cycle.
  • Low severity: batch quarterly with regular maintenance updates.

Automated tooling (Dependabot, Renovate) with CI validation handles the routine updates. Reserve human judgment for critical issues, breaking changes, and vulnerabilities without available fixes.


If you want help setting up a dependency security program for your team or need a review of your current vulnerability management practices, book a session at https://calendly.com/jamesrossjr.


Keep Reading