From 826dd4c31ce1db1287b87ec1ef8d978795ae6c5f Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Wed, 13 Apr 2022 17:23:51 -0500 Subject: [PATCH 1/7] Track the identifiers seen during scanning on resulting Report --- lib/bundler/audit/report.rb | 6 ++++++ lib/bundler/audit/scanner.rb | 4 ++++ spec/scanner_spec.rb | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/lib/bundler/audit/report.rb b/lib/bundler/audit/report.rb index 51847f5f..b4e9bddc 100644 --- a/lib/bundler/audit/report.rb +++ b/lib/bundler/audit/report.rb @@ -37,6 +37,11 @@ class Report # @return [Array] attr_reader :unpatched_gems + # Identifiers of all seen vulnerabilities (including ignored vulns). + # + # @return [Set] + attr_accessor :seen_identifiers + # # Initializes the report. # @@ -49,6 +54,7 @@ def initialize(results=[]) @results = [] @insecure_sources = [] @unpatched_gems = [] + @seen_identifiers = Set.new results.each { |result| self << result } end diff --git a/lib/bundler/audit/scanner.rb b/lib/bundler/audit/scanner.rb index 731a93ee..6575238a 100644 --- a/lib/bundler/audit/scanner.rb +++ b/lib/bundler/audit/scanner.rb @@ -121,6 +121,8 @@ def report(options={}) yield result if block_given? end + report.seen_identifiers = @seen + return report end @@ -222,9 +224,11 @@ def scan_specs(options={}) else config.ignore end + @seen = Set.new @lockfile.specs.each do |gem| @database.check_gem(gem) do |advisory| + @seen.merge(advisory.identifiers) is_ignored = ignore.intersect?(advisory.identifiers.to_set) next if is_ignored diff --git a/spec/scanner_spec.rb b/spec/scanner_spec.rb index 6c742a77..055603e7 100644 --- a/spec/scanner_spec.rb +++ b/spec/scanner_spec.rb @@ -116,6 +116,22 @@ expect(report.results).to all(be_kind_of(Bundler::Audit::Results::Result)) end + it "should return a Report containing all identifiers seen during scanning" do + report = subject.report + expected_identifiers = %w[CVE-2013-0155 CVE-2013-0276 CVE-2013-1854 CVE-2013-1856 CVE-2014-3482 CVE-2015-3227 CVE-2015-7577 OSVDB-108664 OSVDB-89025 OSVDB-90072 OSVDB-91451 OSVDB-91453] + + expect(report.seen_identifiers).to contain_exactly(*expected_identifiers) + end + + context "when some identifiers are ignored" do + it "should return a Report containing the seen but ignored identifiers" do + ignored_identifiers = %w[CVE-2013-0155 OSVDB-108664] + report = subject.report(ignore: ignored_identifiers) + + expect(report.seen_identifiers).to include(*ignored_identifiers) + end + end + context "when given a block" do it "should yield results" do results = [] From de8c6039df82823d15930cd9788967f3de942d80 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Wed, 13 Apr 2022 17:30:35 -0500 Subject: [PATCH 2/7] Ignore a vulnerable identifier in the spec --- spec/scanner_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/scanner_spec.rb b/spec/scanner_spec.rb index 055603e7..b8120767 100644 --- a/spec/scanner_spec.rb +++ b/spec/scanner_spec.rb @@ -36,12 +36,12 @@ end context "when the :ignore option is given" do - subject { super().scan(ignore: ['OSVDB-89026']) } + subject { super().scan(ignore: ['OSVDB-89025']) } it "should ignore the specified advisories" do ids = subject.map { |result| result.advisory.id } - expect(ids).not_to include('OSVDB-89026') + expect(ids).not_to include('OSVDB-89025') end end end From b46033c2e66179245e7e9738bafe98ac096beeec Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Thu, 14 Apr 2022 13:20:13 -0500 Subject: [PATCH 3/7] Exit with an error code if ignored identifiers are not seen Users need to opt-in to this behavior via the strict_ignore flag --- lib/bundler/audit/cli.rb | 2 ++ spec/cli_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/bundler/audit/cli.rb b/lib/bundler/audit/cli.rb index 5f496f11..94f98d19 100644 --- a/lib/bundler/audit/cli.rb +++ b/lib/bundler/audit/cli.rb @@ -45,6 +45,7 @@ class CLI < ::Thor method_option :gemfile_lock, type: :string, aliases: '-G', default: 'Gemfile.lock' method_option :output, type: :string, aliases: '-o' + method_option :strict_ignore, type: :boolean, default: false def check(dir=Dir.pwd) unless File.directory?(dir) @@ -86,6 +87,7 @@ def check(dir=Dir.pwd) output.close if options[:output] exit(1) if report.vulnerable? + exit(1) if options[:strict_ignore] && !report.seen_identifiers.superset?(Set.new(options.ignore)) end desc 'stats', 'Prints ruby-advisory-db stats' diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index c240b02d..7fb03d96 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -175,4 +175,26 @@ end end end + + describe "#check" do + context "--strict-ignore" do + let(:directory) { File.join("spec", "bundle", "secure") } + + subject do + described_class.new([], strict_ignore: true, ignore: ["CVE-2006-1982"], format: "text", database: File.expand_path("fixtures/database/", File.dirname(__FILE__)), gemfile_lock: "Gemfile.lock", config: ".bundler-audit.yml") + end + + + context "when the ignored CVE is not found" do + it "exits with an error status code" do + expect { + subject.check(directory) + }.to raise_error(SystemExit) do |error| + expect(error.success?).to eq(false) + expect(error.status).to eq(1) + end + end + end + end + end end From d4752170c1a6f42e60b5a874933a23481393abd2 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Thu, 14 Apr 2022 13:34:11 -0500 Subject: [PATCH 4/7] Add the set of ignored identifiers to the report --- lib/bundler/audit/report.rb | 6 ++++++ lib/bundler/audit/scanner.rb | 6 ++++++ spec/scanner_spec.rb | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/lib/bundler/audit/report.rb b/lib/bundler/audit/report.rb index b4e9bddc..ad1dfd98 100644 --- a/lib/bundler/audit/report.rb +++ b/lib/bundler/audit/report.rb @@ -42,6 +42,11 @@ class Report # @return [Set] attr_accessor :seen_identifiers + # Identifiers that were omitted from results + # + # @return [Set] + attr_accessor :ignored_identifiers + # # Initializes the report. # @@ -55,6 +60,7 @@ def initialize(results=[]) @insecure_sources = [] @unpatched_gems = [] @seen_identifiers = Set.new + @ignored_identifiers = Set.new results.each { |result| self << result } end diff --git a/lib/bundler/audit/scanner.rb b/lib/bundler/audit/scanner.rb index 6575238a..b436665a 100644 --- a/lib/bundler/audit/scanner.rb +++ b/lib/bundler/audit/scanner.rb @@ -122,6 +122,12 @@ def report(options={}) end report.seen_identifiers = @seen + report.ignored_identifiers = ignore = if options[:ignore] + Set.new(options[:ignore]) + else + config.ignore + end + return report end diff --git a/spec/scanner_spec.rb b/spec/scanner_spec.rb index b8120767..668a2f33 100644 --- a/spec/scanner_spec.rb +++ b/spec/scanner_spec.rb @@ -130,6 +130,13 @@ expect(report.seen_identifiers).to include(*ignored_identifiers) end + + it "should return a Report listing the ignored identifiers" do + ignored_identifiers = %w[CVE-2013-0155 OSVDB-108664] + report = subject.report(ignore: ignored_identifiers) + + expect(report.ignored_identifiers).to contain_exactly(*ignored_identifiers) + end end context "when given a block" do From 61212bde86d714bbbbe49420c172875624a89de7 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Thu, 14 Apr 2022 14:03:14 -0500 Subject: [PATCH 5/7] Add helper methods to the report for retrieving unseen ignored identifiers --- lib/bundler/audit/cli.rb | 2 +- lib/bundler/audit/report.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/bundler/audit/cli.rb b/lib/bundler/audit/cli.rb index 94f98d19..d1c12966 100644 --- a/lib/bundler/audit/cli.rb +++ b/lib/bundler/audit/cli.rb @@ -87,7 +87,7 @@ def check(dir=Dir.pwd) output.close if options[:output] exit(1) if report.vulnerable? - exit(1) if options[:strict_ignore] && !report.seen_identifiers.superset?(Set.new(options.ignore)) + exit(1) if options[:strict_ignore] && report.unseen_ignored_identifiers? end desc 'stats', 'Prints ruby-advisory-db stats' diff --git a/lib/bundler/audit/report.rb b/lib/bundler/audit/report.rb index ad1dfd98..117465c9 100644 --- a/lib/bundler/audit/report.rb +++ b/lib/bundler/audit/report.rb @@ -145,6 +145,14 @@ def vulnerable_gems @unpatched_gems.map(&:gem) end + def unseen_ignored_identifiers? + unseen_ignored_identifiers.any? + end + + def unseen_ignored_identifiers + @ignored_identifiers - @seen_identifiers + end + # # @return [Hash{Symbol => Object}] # From 47e8191e3110121bcc99c318e3a992a98abda8b9 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Thu, 14 Apr 2022 14:03:31 -0500 Subject: [PATCH 6/7] Print the unseen ignored identifiers when outputting text format --- lib/bundler/audit/cli/formats/text.rb | 4 ++++ spec/cli/formats/text_spec.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/bundler/audit/cli/formats/text.rb b/lib/bundler/audit/cli/formats/text.rb index 966544a4..a234b1bf 100644 --- a/lib/bundler/audit/cli/formats/text.rb +++ b/lib/bundler/audit/cli/formats/text.rb @@ -47,6 +47,10 @@ def print_report(report,output=$stdout) end end + if report.unseen_ignored_identifiers? + print_warning "These identifiers were ignored but not found: #{report.unseen_ignored_identifiers.to_a.join(", ")}" + end + if report.vulnerable? say "Vulnerabilities found!", :red else diff --git a/spec/cli/formats/text_spec.rb b/spec/cli/formats/text_spec.rb index 88f5de8e..a47a8e9a 100644 --- a/spec/cli/formats/text_spec.rb +++ b/spec/cli/formats/text_spec.rb @@ -266,6 +266,19 @@ end end + context "when there are unseen ignored identifiers" do + let(:report) do + Bundler::Audit::Report.new.tap do |r| + r.ignored_identifiers = Set.new(unseen) + end + end + let(:unseen) { %w[CVE-2020-8833 OSVDB-108664] } + + it "must print the unseen identifiers" do + expect(output_lines).to include("These identifiers were ignored but not found: #{unseen.join(", ")}") + end + end + it "must restore $stdout" do expect($stdout).to_not be(stdout) end From 537a6224b66e70dd03ed6f88eddc9e8e61a1af97 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Thu, 14 Apr 2022 14:19:15 -0500 Subject: [PATCH 7/7] Fix rubocop violations --- lib/bundler/audit/cli/formats/text.rb | 2 +- lib/bundler/audit/scanner.rb | 11 +++++------ spec/cli/formats/text_spec.rb | 2 +- spec/cli_spec.rb | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/bundler/audit/cli/formats/text.rb b/lib/bundler/audit/cli/formats/text.rb index a234b1bf..20bc7d72 100644 --- a/lib/bundler/audit/cli/formats/text.rb +++ b/lib/bundler/audit/cli/formats/text.rb @@ -48,7 +48,7 @@ def print_report(report,output=$stdout) end if report.unseen_ignored_identifiers? - print_warning "These identifiers were ignored but not found: #{report.unseen_ignored_identifiers.to_a.join(", ")}" + print_warning "These identifiers were ignored but not found: #{report.unseen_ignored_identifiers.to_a.join(', ')}" end if report.vulnerable? diff --git a/lib/bundler/audit/scanner.rb b/lib/bundler/audit/scanner.rb index b436665a..c520dc0a 100644 --- a/lib/bundler/audit/scanner.rb +++ b/lib/bundler/audit/scanner.rb @@ -122,12 +122,11 @@ def report(options={}) end report.seen_identifiers = @seen - report.ignored_identifiers = ignore = if options[:ignore] - Set.new(options[:ignore]) - else - config.ignore - end - + report.ignored_identifiers = if options[:ignore] + Set.new(options[:ignore]) + else + config.ignore + end return report end diff --git a/spec/cli/formats/text_spec.rb b/spec/cli/formats/text_spec.rb index a47a8e9a..56a30c25 100644 --- a/spec/cli/formats/text_spec.rb +++ b/spec/cli/formats/text_spec.rb @@ -275,7 +275,7 @@ let(:unseen) { %w[CVE-2020-8833 OSVDB-108664] } it "must print the unseen identifiers" do - expect(output_lines).to include("These identifiers were ignored but not found: #{unseen.join(", ")}") + expect(output_lines).to include("These identifiers were ignored but not found: #{unseen.join(', ')}") end end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 7fb03d96..0e47a1e7 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -184,7 +184,6 @@ described_class.new([], strict_ignore: true, ignore: ["CVE-2006-1982"], format: "text", database: File.expand_path("fixtures/database/", File.dirname(__FILE__)), gemfile_lock: "Gemfile.lock", config: ".bundler-audit.yml") end - context "when the ignored CVE is not found" do it "exits with an error status code" do expect {