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
Step 1 - Install
Per github.com/rubysec/bundler-audit:
gem install bundler-auditNo system-level dependencies beyond a Ruby environment. After install, run a one-time database fetch:
bundle-audit updatePer 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 --updateThe --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-updatebundler-audit scans Gemfile.lock in the current directory and checks two classes of issues (github.com/rubysec/bundler-audit):
Step 3 - Output formats
Per github.com/rubysec/bundler-audit:
| Flag | Output |
|---|---|
| (none) | Default human-readable text |
--format json | JSON; suitable for sca-prioritizer |
--output FILE | Write output to file instead of stdout |
JSON + file example, useful as a CI artifact:
bundle-audit check --update --format json --output bundle-audit.jsonTo scan a Gemfile.lock at a non-default path (github.com/rubysec/bundler-audit):
bundle-audit check --gemfile-lock path/to/Gemfile.custom.lock --updateStep 4 - Advisory suppression
Per github.com/rubysec/bundler-audit, create .bundler-audit.yml at the project root:
---
ignore:
- CVE-2024-1234
- GHSA-xxxx-yyyy-zzzzThe 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-1234Inline 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-zzzzUse --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.newThis registers two Rake tasks:
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:
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.jsonThe if: always() upload ensures findings are accessible even when the audit step fails, enabling triage without re-running the workflow.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Skip --update in CI | Local db may lag by weeks; misses recent advisories | Always --update in CI or pre-update in a dedicated step |
--ignore flags hardcoded in CI YAML | Not auditable in git; no justification attached | Move to .bundler-audit.yml with inline comments |
Scan with no Gemfile.lock committed | bundler-audit reads only the lockfile; no lockfile means no scan | Commit Gemfile.lock; failing to do so is an anti-pattern per ba-readme |
| Suppress an advisory indefinitely | No expiry signal; suppressions rot silently | Add re-review date in comment; enforce via quarterly review |
| Run bundler-audit only; skip cross-DB scanner | ruby-advisory-db covers Ruby ecosystem; OSV.dev may surface additional findings | Pair with osv-scanner for cross-DB coverage |