Skip to content

Commit 3881855

Browse files
committed
Open source commit
0 parents  commit 3881855

File tree

8 files changed

+170
-0
lines changed

8 files changed

+170
-0
lines changed

.codeclimate.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
engines:
2+
rubocop:
3+
enabled: true
4+
ratings:
5+
paths:
6+
- "**.rb"

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.2.2

Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM mhart/alpine-node
2+
3+
WORKDIR /usr/src/app
4+
RUN apk --update add ruby ruby-dev ruby-bundler build-base git
5+
6+
COPY Gemfile /usr/src/app/
7+
COPY Gemfile.lock /usr/src/app/
8+
RUN bundle install -j 4 && \
9+
bundle-audit update && \
10+
apk del build-base && rm -fr /usr/share/ri
11+
12+
COPY . /usr/src/app
13+
14+
CMD ["/usr/src/app/bin/bundler-audit"]
15+

Gemfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
source "https://rubygems.org"
2+
3+
gem 'bundler-audit'
4+
gem 'json'
5+
gem 'versionomy'

Gemfile.lock

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
blockenspiel (0.4.5)
5+
bundler-audit (0.3.1)
6+
bundler (~> 1.2)
7+
thor (~> 0.18)
8+
json (1.8.3)
9+
thor (0.19.1)
10+
versionomy (0.4.4)
11+
blockenspiel (>= 0.4.5)
12+
13+
PLATFORMS
14+
ruby
15+
16+
DEPENDENCIES
17+
bundler-audit
18+
json
19+
versionomy
20+
21+
BUNDLED WITH
22+
1.10.3

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Code Climate bundler-audit Engine
2+
3+
[![Code Climate](https://codeclimate.com/repos/55841a7de30ba012e6020762/badges/20e726217fbdea3896db/gpa.svg)](https://codeclimate.com/repos/55841a7de30ba012e6020762/feed)
4+
5+
`codeclimate-bundler-audit` is a Code Climate engine that wraps [bundler-audit](https://github.com/rubysec/bundler-audit). You can run it on your command line using the Code Climate CLI, or on our hosted analysis platform.
6+
7+
bundler-audit offers patch-level verification for [Bundler](http://bundler.io/).
8+
9+
### Installation
10+
11+
1. If you haven't already, [install the Code Climate CLI](https://github.com/codeclimate/codeclimate).
12+
2. Run `codeclimate engines:enable bundler-audit`. This command both installs the engine and enables it in your `.codeclimate.yml` file.
13+
3. You're ready to analyze! Browse into your project's folder and run `codeclimate analyze`.
14+
15+
### Need help?
16+
17+
For help with bundler-audit, [check out their documentation](https://github.com/rubysec/bundler-audit).
18+
19+
If you're running into a Code Climate issue, first look over this project's [GitHub Issues](https://github.com/codeclimate/bundler-audit/issues), as your question may have already been covered. If not, [go ahead and open a support ticket with us](https://codeclimate.com/help).

bin/bundler-audit

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env ruby
2+
3+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "../lib")))
4+
5+
require 'cc/engine/bundler_audit'
6+
7+
def parse_engine_config(raw)
8+
raw ? JSON.parse(raw) : {}
9+
end
10+
engine_config = parse_engine_config(ENV['ENGINE_CONFIG'])
11+
CC::Engine::BundlerAudit.new(
12+
directory: "/code", engine_config: engine_config, io: STDOUT
13+
).run

lib/cc/engine/bundler_audit.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
require 'json'
2+
require 'versionomy'
3+
4+
module CC
5+
module Engine
6+
class BundlerAudit
7+
def initialize(directory: , io: , engine_config: )
8+
@directory = directory
9+
@engine_config = engine_config
10+
@io = io
11+
end
12+
13+
def run
14+
Dir.chdir(@directory)
15+
raw_output = `bundle-audit`
16+
raw_issues = raw_output.split(/\n\n/).select { |chunk|
17+
chunk =~ /^Name: /
18+
}
19+
@gemfile_lock_lines = File.read(
20+
File.join(@directory, 'Gemfile.lock')
21+
).lines
22+
raw_issues.each do |raw_issue|
23+
issue = issue_from_raw(raw_issue)
24+
@io.print("#{issue.to_json}\0")
25+
end
26+
end
27+
28+
private
29+
30+
def issue_from_raw(raw_issue)
31+
raw_issue_hash = {}
32+
raw_issue.lines.each do |l|
33+
l =~ /^([^:]+): (.+)\n?/
34+
raw_issue_hash[$1] = $2
35+
end
36+
line_number = nil
37+
@gemfile_lock_lines.each_with_index do |l, i|
38+
if l =~ /^\s*#{raw_issue_hash['Name']} \([\d.]+\)/
39+
line_number = i + 1
40+
end
41+
end
42+
{
43+
type: 'Issue',
44+
check_name: "Insecure Dependency",
45+
description: raw_issue_hash['Title'],
46+
categories: ['Security'],
47+
remediation_points: remediation_points(
48+
raw_issue_hash['Version'], raw_issue_hash['Solution']
49+
),
50+
location: {
51+
path: 'Gemfile.lock',
52+
lines: {
53+
begin: line_number,
54+
end: line_number
55+
}
56+
}
57+
}
58+
end
59+
60+
def remediation_points(current_version, raw_solution)
61+
if raw_solution =~ /^upgrade to (.*)/
62+
raw_upgrades = $1.scan(/\d+\.\d+\.\d+/)
63+
current_version = Versionomy.parse(current_version)
64+
result = 5_000_000_000
65+
raw_upgrades.each do |raw_upgrade|
66+
upgrade_version = Versionomy.parse(raw_upgrade)
67+
if upgrade_version > current_version
68+
points_this_upgrade = nil
69+
if current_version.major == upgrade_version.major
70+
if current_version.minor == upgrade_version.minor
71+
points_this_upgrade = 500_000 # patch upgrade
72+
else
73+
points_this_upgrade = 5_000_000 # minor upgrade
74+
end
75+
else
76+
points_this_upgrade = 50_000_000 # major upgrade
77+
end
78+
result = points_this_upgrade if points_this_upgrade < result
79+
end
80+
end
81+
result
82+
else
83+
500_000_000 # No upgrade of gem possible
84+
end
85+
end
86+
end
87+
end
88+
end
89+

0 commit comments

Comments
 (0)