Skip to content

Commit

Permalink
Merge pull request #133 from serranos/add-bundle-audit-tests
Browse files Browse the repository at this point in the history
Add unit tests for bundle-audit
  • Loading branch information
mkonda authored Sep 4, 2018
2 parents 53bbad7 + 2c3781a commit b10cce3
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 0 deletions.
42 changes: 42 additions & 0 deletions spec/tasks/bundle-audit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## bundler-audit spec tests

### Overview of bundler-audit

[bundler-audit](https://github.com/rubysec/bundler-audit)

Scans a project's vulnerable versions of gems in the `Gemfile.lock` file and checks for gem sources without TLS.

The names/versions are compared against the [ruby-advisory-db ](https://github.com/rubysec/ruby-advisory-db).

To install bundler-audit:
```
gem install bundler-audit
```

The simplest way to run it from the command line is to `cd` to the folder with the `Gemfile.lock` file and call:
```
bundle-audit check
```

In Glue, `bundler-audit` is called with the following argument:
```
bundle-audit check
```

Some other command line options when running bundler-audit locally:
* `--update` - Updates the ruby-advisory-db;
* `--ignore [ADVISORY-ID]` - Ignores specific advisories.

See [bundler-audit documentation](https://www.rubydoc.info/gems/bundler-audit/frames) for more info.

### The spec tests

The specs do not call the bundler-audit tool because this would be too slow (~1 sec per spec test).
Instead the specs rely on stubbing Glue's `runsystem` method (which calls CLI commands).

In the specs, the return value of `runsystem` is always a canned response.
Either it will be a generic, minimal response, or it will be a snapshot of an actual bundler-audit report.

The actual reports were generated via the script `generate_reports.sh`.
The targets of the spec tests were set up in a minimal way to produce non-trivial output.
This required a `Gemfile.lock` file per target.
209 changes: 209 additions & 0 deletions spec/tasks/bundle-audit/bundle-audit_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
require 'spec_helper'
require 'rspec/collection_matchers'
require 'glue'
require 'glue/event'
require 'glue/tracker'
require 'glue/tasks'
require 'glue/tasks/bundle-audit'

describe Glue::BundleAudit do

BUNDLEAUDIT_TARGETS_PATH = 'spec/tasks/bundle-audit/targets'

def get_bundleaudit(target = 'nil_target')
trigger = Glue::Event.new(target)
trigger.path = File.join(BUNDLEAUDIT_TARGETS_PATH, target)
tracker = Glue::Tracker.new({})
Glue::BundleAudit.new(trigger, tracker)
end

def get_raw_report(target, subtarget = nil)
path = File.join(get_target_path(target, subtarget), "report.txt")
File.read(path).chomp
end

def get_target_path(target, subtarget = nil)
if subtarget.nil?
File.join(BUNDLEAUDIT_TARGETS_PATH, target)
else
File.join(BUNDLEAUDIT_TARGETS_PATH, target, subtarget)
end
end

def cli_args(target, subtarget = nil)
[true, "bundle-audit", "check", { chdir: get_target_path(target, subtarget) }]
end

describe "#initialize" do
let(:task) { @task }
before(:all) { @task = get_bundleaudit }

it "sets the correct 'name'" do
expect(task.name).to eq('BundleAudit')
end

it "sets the correct 'stage'" do
expect(task.stage).to eq(:code)
end

it "sets the correct 'labels'" do
expect(task.labels).to eq(%w[code ruby].to_set)
end
end

describe '#supported?' do
subject(:task) { get_bundleaudit }

context "when 'runsystem' cannot run the task" do
before do
allow(Glue).to receive(:notify) # suppress the output
cmd_args = [false, "bundle-audit", "update"]
cmd_str = 'command not found'
allow(task).to receive(:runsystem).with(*cmd_args).and_return(cmd_str)
end

it { is_expected.not_to be_supported }

it 'issues a notification' do
expect(Glue).to receive(:notify)
task.supported?
end
end

context "when 'runsystem' returns an updating message" do
before do
cmd_args = [false, "bundle-audit", "update"]
cmd_str = 'Updating ruby-advisory-db ...'
allow(task).to receive(:runsystem).with(*cmd_args).and_return(cmd_str)
end

it { is_expected.to be_supported }
end
end

describe "#run" do
let(:task) { get_bundleaudit target }
let(:minimal_response) { "No vulnerabilities found" }

before do
allow(Glue).to receive(:notify) # suppress the output
allow(Glue).to receive(:warn) # suppress the output

# This acts as a guard against actually calling bundle-audit from the CLI.
# (All specs should use canned responses instead.)
allow(task).to receive(:runsystem) do
puts "Warning from rspec -- make sure you're not attempting to call the actual bundle-audit CLI"
puts "within an 'it' block with description '#{self.class.description}'"
minimal_response
end
end

context "with no Gemfile.lock file in root, and no sub-dirs" do
let(:target) { 'no_findings_no_gemfile_lock' }

it "does not call bundle-audit on the target" do
expect(task).not_to receive(:runsystem).with(*cli_args(target))
task.run
end
end

context 'assuming valid (but minimal) reports' do
context 'with one Gemfile.lock in the root dir' do
let(:target) { 'finding_1' }

before do
allow(task).to receive(:runsystem).with(*cli_args(target))
end

it 'passes the task name to Glue.notify' do
expect(Glue).to receive(:notify).with(/^BundleAudit/)
task.run
end

it "calls the 'bundle-audit' cli once, from the root dir" do
expect(task).to receive(:runsystem).with(*cli_args(target))
task.run
end

end

end
end

describe "#analyze" do
let(:task) { get_bundleaudit target }
let(:minimal_response) { "No vulnerabilities found" }

before do
allow(Glue).to receive(:notify) # suppress the output
allow(Glue).to receive(:warn) # suppress the output

# This acts as a guard against actually calling bundle-audit from the CLI.
# (All specs should use canned responses instead.)
allow(task).to receive(:runsystem) do
puts "Warning from rspec -- make sure you're not attempting to call the actual bundle-audit API"
puts "within an 'it' block with description '#{self.class.description}'"
minimal_response
end
end

context "with no Gemfile.lock file in root, and no sub-dirs" do
let(:target) { 'no_findings_no_gemfile_lock' }
subject(:task_findings) { task.findings }
it { is_expected.to eq([]) }
end

context "with one Gemfile.lock in the root dir" do
let(:raw_report) { get_raw_report(target) }

before do
allow(task).to receive(:runsystem).with(*cli_args(target)).and_return(raw_report)
task.run
task.analyze
end

context "with no findings" do
let(:target) { 'no_findings' }
subject(:task_findings) { task.findings }
it { is_expected.to eq([]) }
end

context "with one finding" do
let(:finding) { task.findings.first }

context "of unknown criticality" do
let (:target) { 'finding_2_unknown' }

it "has severity 2" do
expect(finding.severity).to eq(2)
end
end

context "of low criticality" do
let (:target) { 'finding_1' }

it "has severity 1" do
expect(finding.severity).to eq(1)
end
end

context "of medium criticality" do
let (:target) { 'finding_2' }

it "has severity 2" do
expect(finding.severity).to eq(2)
end
end

context "of high criticality" do
let (:target) { 'finding_3' }

it "has severity 3" do
expect(finding.severity).to eq(3)
end
end
end
end
end

end
26 changes: 26 additions & 0 deletions spec/tasks/bundle-audit/generate_reports.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
# Runs bundle-audit on the contents of the 'targets' dir,
# storing the output in 'report.txt' within each target folder.
# Include a 'SKIP.txt' file next to 'package.json' if you don't want snyk to run on that target.

set -e

run_bundleaudit_recurs ()
{
if [ -f "Gemfile.lock" ] && [ ! -f "SKIP.txt" ]; then
bundle-audit check > report.txt
fi

for SUBTARGET in *
do
if [ -d ${SUBTARGET} ]; then
cd ${SUBTARGET}
run_bundleaudit_recurs
cd ..
fi
done
}

DIR=`dirname $0`
cd "${DIR}/targets/"
run_bundleaudit_recurs
13 changes: 13 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_1/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
kafo (0.3.1)

PLATFORMS
ruby

RUBY VERSION
ruby 2.3.3

BUNDLED WITH
1.14.6
9 changes: 9 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_1/report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Name: kafo
Version: 0.3.1
Advisory: CVE-2014-0135
Criticality: Low
URL: http://osvdb.org/show/osvdb/106826
Title: Kafo default_values.yaml Insecure Permissions Local Information Disclosure
Solution: upgrade to ~> 0.3.17, >= 0.5.2

Vulnerabilities found!
13 changes: 13 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_2/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
http (0.7.1)

PLATFORMS
ruby

RUBY VERSION
ruby 2.3.3

BUNDLED WITH
1.14.6
9 changes: 9 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_2/report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Name: http
Version: 0.7.1
Advisory: CVE-2015-1828
Criticality: Medium
URL: https://groups.google.com/forum/#!topic/httprb/jkb4oxwZjkU
Title: HTTPS MitM vulnerability in http.rb
Solution: upgrade to >= 0.7.3, ~> 0.6.4

Vulnerabilities found!
13 changes: 13 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_2_unknown/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
nokogiri (1.8.2)

PLATFORMS
ruby

RUBY VERSION
ruby 2.3.3

BUNDLED WITH
1.14.6
9 changes: 9 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_2_unknown/report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Name: nokogiri
Version: 1.8.2
Advisory: CVE-2018-8048
Criticality: Unknown
URL: https://github.com/sparklemotion/nokogiri/pull/1746
Title: Revert libxml2 behavior in Nokogiri gem that could cause XSS
Solution: upgrade to >= 1.8.3

Vulnerabilities found!
13 changes: 13 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_3/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
curl

PLATFORMS
ruby

RUBY VERSION
ruby 2.3.3

BUNDLED WITH
1.14.6
9 changes: 9 additions & 0 deletions spec/tasks/bundle-audit/targets/finding_3/report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Name: curl
Version:
Advisory: CVE-2013-2617
Criticality: High
URL: http://osvdb.org/show/osvdb/91230
Title: Curl Gem for Ruby URI Handling Arbitrary Command Injection
Solution: remove or disable this gem until a patch is available!

Vulnerabilities found!
13 changes: 13 additions & 0 deletions spec/tasks/bundle-audit/targets/no_findings/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
nokogiri (1.8.4)

PLATFORMS
ruby

RUBY VERSION
ruby 2.3.3

BUNDLED WITH
1.14.6
1 change: 1 addition & 0 deletions spec/tasks/bundle-audit/targets/no_findings/report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No vulnerabilities found
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Empty

0 comments on commit b10cce3

Please sign in to comment.