Testland
Browse all skills & agents

bundle-audit-ruby

Use when a Ruby project has a Gemfile.lock and needs CVE/GHSA scanning or a CI SCA gate. Installs and runs bundler-audit against a Ruby Gemfile.lock, updating the ruby-advisory-db corpus, scanning for vulnerable gem versions and insecure sources, suppressing false positives via .bundler-audit.yml, and gating CI on non-zero exit. Ruby-only SCA scanner: for other ecosystems use npm-pip-maven-audit (multi-ecosystem dispatcher), snyk-test, or osv-scanner; cargo-audit-rust is the Rust analog; once findings exist, reachability-analyzer downranks unreachable gems - not this.

Install with skills.sh (any agent)

npx skills add testland/qa --skill bundle-audit-ruby
View source

bundle-audit-ruby

Overview

bundler-audit (github.com/rubysec/bundler-audit (opens in new window)) is a standalone Ruby gem that scans Gemfile.lock against the ruby-advisory-db (opens in new window): a community-maintained YAML corpus of CVE, GHSA, and OSVDB advisories for RubyGems and Ruby runtimes.

Differentiation vs. npm-pip-maven-audit: that skill treats bundle audit (the Bundler subcommand) as one line in a multi-ecosystem dispatcher; this skill covers the full bundler-audit workflow: advisory-db lifecycle, per-project suppression with justification, Rake task integration, and JSON output for downstream tooling.

When to use

  • Ruby project has a Gemfile.lock and needs CVE/GHSA scanning.
  • CI pipeline needs a fast, offline-capable SCA gate with no commercial license overhead.
  • Team needs per-project advisory suppression with auditable justification in source control.
  • Layering SCA: run bundler-audit for the Ruby-specific feed, pair with osv-scanner for OSV.dev cross-DB consensus.

Install

Per github.com/rubysec/bundler-audit (opens in new window):

gem install bundler-audit

No system-level dependencies beyond a Ruby environment. After install, run a one-time database fetch:

bundle-audit update

Per ruby-advisory-db (opens in new window), the corpus is a git repository of YAML files under gems/ (per-RubyGem advisories) and rubies/ (Ruby runtime advisories). bundle-audit update syncs the local clone of this repo. Subsequent bundle-audit check --no-update runs are fully offline.

Scan

Per github.com/rubysec/bundler-audit (opens in new window):

bundle-audit check --update

The --update flag refreshes the local ruby-advisory-db before scanning, ensuring the check sees the latest advisories. Where outbound git is restricted, pre-update in a build step and scan offline with bundle-audit check --no-update.

bundler-audit scans Gemfile.lock in the current directory and checks two classes of issues (github.com/rubysec/bundler-audit (opens in new window)):

  1. Vulnerable gem versions - gem + version matched against ruby-advisory-db advisories with patched-version ranges.
  2. Insecure sources - http:// and git:// source URIs that transmit without TLS.

Output flags (github.com/rubysec/bundler-audit (opens in new window)):

FlagOutput
(none)Default human-readable text
--format jsonJSON; suitable for multi-tool SCA triage
--output FILEWrite output to file instead of stdout
--gemfile-lock PATHScan a lockfile at a non-default path

JSON + file example, useful as a CI artifact:

bundle-audit check --update --format json --output bundle-audit.json

Suppress false positives

Per github.com/rubysec/bundler-audit (opens in new window), create .bundler-audit.yml at the project root and list advisory IDs to ignore:

---
ignore:
  - CVE-2024-1234
  - GHSA-xxxx-yyyy-zzzz

The ignore array takes CVE, GHSA, or OSVDB identifiers, committed to source control so suppressions are auditable in git history. Every ignore needs an inline justification comment (reachability finding, approver, re-review date); undocumented ignores are a code-smell reviewers should treat as unapproved. The mandatory justification template, per-run --ignore flags, and the quarterly re-review cadence are in references/bundle-audit-ci.md.

Worked example

A Rails service locks nokogiri 1.13.0 and rack 2.2.3. The scan (bundle-audit check --update, per Scan above) flags two advisories and exits non-zero:

Name: nokogiri
Version: 1.13.0
CVE: CVE-2022-24836
Criticality: High
Title: Nokogiri ReDoS on crafted input
Solution: upgrade to >= 1.13.4

Name: rack
Version: 2.2.3
CVE: CVE-2022-30122
Criticality: Medium
Title: Denial of Service in Rack multipart parsing
Solution: upgrade to >= 2.2.3.1

Vulnerabilities found!

Because the exit code is non-zero, a CI job running this step fails. Triage the two findings:

  1. nokogiri is reachable (it parses user-supplied XML). Patch it - bundle update nokogiri to 1.13.4 - and the advisory clears.
  2. rack multipart parsing is confirmed unreachable (the app rejects multipart requests at the edge). Suppress CVE-2022-30122 in .bundler-audit.yml using the justification template from Suppress false positives above.

Re-run offline (bundle-audit check --no-update): both findings resolved, exit 0, CI passes. Wire the gate into the pipeline per references/bundle-audit-ci.md.

Anti-patterns

Anti-patternWhy it failsFix
Skip --update in CILocal db may lag by weeks; misses recent advisoriesAlways --update in CI or pre-update in a dedicated step
--ignore flags hardcoded in CI YAMLNot auditable in git; no justification attachedMove to .bundler-audit.yml with inline comments
Scan with no Gemfile.lock committedbundler-audit reads only the lockfile; no lockfile means no scanCommit Gemfile.lock; failing to do so is an anti-pattern per ba-readme (opens in new window)
Suppress an advisory indefinitelyNo expiry signal; suppressions rot silentlyAdd re-review date in comment; enforce via quarterly review
Run bundler-audit only; skip cross-DB scannerruby-advisory-db covers Ruby ecosystem; OSV.dev may surface additional findingsPair with osv-scanner for cross-DB coverage

Limitations

  • Scans Gemfile.lock only; gems loaded outside Bundler (standalone require) are not visible to bundler-audit.
  • No reachability analysis: every advisory on a locked gem counts even if the vulnerable code path is not exercised.
  • ruby-advisory-db coverage is community-maintained (ruby-advisory-db (opens in new window)); advisories without a community submission are absent until filed.
  • --ignore has no built-in expiry mechanism; teams must enforce review cadence in process, not in tooling.

References

bundle-audit CI wiring and suppression waivers

View source (opens in new window)

bundle-audit CI wiring and suppression waivers

Deep reference for bundle-audit-ruby SKILL.md. Consult when writing a committed .bundler-audit.yml waiver, wiring the audit into Rake, or gating a CI pipeline on the exit code. SKILL.md keeps the install + first scan and points here for the rest.

Suppression waivers with justification

Create .bundler-audit.yml at the project root (ba-readme (opens in new window)). The ignore array takes CVE, GHSA, or OSVDB identifiers and is committed to source control, making every suppression auditable in git history.

Every ignore MUST carry an inline justification: the reachability finding, the approver, and a 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 suppression (not persisted) is for temporary automated workarounds only; prefer the committed file for anything durable:

bundle-audit check --ignore CVE-2024-1234 --ignore GHSA-xxxx-yyyy-zzzz

--ignore has no built-in expiry. Enforce a review cadence in process: every quarter, audit ignore: entries and drop any whose re-review date has passed.

Rake integration

Register the audit tasks in your Rakefile (ba-readme (opens in new window)):

require 'bundler/audit/task'
Bundler::Audit::Task.new

This adds rake bundle:audit (runs the check) and rake bundle:audit:update (updates the advisory db). Chain the audit into the default task so it gates the local test run:

task default: %w[spec bundle:audit]

bundle:audit exits non-zero on findings, so it fails the default run.

Exit codes and CI gating

Per ba-readme (opens in new window), bundle-audit check exits 0 when no vulnerabilities or insecure sources are found, and non-zero when at least one is found - the signal CI keys on.

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

The if: always() upload keeps findings accessible even when the audit step fails, enabling triage without re-running the workflow.

Related skills

cargo-audit-rust

Configures and runs cargo-audit against the RustSec Advisory Database for Rust projects; covers `cargo audit` (vulnerability scan), `cargo audit fix` (automated dependency updates), `--deny unmaintained|unsound|yanked|warnings` exit-code control, `audit.toml` per-advisory suppression with mandatory `expires` + `reason`, SARIF output for GitHub Code Scanning upload, and `rustsec/audit-check` GitHub Actions integration. Use when the codebase has a Cargo.lock and needs Rust-specific SCA beyond what the multi-ecosystem npm-pip-maven-audit wrapper provides.

cve-exploitability-triage

Ranks known CVE findings by real-world exploitability instead of severity alone: enriches each CVE with its EPSS probability (the chance exploitation activity is observed in the next 30 days) and CISA KEV membership (confirmed exploited in the wild), applies OpenVEX status assertions to set aside vulnerabilities the product is not affected by, applies a reachability heuristic for vulnerable code that is never called, and assigns every finding to one of four buckets (Fix-Now, Fix-This-Sprint, Fix-Backlog, Accept-Risk) using documented EPSS thresholds. Treats a CISA KEV listing as non-waivable under any justification. Use when a dependency, container image, or SBOM vulnerability scan has produced more CVEs than the team can fix in the available window and someone has to decide which ones get fixed first and which can wait.

dependabot-config

Reference for `.github/dependabot.yml` - GitHub-native dependency-update orchestrator. Required keys (`version: 2`, `updates[]` array) plus per-update fields (`package-ecosystem`, `directory` / `directories`, `schedule.interval`); common optional fields (`ignore`, `groups`, `allow`, `labels`, `milestone`, `open-pull-requests-limit`, `target-branch`, `vendor`, `versioning-strategy`, `assignees`, `commit-message`); auto-rebase + grouped-PR + security-only updates. Use when authoring or reviewing Dependabot configs in GitHub-hosted repos.

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), `bundle audit` (Ruby Bundler); fastest no-install-required SCA option. Use when the team wants fast, no-extra-tooling SCA in CI as a first line of defense, or pairs with snyk/osv-scanner for layered coverage.

osv-scanner

Configures and runs Google OSV-Scanner - open-source SCA against the OSV.dev vulnerability database; supports `osv-scanner scan -r ./` recursive scan + per-lockfile scan via `-L package-lock.json`; SBOM input (CycloneDX / SPDX) for non-standard package managers; `--format json|sarif|markdown|vertical|html` output; suppressions via `osv-scanner.toml` config. Use when the team needs OSS-native SCA without commercial-license overhead, or wants a second-opinion DB pair with Snyk's commercial DB.

reachability-analyzer

Runs dead-dependency analysis across JS, Python, and Rust projects using ecosystem-native static tools (`depcheck`/`knip` for JS, `vulture` for Python, `cargo-machete` for Rust), then cross-references the unused-dependency list against SCA findings to downrank vulns in code that is never loaded. Use when SCA output (from `osv-scanner`, `snyk-test`, or `npm-pip-maven-audit`) is too noisy to triage and the team needs to separate unreachable CVEs from exploitable ones before sprint planning; sibling cve-exploitability-triage ranks by EPSS/KEV exploitation signal, not code reachability.

renovate-config

Reference for `renovate.json` - Mend Renovate dependency-update orchestrator (multi-platform: GitHub / GitLab / Bitbucket / Azure DevOps / Gitea); top-level keys (`extends` for preset references, `schedule`, `prConcurrentLimit`, `vulnerabilityAlerts`); `packageRules[]` array with `matchPackageNames` / `matchUpdateTypes` / `automerge` matching; `ignoreDeps`, `addLabels`, `automergeSchedule`. Use when authoring or reviewing Renovate configs in any repo platform Renovate supports.

snyk-test

Configures and runs Snyk, a commercial multi-mode scanner: snyk test for SCA (dependency scanning), snyk code test for SAST (code security scanning), snyk container test for container images, snyk iac test for IaC (infrastructure-as-code), snyk monitor for continuous new-vuln alerts; policy file .snyk for ignore + patch. Use when the team has a Snyk license and needs SCA (dependency scanning) or continuous vuln monitoring; for open-source scanning without a Snyk license, prefer osv-scanner.