Testland
Browse all skills & agents

bundle-audit-ruby

Installs and runs bundler-audit against a Ruby Gemfile.lock, updating the ruby-advisory-db advisory corpus, scanning for vulnerable gem versions and insecure sources, suppressing false positives via .bundler-audit.yml with justification requirements, and gating CI on non-zero exit. Use when a Ruby project needs dedicated Gemfile.lock SCA beyond the single-ecosystem touch bundler-audit receives in npm-pip-maven-audit.

bundle-audit-ruby

Overview

bundler-audit (github.com/rubysec/bundler-audit) is a standalone Ruby gem that scans Gemfile.lock against the ruby-advisory-db: 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.

Step 1 - Install

Per github.com/rubysec/bundler-audit:

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, 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.

Step 2 - Basic scan

Per github.com/rubysec/bundler-audit:

bundle-audit check --update

The --update flag refreshes the local ruby-advisory-db before scanning, ensuring the check sees the latest advisories. In environments where outbound git is restricted, pre-update during a build step and scan 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):

  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.

Step 3 - Output formats

Per github.com/rubysec/bundler-audit:

FlagOutput
(none)Default human-readable text
--format jsonJSON; suitable for sca-prioritizer
--output FILEWrite output to file instead of stdout

JSON + file example, useful as a CI artifact:

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

To scan a Gemfile.lock at a non-default path (github.com/rubysec/bundler-audit):

bundle-audit check --gemfile-lock path/to/Gemfile.custom.lock --update

Step 4 - Advisory suppression

Per github.com/rubysec/bundler-audit, create .bundler-audit.yml at the project root:

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

The ignore array takes CVE, GHSA, or OSVDB identifiers. These are committed to source control, making suppressions auditable in git history.

Justification template (mandatory in team practice):

---
# 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

Inline comments in YAML document the reachability analysis, approver, and re-review date. Suppressions without comments are a code-smell: reviewers should treat undocumented ignores as unapproved.

Per-run suppression (not persisted):

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

Use --ignore flags only in automated temporary workarounds; prefer .bundler-audit.yml for anything committed.

Step 5 - Rake integration

Per github.com/rubysec/bundler-audit, add to your Rakefile:

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

This registers two Rake tasks:

  • rake bundle:audit - runs the audit check.
  • rake bundle:audit:update - updates the local advisory database.

Rake integration composes with existing test pipelines:

task default: %w[spec bundle:audit]

The bundle:audit task exits non-zero on findings, making it a natural gate before a spec run or within a CI task matrix.

Step 6 - Exit codes and CI gating

Per github.com/rubysec/bundler-audit:

  • Exit 0 - no vulnerabilities or insecure sources found.
  • Exit non-zero - at least one vulnerability or insecure source found.

GitHub Actions example:

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 ensures findings are accessible even when the audit step fails, enabling triage without re-running the workflow.

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
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); 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