SuperAPI AI Directory

patch-vulnerabilities

Security & auditing

View repository

Updated 10-16 03:43

About this skill

Patch npm Dependencies

Comprehensively scan npm dependencies for security vulnerabilities, outdated packages, and stale overrides. Patch eligible ones (respecting a 7-day publish-age cooldown), verify fixes, and repeat until no new patchable issues remain.

Each tool catches a different blind spot:

  1. npm audit — known security vulnerabilities only. Does not flag deprecated or outdated packages.
  2. npm outdated — catches outdated direct dependencies only. Ignores overrides and does not flag vulnerabilities.
  3. Override checks — overrides are invisible to both tools. You must manually check each override version against the npm registry.

All three checks are required for full coverage.

Prerequisites

  • Node.js and npm installed
  • A project with package.json and npm-shrinkwrap.json or package-lock.json
  • Network access to the npm registry

Workflow

Execute the phases below in a loop. Each pass through the loop is one patch cycle. Continue cycling until the termination condition is met.


Phase 1 — Scan

Run all three scans and combine results into a single unified list of actionable items.

1A. Security vulnerabilities (npm audit)

npm audit --json 2>/dev/null

Parse the JSON output. Extract from the vulnerabilities object:

  • Package name
  • Current version (from nodes or range)
  • Severity (critical, high, moderate, low)
  • Fix available (via and fixAvailable fields)
  • Dependency type: direct or transitive

To classify each vulnerability:

  • Direct: the package appears in dependencies or devDependencies in package.json
  • Transitive: the package does NOT appear in package.json — it is pulled in by a direct dependency. The npm audit --json output shows the dependency chain in the via and effects fields. Identify the top-level ancestor (the direct dependency that pulls in the vulnerable transitive package).

1B. Outdated direct dependencies (npm outdated)

npm outdated --json 2>/dev/null

Parse the JSON output. For each entry extract:

  • Package name
  • current version (what is installed)
  • wanted version (latest that satisfies the semver range in package.json)
  • latest version (latest on the registry)

An entry is actionable when current differs from wanted (semver-compatible update available). Note entries where wanted differs from latest — these indicate a major version is available but not semver-compatible with the declared range; flag these separately.

1C. Stale overrides

Read the overrides object from package.json. Flatten all nested overrides to a list of { parent, package, pinnedVersion } tuples. For each, query the npm registry for the latest version:

npm view <package> version

Compare the pinned version to the latest. An override is stale when pinnedVersion !== latest. Classify the gap:

  • Patch/minor: likely safe to update
  • Major: flag as a breaking change

Combined scan summary

Present all findings to the user in a single table:

Package Source Current Target Type Severity/Notes

Where Source is one of: audit, outdated, override.

If all three scans find zero actionable items, stop — the project is clean.


Phase 2 — Check cooldown eligibility

For every actionable item from Phase 1 that has an update target, verify the target version's publish date against the 7-day cooldown rule: the target version must have been published at least 7 days ago.

Query the npm registry for each package:

npm view <package> time --json

This returns a JSON object mapping version strings to ISO 8601 timestamps. Find the entry for the target version. Calculate the age:

age_days = (now - publish_date) / 86400

If age_days >= 7: the package is eligible for patching. If age_days < 7: the latest target is in cooldown. Before skipping, check for eligible intermediate versions — an older version between current and target that passes the 7-day rule.

Use the time JSON (already fetched) to find all versions where:

  1. version > current (newer than what is installed)
  2. version <= target (at or below the wanted version)
  3. age_days >= 7 (passes cooldown)
  4. The version is a stable release (skip pre-release tags like -alpha, -beta, -rc)

If eligible intermediate versions exist, use the highest one as the patching target instead.

If no intermediate version is eligible either, report the package as "cooling down" with the date the latest target becomes eligible.

Report cooldown status to the user:

Package Source Target version Published Age Eligible Eligible date

If no packages are eligible (including via intermediate versions), stop — all remaining items are in cooldown or have no fix. Report the earliest eligibility date.


Phase 3 — Patch

For each eligible package, apply the fix. The strategy depends on the source and dependency type.

3A. Security vulnerabilities (from npm audit)

Direct dependencies
  1. Direct fix — If npm audit fix can resolve it without breaking changes:

    npm audit fix --dry-run 2>/dev/null
    

    Review the dry-run output. If changes look safe, apply:

    npm audit fix
    
  2. Targeted update — If npm audit fix cannot resolve it or introduces breaking changes, update the specific package:

    npm install <package>@<fix-version>
    
  3. Major version update — If the fix requires a major version bump:

    npm install <package>@<fix-version>
    

    Flag this to the user as a breaking change and note it requires additional testing.

Transitive dependencies

Transitive vulnerabilities cannot be fixed by installing the vulnerable package directly. Work through the dependency chain instead:

  1. Update the top-level ancestor — Often, updating the direct dependency that pulls in the vulnerable transitive package resolves the issue:

    npm install <top-level-ancestor>@latest
    

    Apply the cooldown check to this version too.

  2. npm audit fix — This resolves the dependency tree automatically when a compatible version exists:

    npm audit fix
    
  3. npm overrides — If the top-level ancestor has not released a fix yet, use an npm override to force the transitive dependency to the fixed version. Add to package.json:

    {
      "overrides": {
        "<vulnerable-package>": "<fix-version>"
      }
    }
    

    Then run npm install to apply. Apply the cooldown check to the override version. Flag overrides to the user — they bypass the parent package's declared compatibility range and may cause runtime issues.

  4. No fix available — If the top-level ancestor pins the vulnerable version and no override is safe, report the vulnerability as unfixable for now. Note the top-level ancestor and suggest the user open an issue or PR upstream.

3B. Outdated direct dependencies (from npm outdated)

For each outdated package where current != wanted:

npm install <package>@<wanted-version>

This is a semver-compatible update within the declared range in package.json, so it should be safe. If wanted != latest and the user wants to pursue the latest (major) version, flag it as a breaking change.

3C. Stale overrides

For each stale override, update the pinned version in package.json's overrides section, then run:

npm install
  • Patch/minor updates: update the version string directly.
  • Major updates: flag to the user as a breaking change. Since overrides bypass the parent package's declared compatibility range, a major bump is especially risky. Only apply if the user confirms, or skip and report.

After each patch

Build the project and run the test suite to verify nothing broke:

npm run build && npm test

If the build or tests fail after a patch:

  • Revert the change: git checkout -- package.json npm-shrinkwrap.json package-lock.json && npm install
  • Report the failure to the user with the test output
  • Continue to the next eligible package

If tests pass, commit the change:

  • Stage: git add package.json npm-shrinkwrap.json package-lock.json
  • Commit with a descriptive message, e.g.:
    • Audit fix: fix: upgrade <package> to <version> to fix <severity> vulnerability
    • Outdated dep: fix: upgrade <package> from <old> to <new>
    • Override update: fix: update <package> override from <old> to <new>

Phase 4 — Re-scan and loop

Return to Phase 1. Run all three scans again to check for remaining issues. A previous patch may have resolved transitive issues or introduced new ones.


Termination conditions

Stop the loop when any of these is true:

  1. All three scans (npm audit, npm outdated, override check) report zero actionable items
  2. All remaining items have targets in cooldown (< 7 days old)
  3. All remaining items have no fix available
  4. A patch cycle produced zero successful patches (nothing new was fixed)

Final report

After the loop ends, present a summary:

## Dependency Patch Summary

Patch cycles completed: N

Packages patched: count
  From npm audit:
    - Direct: list with versions
    - Transitive (via ancestor update): list
    - Transitive (via override): list
  From npm outdated:
    - list with old → new versions
  From override check:
    - list with old → new versions and parent package

Remaining issues: count
  - In cooldown (eligible on <date>): list
  - No fix available: list
  - Major version available (not auto-applied): list
  - Transitive, waiting on upstream: list with top-level ancestor

Overrides: list all current overrides
  - Review periodically and remove when upstream fixes land

Important notes

  • Always run tests between patches to catch breakage early.
  • Commit each patch individually for easy rollback.
  • When a fix requires a major version bump, warn the user explicitly. Do not auto-apply major version bumps for overrides — flag and skip.
  • Never force-install a version published less than 7 days ago.
  • If npm-shrinkwrap.json exists, include it in commits alongside package-lock.json.
  • Overrides bypass the parent package's declared compatibility range. Always flag new or updated overrides to the user.