npm-pip-maven-audit

Configures and runs native package-manager audit commands across ecosystems - npm audit --audit-level=high (npm), yarn npm audit (Yarn 2+), pnpm audit (pnpm), pip-audit (Python via PyPA), mvn dependency:check (Maven via OWASP Dependency-Check plugin), cargo audit (Rust, with .cargo/audit.toml suppression, --deny semantics, SARIF, binary auditing, and the rustsec/audit-check Action as a reference), and bundle audit (Ruby Bundler, with .bundler-audit.yml waivers, Rake integration, and CI gating as a reference); fastest no-install-required SCA option. Use when the team wants fast, no-extra-tooling SCA in CI as a first line of defense, when a Rust or Ruby repo needs its ecosystem-native scanner, or pairs with snyk/osv-scanner for layered coverage.

/plugin install qa-security-scanning@testland-qa

Overview

Most package managers ship native audit subcommands that query an ecosystem-specific advisory feed (npm advisories, PyPA, RubySec, Cargo advisory DB, etc.) - the fastest first-line defense: already installed, no extra tooling, runs in seconds. For full coverage, run one native audit + a unified scanner (snyk-test / osv-scanner). The speed/coverage tradeoff table is in references/ecosystem-config-and-triage.md.

When to use

  • Fast first-line CI gate (run before slower comprehensive scans).
  • Single-ecosystem repo where one native audit is sufficient.
  • Local dev loop: npm audit after npm install is faster than setting up Snyk locally.
  • Layered with snyk-test + osv-scanner for full coverage.

Step 1 - npm / Yarn / pnpm

# npm (built-in since npm 6)
npm audit
npm audit --audit-level=high              # filter to HIGH+CRITICAL
npm audit --json > audit.json
npm audit fix                              # auto-upgrade where compatible
npm audit fix --force                      # may break: bumps majors

# Yarn 2+ (Berry)
yarn npm audit
yarn npm audit --severity=high
yarn npm audit --recursive                 # scan all workspaces

# pnpm
pnpm audit
pnpm audit --audit-level high
pnpm audit --json

Source: docs.npmjs.com/cli/v10/commands/npm-audit + yarnpkg.com/cli/npm/audit + pnpm.io/cli/audit.

Step 2 - pip-audit (Python)

pip install pip-audit

# Scan installed packages in current env
pip-audit

# Scan a requirements file
pip-audit -r requirements.txt

# Scan with PyPA + OSV.dev
pip-audit -s pypi -s osv

# JSON / SARIF output
pip-audit --format json --output pip-audit.json
pip-audit --format sarif --output pip-audit.sarif

# Fix vulnerabilities (auto-upgrade)
pip-audit --fix

# Skip specific CVEs
pip-audit --ignore-vuln GHSA-xxxx-yyyy-zzzz

Source: pypi.org/project/pip-audit + github.com/pypa/pip-audit. pip-audit is the official PyPA tool (preferred over the older safety package).

Step 3 - Maven (OWASP Dependency-Check)

Maven has no native mvn audit; use the OWASP Dependency-Check plugin, which fails the build above a CVSS threshold and emits HTML/JSON/SARIF:

mvn dependency-check:check

The pom.xml plugin block (with failBuildOnCVSS + suppression file) and the Gradle equivalent are in references/ecosystem-config-and-triage.md.

Source: jeremylong.github.io/DependencyCheck/dependency-check-maven/.

Step 4 - cargo audit (Rust)

cargo install cargo-audit

cargo audit
cargo audit --json
cargo audit --deny warnings              # treat warnings as errors
cargo audit --ignore RUSTSEC-2023-0001   # specific advisory

Source: rustsec.org + github.com/rustsec/rustsec.

Exit codes, per-category --deny flags, the committed .cargo/audit.toml suppression schema, SARIF output, cargo audit fix, binary auditing (cargo audit bin), and the rustsec/audit-check Action are in references/cargo-bundle-audit.md.

Step 5 - bundler-audit (Ruby)

gem install bundler-audit

bundle-audit check                        # one-time scan
bundle-audit update                       # refresh advisory DB
bundle-audit check --update               # combined refresh + scan
bundle-audit check --ignore CVE-2024-1234 # specific CVE

Source: github.com/rubysec/bundler-audit.

The committed .bundler-audit.yml waiver template (inline justification + re-review date), Rake task wiring, JSON output, and the CI gate are in references/cargo-bundle-audit.md.

Step 6 - False-positive triage (MANDATORY)

Each native audit has its own suppression mechanism:

ToolSuppression
npm auditnpm audit --omit dev (skip devDependencies) + package.json overrides field for forced version pin
pip-audit--ignore-vuln <id> CLI flag (per-CVE)
dependency-check-mavendependency-check-suppressions.xml (XML schema with vuln-name regex + reason)
cargo auditcommitted .cargo/audit.toml [advisories] ignore (preferred over --ignore CLI flags - see references/cargo-bundle-audit.md)
bundle-auditcommitted .bundler-audit.yml ignore: list (preferred over --ignore CLI flags - see references/cargo-bundle-audit.md)

Every suppression carries a mandatory justification (reason + approver + re-review date). The Maven XML template, the AUDIT_IGNORES.md pattern for ad-hoc CLI ignores, and the quarterly review cadence are in references/ecosystem-config-and-triage.md.

Step 7 - CI integration patterns

# Fast first-line gate
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      # npm
      - if: hashFiles('package-lock.json') != ''
        run: npm audit --audit-level=high
      # Python
      - if: hashFiles('requirements.txt') != ''
        run: pip-audit -r requirements.txt
      # Maven
      - if: hashFiles('pom.xml') != ''
        run: mvn dependency-check:check
      # Rust
      - if: hashFiles('Cargo.lock') != ''
        run: cargo audit
      # Ruby
      - if: hashFiles('Gemfile.lock') != ''
        run: bundle-audit check --update

The if: hashFiles(...) pattern auto-skips ecosystems not present in the repo.

Anti-patterns

Anti-patternWhy it failsFix
npm audit without --audit-levelLow-severity noise overwhelms; team disablesStart with --audit-level=high (Step 1)
npm audit fix --force in CIBumps majors silently; breaks buildsManual review for force-fix; never in CI
Suppress without Re-review-datePermanent debtMandatory template (Step 6)
Skip --update for bundle-auditStale advisory DB; misses recent CVEsAlways --update (Step 5)
Ignore bundle-audit check exit codeFindings invisibleLet exit code propagate to CI
Use only native audit; skip Snyk/OSVPer-ecosystem-DB blind spotsLayered (Step 1 cross-ref)

Limitations

  • Per-ecosystem DB coverage varies; one ecosystem's feed may carry a CVE another lacks.
  • No reachability analysis: every CVE on a declared dep counts even if the vulnerable function isn't called.
  • Maven Dependency-Check requires an NVD data sync (slow first run, ~1 GB cache).
  • npm audit fix --force bumps majors; always manual-review before applying.
  • Yarn classic (1.x) and pnpm have slightly different audit output shapes vs npm.

References

  • Native audit docs: npm (docs.npmjs.com/cli/v10/commands/npm-audit), Yarn (yarnpkg.com/cli/npm/audit), pnpm (pnpm.io/cli/audit), pip-audit (pypa.github.io/pip-audit), OWASP Dependency-Check (jeremylong.github.io/DependencyCheck), cargo-audit (rustsec.org), bundler-audit (github.com/rubysec/bundler-audit)
  • Per-ecosystem config, triage templates, and output aggregation: references/ecosystem-config-and-triage.md
  • Rust + Ruby depth (audit.toml, .bundler-audit.yml, SARIF, binary auditing, Rake/Actions wiring): references/cargo-bundle-audit.md
  • snyk-test, osv-scanner, dependabot-config, renovate-config - sister tools
Reference

cargo audit (Rust) + bundle-audit (Ruby) - deep reference

View source (opens in a new tab)

Companion reference for npm-pip-maven-audit Steps 4-5. SKILL.md keeps the one-command scans; consult this file for committed suppression files, exit-code semantics, SARIF output, binary auditing, and Rake / GitHub Actions wiring.

cargo audit (Rust)

cargo-audit scans Cargo.lock against the RustSec Advisory Database (rustsec.org/advisories) for vulnerable, unmaintained, unsound, and yanked crates. Minimum Rust version: 1.74 per rustsec-readme (opens in new window).

cargo install cargo-audit --locked
# enable the `cargo audit fix` subcommand:
cargo install cargo-audit --features=fix --locked

cargo audit                       # run at the workspace root (Cargo.lock)
cargo audit -f path/to/Cargo.lock # explicit lockfile path
cargo audit --no-fetch            # skip advisory-db fetch (air-gapped)

First run clones the advisory DB into ~/.cargo/advisory-db (rustsec-readme (opens in new window)).

Exit codes and --deny flags

CodeMeaning
0No vulnerabilities / denial criteria not triggered
1Vulnerabilities found matching denial criteria
2Execution error (missing lockfile, DB fetch failure)

--deny turns advisory categories into hard failures; --deny warnings is the catch-all that enables all denial categories (cargo-audit audit.rs (opens in new window)):

cargo audit --deny warnings                      # fail on any vulnerability
cargo audit --deny unmaintained --deny unsound   # per-category hard fail
cargo audit --deny yanked

Output formats

--format supports terminal (default), json, and sarif (rustsec-readme (opens in new window)):

cargo audit --format json > cargo-audit.json
cargo audit --format sarif > cargo-audit.sarif   # GitHub Code Scanning upload

.cargo/audit.toml suppression

Persistent suppression belongs in a committed .cargo/audit.toml, not CLI --ignore flags (not auditable in git). Per the audit.toml example (opens in new window):

# .cargo/audit.toml
[advisories]
ignore = ["RUSTSEC-2024-0999"]
# RUSTSEC-2024-0999: serde_cbor unmaintained; test fixtures only.
# Approved-by: alice@example.com  Re-review-date: 2026-09-30
informational_warnings = ["unmaintained", "unsound"]
severity_threshold = "medium"     # none | low | medium | high | critical

[output]
format = "terminal"               # terminal | json | sarif
deny = ["warnings"]               # mirrors --deny flags
show_tree = true

[database]
fetch = false                     # offline / air-gapped builds
stale = false

cargo audit fix

cargo audit fix --dry-run   # preview
cargo audit fix             # update Cargo.toml constraints + cargo update

cargo audit fix is experimental per rustsec-readme (opens in new window); it cannot resolve conflicting version constraints. Always run cargo test after.

Binary auditing

cargo install cargo-auditable
cargo auditable build --release          # embeds Cargo.lock metadata
cargo audit bin target/release/my-app    # audit the compiled binary

Binaries built without cargo-auditable have no embedded metadata and cannot be audited (rustsec-readme (opens in new window)).

GitHub Actions

The official rustsec/audit-check (opens in new window) action wraps cargo audit, fails the check run on any security advisory, and opens a GitHub Issue per advisory on scheduled runs:

on:
  push:
    paths: ['**/Cargo.toml', '**/Cargo.lock']
  schedule:
    - cron: '0 0 * * *'
jobs:
  security_audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

SARIF upload alongside:

      - run: cargo audit --format sarif > cargo-audit.sarif || true
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: cargo-audit.sarif }

Rust-specific limitations

  • RustSec DB covers crates.io; vendored / path-dependency crates are not covered.
  • Library crates must commit Cargo.lock (or cargo generate-lockfile in CI) or there is nothing to scan.
  • OSV.dev imports RustSec advisories in real time; pair with osv-scanner for cross-DB consensus.

bundle-audit (Ruby)

bundler-audit (ba-readme (opens in new window)) scans Gemfile.lock against the ruby-advisory-db (opens in new window) - a community-maintained YAML corpus of CVE / GHSA / OSVDB advisories under gems/ (per-gem) and rubies/ (runtimes). It checks two classes of issues: vulnerable gem versions and insecure sources (http:// / git:// URIs).

gem install bundler-audit
bundle-audit update              # one-time advisory-db clone/sync
bundle-audit check --update      # refresh + scan (always --update in CI)
bundle-audit check --no-update   # fully offline once the DB is synced

Output flags (ba-readme (opens in new window)):

FlagOutput
--format jsonJSON for multi-tool SCA triage
--output FILEWrite to file (CI artifact)
--gemfile-lock PATHNon-default lockfile path

.bundler-audit.yml waivers

Committed suppression file at the project root (ba-readme (opens in new window)); the ignore array takes CVE / GHSA / OSVDB identifiers. Every ignore MUST carry an inline justification - reachability finding, approver, re-review date; reviewers treat undocumented ignores as unapproved:

---
# Suppressions last reviewed: 2026-06-04  Re-review by: 2026-09-04
ignore:
  # CVE-2024-1234: vulnerable function not reachable; foo-gem used only in
  # test fixtures. Verified via grep + code review. Approved: alice@example.com
  - CVE-2024-1234

Per-run --ignore CVE-2024-1234 flags are for temporary workarounds only; they have no expiry mechanism - enforce a quarterly review cadence in process.

Rake integration

Per ba-readme (opens in new window):

require 'bundler/audit/task'
Bundler::Audit::Task.new
task default: %w[spec bundle:audit]   # audit gates the local test run

Adds rake bundle:audit and rake bundle:audit:update; bundle:audit exits non-zero on findings.

CI gating

bundle-audit check exits 0 clean, non-zero on findings (ba-readme (opens in new window)):

jobs:
  bundle-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with: { bundler-cache: true }
      - run: gem install bundler-audit
      - run: bundle-audit check --update --format json --output bundle-audit.json
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: bundle-audit, path: bundle-audit.json }

Ruby-specific limitations

  • Reads Gemfile.lock only; gems loaded outside Bundler are invisible.
  • ruby-advisory-db is community-maintained; unfiled advisories are absent.
  • Pair with osv-scanner for OSV.dev cross-DB coverage.
Reference

Per-ecosystem audit config, triage, and aggregation

View source (opens in a new tab)

Deep reference for the npm-pip-maven-audit SKILL.md. SKILL.md keeps the primary scan command per ecosystem and the CI hashFiles gate; this file holds the speed/coverage tradeoffs, the Maven plugin block, the full suppression templates, and output aggregation.

Native audit vs unified scanner tradeoffs

PropertyNative auditSnyk / OSV
Speed<5s typical10s - 60s
DB coveragePer-ecosystem onlyCross-ecosystem aggregated
False-positive triagePer-ecosystem CLIUnified config
Reachability analysisNoneNone (most tools)
CI integrationBuilt into package managerPer-tool action

Native audit catches the high-confidence per-ecosystem feed quickly; the unified scanner catches cross-ecosystem aggregations and waivers.

Maven OWASP Dependency-Check plugin

Maven's audit story is the OWASP Dependency-Check plugin (no native mvn audit):

<!-- pom.xml -->
<plugin>
  <groupId>org.owasp</groupId>
  <artifactId>dependency-check-maven</artifactId>
  <version>10.0.4</version>
  <executions>
    <execution>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <failBuildOnCVSS>7.0</failBuildOnCVSS>
    <suppressionFile>dependency-check-suppressions.xml</suppressionFile>
    <formats>
      <format>HTML</format>
      <format>JSON</format>
      <format>SARIF</format>
    </formats>
  </configuration>
</plugin>

For Gradle: the same plugin via the org.owasp.dependencycheck Gradle plugin. Source: jeremylong.github.io/DependencyCheck/dependency-check-maven/.

Suppression justification templates

Justification is mandatory in the suppression file or audit-skip list:

<!-- dependency-check-suppressions.xml (Maven) -->
<suppress>
  <notes>
    Reason: log4j-core 2.14.x is bundled but not loaded at runtime
            (verified via dependency tree analysis)
    Approved-by: alice@example.com
    Re-review-date: 2026-09-15
  </notes>
  <packageUrl regex="true">^pkg:maven/org\.apache\.logging\.log4j/log4j-core@2\.14\..*$</packageUrl>
  <vulnerabilityName>CVE-2021-44228</vulnerabilityName>
</suppress>

For ad-hoc CLI ignores (pip-audit --ignore-vuln, cargo audit --ignore), maintain a sibling AUDIT_IGNORES.md mapping each ID to reason + approver + re-review-date. Without the sibling file, the ignore is invisible to reviewers.

Cadence: every quarter, audit suppression entries; expired re-review dates remove entries.

Output aggregation

For downstream aggregation, output each tool's JSON to a stable filename:

npm audit --json > sca-npm.json || true        # || true: don't fail before triage
pip-audit --format json --output sca-pip.json || true
mvn dependency-check:check -Dformats=JSON
cargo audit --json > sca-cargo.json || true

The aggregation step normalizes each tool's schema + dedupes cross-tool findings.