From d964d9e0fa7b2f22226094637e39e8d73934f5db Mon Sep 17 00:00:00 2001 From: kallal79 Date: Wed, 11 Mar 2026 00:26:29 +0530 Subject: [PATCH] Fix #403: exit with error when --gemfile-lock is not a valid lock file Bundler::LockfileParser silently accepts any file (Gemfile, README.md, etc.) and returns empty results, causing bundle-audit to exit 0 with 'No vulnerabilities found' even when given the wrong file. Changes: - Add Scanner::InvalidGemfileLock exception class - Add LOCKFILE_HEADER_RE regexp to validate lock file content before parsing - Raise InvalidGemfileLock in Scanner#initialize when file does not start with a recognised Bundler lockfile section header - Rescue InvalidGemfileLock in CLI#check and exit with status 1 plus a descriptive stderr message - Add spec for Scanner#initialize raising InvalidGemfileLock - Add specs for CLI#check printing error to stderr and exiting with 1 Fixes https://github.com/rubysec/bundler-audit/issues/403 --- lib/bundler/audit/cli.rb | 3 +++ lib/bundler/audit/scanner.rb | 14 +++++++++++++- spec/cli_spec.rb | 31 +++++++++++++++++++++++++++++++ spec/scanner_spec.rb | 10 ++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/bundler/audit/cli.rb b/lib/bundler/audit/cli.rb index 97979ecc..466160d2 100644 --- a/lib/bundler/audit/cli.rb +++ b/lib/bundler/audit/cli.rb @@ -71,6 +71,9 @@ def check(dir=Dir.pwd) rescue Bundler::GemfileLockNotFound => exception say exception.message, :red exit 1 + rescue Scanner::InvalidGemfileLock => exception + say_error exception.message, :red + exit 1 end report = scanner.report(ignore: options.ignore) diff --git a/lib/bundler/audit/scanner.rb b/lib/bundler/audit/scanner.rb index f0e79a30..1613931c 100644 --- a/lib/bundler/audit/scanner.rb +++ b/lib/bundler/audit/scanner.rb @@ -36,6 +36,12 @@ module Audit # class Scanner + # Raised when the given file is not a valid Bundler lockfile. + class InvalidGemfileLock < StandardError; end + + # Regexp matching the first line of every valid Bundler lockfile. + LOCKFILE_HEADER_RE = /\A(GEM|GIT|PATH|PLUGIN SOURCE|PLATFORMS|DEPENDENCIES|BUNDLED WITH)\b/ + # The advisory database. # # @return [Database] @@ -84,7 +90,13 @@ def initialize(root=Dir.pwd,gemfile_lock='Gemfile.lock',database=Database.new,co raise(Bundler::GemfileLockNotFound,"Could not find #{gemfile_lock.inspect} in #{@root.inspect}") end - @lockfile = LockfileParser.new(File.read(gemfile_lock_path)) + lock_file_content = File.read(gemfile_lock_path) + + unless lock_file_content =~ LOCKFILE_HEADER_RE + raise(InvalidGemfileLock,"#{gemfile_lock.inspect} is not a valid Gemfile.lock") + end + + @lockfile = LockfileParser.new(lock_file_content) config_dot_file_full_path = File.absolute_path(config_dot_file, @root) diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 11e4f256..0d1de922 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -161,4 +161,35 @@ end end end + + describe "#check" do + context "when --gemfile-lock is not a valid lock file" do + let(:bundle_dir) { File.expand_path(File.join('spec','bundle','unpatched_gems')) } + let(:database_path) { Fixtures::Database::PATH } + + it "must print an error message to stderr" do + expect { + begin + described_class.start ['check', bundle_dir, + '--gemfile-lock', 'Gemfile', + '--database', database_path, + '--no-update'] + rescue SystemExit + end + }.to output(/is not a valid Gemfile\.lock/).to_stderr + end + + it "must exit with status 1" do + expect { + described_class.start ['check', bundle_dir, + '--gemfile-lock', 'Gemfile', + '--database', database_path, + '--no-update'] + }.to raise_error(SystemExit) do |error| + expect(error.success?).to eq(false) + expect(error.status).to eq(1) + end + end + end + end end diff --git a/spec/scanner_spec.rb b/spec/scanner_spec.rb index afde727c..4cbfb600 100644 --- a/spec/scanner_spec.rb +++ b/spec/scanner_spec.rb @@ -7,6 +7,16 @@ subject { described_class.new(directory) } + describe "#initialize" do + context "when given a non-lock file as gemfile_lock" do + it "should raise InvalidGemfileLock" do + expect { + described_class.new(directory, 'Gemfile') + }.to raise_error(Scanner::InvalidGemfileLock,/is not a valid Gemfile\.lock/) + end + end + end + describe "#scan" do it "should yield results" do results = []