pronto-bundler_audit 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop +4 -0
- data/.rubocop.yml +156 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +8 -10
- data/README.md +0 -1
- data/Rakefile +3 -1
- data/bin/console +1 -0
- data/lib/pronto/bundler_audit/advisory_formatters/base_advisory_formatter.rb +72 -0
- data/lib/pronto/bundler_audit/advisory_formatters/compact.rb +23 -0
- data/lib/pronto/bundler_audit/advisory_formatters/verbose.rb +26 -0
- data/lib/pronto/bundler_audit/auditor.rb +35 -0
- data/lib/pronto/bundler_audit/results/base_result.rb +41 -0
- data/lib/pronto/bundler_audit/results/insecure_source.rb +26 -0
- data/lib/pronto/bundler_audit/results/unpatched_gem.rb +48 -0
- data/lib/pronto/bundler_audit/scanner.rb +46 -0
- data/lib/pronto/bundler_audit/version.rb +3 -1
- data/lib/pronto/bundler_audit.rb +8 -5
- data/pronto-bundler_audit.gemspec +13 -20
- metadata +17 -8
- data/lib/pronto/bundler_audit/patch_handler.rb +0 -165
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a6328fffd859750def2c69f570ea970ecc44aa1b31362dfa03b5933cde49201
|
4
|
+
data.tar.gz: c6c226e11867996c7d19d53089a31e20b87acaebe3593713f11d310f7e1b602b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df67a7343d90292e9b15fdfeb6c75eab64b76b27a98ee1feffb3ac9b2c1ef3f0e20822048a595018904d19655c8c82142635644409e01faaee3fa313fccc5826
|
7
|
+
data.tar.gz: 167db3c4bfbd04bec6b25d519f0fd060809de610f899d8e3cc166601baac28ffcc512129ab02496305899df4b7f192fc77a274352f4cc2fcdfcb8f6c846e5edb
|
data/.rubocop
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
AllCops:
|
2
|
+
UseCache: true
|
3
|
+
DisplayCopNames: true
|
4
|
+
DisplayStyleGuide: true
|
5
|
+
ExtraDetails: false
|
6
|
+
TargetRubyVersion: 2.5.3
|
7
|
+
|
8
|
+
Layout/ClassStructure:
|
9
|
+
Enabled: true
|
10
|
+
Categories:
|
11
|
+
module_inclusion:
|
12
|
+
- extend
|
13
|
+
- include
|
14
|
+
- prepend
|
15
|
+
attributes:
|
16
|
+
- attr_accessor
|
17
|
+
- attr_reader
|
18
|
+
- attr_writer
|
19
|
+
ExpectedOrder:
|
20
|
+
- constants
|
21
|
+
- module_inclusion
|
22
|
+
- attributes
|
23
|
+
- public_class_methods
|
24
|
+
- initializer
|
25
|
+
- public_methods
|
26
|
+
- predicates
|
27
|
+
- protected_methods
|
28
|
+
- private_methods
|
29
|
+
|
30
|
+
Layout/DotPosition:
|
31
|
+
EnforcedStyle: trailing
|
32
|
+
|
33
|
+
Layout/EmptyLineAfterGuardClause:
|
34
|
+
Enabled: true
|
35
|
+
|
36
|
+
Layout/EndOfLine:
|
37
|
+
EnforcedStyle: lf
|
38
|
+
|
39
|
+
Layout/IndentFirstArgument:
|
40
|
+
EnforcedStyle: consistent_relative_to_receiver
|
41
|
+
|
42
|
+
Layout/IndentFirstArrayElement:
|
43
|
+
EnforcedStyle: consistent
|
44
|
+
|
45
|
+
Layout/IndentFirstHashElement:
|
46
|
+
EnforcedStyle: consistent
|
47
|
+
|
48
|
+
Layout/MultilineAssignmentLayout:
|
49
|
+
Enabled: true
|
50
|
+
|
51
|
+
Layout/MultilineMethodCallBraceLayout:
|
52
|
+
EnforcedStyle: same_line
|
53
|
+
|
54
|
+
Layout/MultilineMethodCallIndentation:
|
55
|
+
EnforcedStyle: indented_relative_to_receiver
|
56
|
+
|
57
|
+
Layout/MultilineMethodDefinitionBraceLayout:
|
58
|
+
EnforcedStyle: same_line
|
59
|
+
|
60
|
+
Layout/MultilineOperationIndentation:
|
61
|
+
Enabled: false # Waiting for e.g. `indented_relative_to_receiver`.
|
62
|
+
|
63
|
+
Lint/AmbiguousOperator:
|
64
|
+
Enabled: false # Conflicts with other rules.
|
65
|
+
|
66
|
+
Lint/AmbiguousRegexpLiteral:
|
67
|
+
Enabled: false # Conflicts with other rules.
|
68
|
+
|
69
|
+
Lint/Void:
|
70
|
+
CheckForMethodsWithNoSideEffects: true
|
71
|
+
|
72
|
+
Metrics/BlockLength:
|
73
|
+
ExcludedMethods:
|
74
|
+
- new
|
75
|
+
- describe # Tests
|
76
|
+
- context # Tests
|
77
|
+
- ips # Benchmarking
|
78
|
+
|
79
|
+
Metrics/ClassLength:
|
80
|
+
Exclude:
|
81
|
+
- "test/**/*"
|
82
|
+
- "lib/object_inspector/formatters/templating_formatter.rb"
|
83
|
+
|
84
|
+
Metrics/LineLength:
|
85
|
+
Max: 80
|
86
|
+
Exclude:
|
87
|
+
- "test/**/*"
|
88
|
+
- "object_inspector.gemspec"
|
89
|
+
|
90
|
+
Naming/UncommunicativeMethodParamName:
|
91
|
+
AllowedNames:
|
92
|
+
- a
|
93
|
+
- b
|
94
|
+
|
95
|
+
Style/Alias:
|
96
|
+
EnforcedStyle: prefer_alias_method
|
97
|
+
|
98
|
+
Style/BlockDelimiters:
|
99
|
+
Enabled: false # Reconsider later.
|
100
|
+
|
101
|
+
Style/ClassAndModuleChildren:
|
102
|
+
AutoCorrect: true
|
103
|
+
Exclude:
|
104
|
+
- "test/**/*"
|
105
|
+
|
106
|
+
Style/CollectionMethods:
|
107
|
+
Enabled: true
|
108
|
+
PreferredMethods:
|
109
|
+
collect: map
|
110
|
+
collect!: map!
|
111
|
+
find_all: select
|
112
|
+
detect: detect
|
113
|
+
inject: inject
|
114
|
+
|
115
|
+
Style/EmptyElse:
|
116
|
+
# It"s helpful to show intent by including a comment in an else block.
|
117
|
+
Enabled: false
|
118
|
+
|
119
|
+
Style/EmptyMethod:
|
120
|
+
EnforcedStyle: expanded
|
121
|
+
|
122
|
+
Style/ExpandPathArguments:
|
123
|
+
Exclude:
|
124
|
+
- "object_inspector.gemspec"
|
125
|
+
|
126
|
+
Style/FormatString:
|
127
|
+
Enabled: false # % notation with an Array just reads better sometimes.
|
128
|
+
|
129
|
+
Style/Lambda:
|
130
|
+
EnforcedStyle: literal
|
131
|
+
|
132
|
+
Style/NumericPredicate:
|
133
|
+
Enabled: false # Trying to be welcoming to earlier versions of Ruby.
|
134
|
+
# AutoCorrect: true
|
135
|
+
|
136
|
+
Style/RegexpLiteral:
|
137
|
+
EnforcedStyle: mixed
|
138
|
+
|
139
|
+
Style/RescueStandardError:
|
140
|
+
EnforcedStyle: implicit
|
141
|
+
|
142
|
+
Style/ReturnNil:
|
143
|
+
Enabled: true
|
144
|
+
|
145
|
+
Style/StringMethods:
|
146
|
+
Enabled: true
|
147
|
+
|
148
|
+
Style/SingleLineMethods:
|
149
|
+
Exclude:
|
150
|
+
- "test/**/*_test.rb"
|
151
|
+
|
152
|
+
Style/StringLiterals:
|
153
|
+
EnforcedStyle: double_quotes
|
154
|
+
|
155
|
+
Style/StringLiteralsInInterpolation:
|
156
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
### 0.3.0 - 2019-05-03
|
2
|
+
- Internal rewrite into smaller objects with full test coverage
|
3
|
+
- Switch to using the verbose advisory formatter by default
|
4
|
+
|
1
5
|
### 0.2.1 - 2019-04-30
|
2
6
|
- Fix handling of the Pronto::Git::Patches collection in Pronto::BundlerAudit#run
|
3
7
|
- Ensure an Array is returned by Pronto::BundlerAudit#run, as expected by Pronto
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pronto-bundler_audit (0.
|
4
|
+
pronto-bundler_audit (0.3.0)
|
5
5
|
bundler-audit (~> 0)
|
6
6
|
pronto (~> 0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
addressable (2.
|
11
|
+
addressable (2.6.0)
|
12
12
|
public_suffix (>= 2.0.2, < 4.0)
|
13
13
|
ansi (1.5.0)
|
14
14
|
ast (2.4.0)
|
@@ -29,7 +29,6 @@ GEM
|
|
29
29
|
multi_xml (>= 0.5.2)
|
30
30
|
jaro_winkler (1.5.2)
|
31
31
|
json (2.2.0)
|
32
|
-
metaclass (0.0.4)
|
33
32
|
method_source (0.9.2)
|
34
33
|
mime-types (3.2.2)
|
35
34
|
mime-types-data (~> 3.2015)
|
@@ -40,8 +39,7 @@ GEM
|
|
40
39
|
builder
|
41
40
|
minitest (>= 5.0)
|
42
41
|
ruby-progressbar
|
43
|
-
|
44
|
-
metaclass (~> 0.0.1)
|
42
|
+
much-stub (0.1.0)
|
45
43
|
multi_xml (0.6.0)
|
46
44
|
multipart-post (2.0.0)
|
47
45
|
octokit (4.14.0)
|
@@ -65,7 +63,7 @@ GEM
|
|
65
63
|
public_suffix (3.0.3)
|
66
64
|
rainbow (3.0.0)
|
67
65
|
rake (12.3.2)
|
68
|
-
rubocop (0.68.
|
66
|
+
rubocop (0.68.1)
|
69
67
|
jaro_winkler (~> 1.5.1)
|
70
68
|
parallel (~> 1.10)
|
71
69
|
parser (>= 2.5, != 2.5.1.1)
|
@@ -74,9 +72,9 @@ GEM
|
|
74
72
|
unicode-display_width (>= 1.4.0, < 1.6)
|
75
73
|
ruby-progressbar (1.10.0)
|
76
74
|
rugged (0.28.1)
|
77
|
-
sawyer (0.8.
|
78
|
-
addressable (>= 2.3.5
|
79
|
-
faraday (
|
75
|
+
sawyer (0.8.2)
|
76
|
+
addressable (>= 2.3.5)
|
77
|
+
faraday (> 0.8, < 2.0)
|
80
78
|
simplecov (0.16.1)
|
81
79
|
docile (~> 1.1)
|
82
80
|
json (>= 1.8, < 3)
|
@@ -95,7 +93,7 @@ DEPENDENCIES
|
|
95
93
|
byebug (~> 11)
|
96
94
|
minitest (~> 5)
|
97
95
|
minitest-reporters (~> 1)
|
98
|
-
|
96
|
+
much-stub (~> 0)
|
99
97
|
pronto-bundler_audit!
|
100
98
|
pry (~> 0)
|
101
99
|
pry-byebug (~> 3)
|
data/README.md
CHANGED
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pronto
|
4
|
+
class BundlerAudit
|
5
|
+
module AdvisoryFormatters
|
6
|
+
# Pronto::BundlerAudit::AdvisoryFormatters::BaseAdvisoryFormatter is an
|
7
|
+
# abstract base class for formatting Bundler::Audit::Advisory objects
|
8
|
+
# as a String in the context of the given `gem`
|
9
|
+
# (Bundler::LazySpecification).
|
10
|
+
class BaseAdvisoryFormatter
|
11
|
+
# param gem [Bundler::LazySpecification]
|
12
|
+
# param advisory [Bundler::Audit::Advisory]
|
13
|
+
def initialize(gem:, advisory:)
|
14
|
+
@gem = gem
|
15
|
+
@advisory = advisory
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def gem_name
|
25
|
+
@gem.name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gem_version
|
29
|
+
@gem.version
|
30
|
+
end
|
31
|
+
|
32
|
+
def advisory_description
|
33
|
+
if @advisory.cve
|
34
|
+
"CVE-#{@advisory.cve}"
|
35
|
+
elsif @advisory.osvdb
|
36
|
+
@advisory.osvdb
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def advisory_criticality
|
41
|
+
str = @advisory.criticality.to_s.capitalize
|
42
|
+
str = "Unknown" if str.empty?
|
43
|
+
str
|
44
|
+
end
|
45
|
+
|
46
|
+
def advisory_url
|
47
|
+
@advisory.url
|
48
|
+
end
|
49
|
+
|
50
|
+
def advisory_title
|
51
|
+
@advisory.title
|
52
|
+
end
|
53
|
+
|
54
|
+
def advisory_solution
|
55
|
+
if any_patched_versions?
|
56
|
+
"Upgrade to #{patched_versions}."
|
57
|
+
else
|
58
|
+
"Remove or disable this gem until a patch is available!"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def patched_versions
|
63
|
+
@advisory.patched_versions.join(", ")
|
64
|
+
end
|
65
|
+
|
66
|
+
def any_patched_versions?
|
67
|
+
!@advisory.patched_versions.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_advisory_formatter"
|
4
|
+
|
5
|
+
module Pronto
|
6
|
+
class BundlerAudit
|
7
|
+
module AdvisoryFormatters
|
8
|
+
# Pronto::BundlerAudit::AdvisoryFormatters::Compact is a compact message
|
9
|
+
# formatter for the given gem object and Bundler::Audit::Advisory#advisory
|
10
|
+
# object.
|
11
|
+
class Compact < BaseAdvisoryFormatter
|
12
|
+
def to_s
|
13
|
+
[
|
14
|
+
"Gem: #{gem_name} v#{gem_version}",
|
15
|
+
"#{advisory_criticality} Advisory: #{advisory_title} -- "\
|
16
|
+
"#{advisory_description} (#{advisory_url})",
|
17
|
+
"Solution: #{advisory_solution}"
|
18
|
+
].join(" | ")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_advisory_formatter"
|
4
|
+
|
5
|
+
module Pronto
|
6
|
+
class BundlerAudit
|
7
|
+
module AdvisoryFormatters
|
8
|
+
# Pronto::BundlerAudit::AdvisoryFormatters::Verbose is a verbose message
|
9
|
+
# formatter for the given gem object and Bundler::Audit::Advisory#advisory
|
10
|
+
# object.
|
11
|
+
class Verbose < BaseAdvisoryFormatter
|
12
|
+
def to_s
|
13
|
+
[
|
14
|
+
"Name: #{gem_name}",
|
15
|
+
"Version: #{gem_version}",
|
16
|
+
"Advisory: #{advisory_description}",
|
17
|
+
"Criticality: #{advisory_criticality}",
|
18
|
+
"URL: #{advisory_url}",
|
19
|
+
"Title: #{advisory_title}",
|
20
|
+
"Solution: #{advisory_solution}"
|
21
|
+
].join("\n")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pronto/bundler_audit/scanner"
|
4
|
+
|
5
|
+
module Pronto
|
6
|
+
class BundlerAudit
|
7
|
+
# Pronto::BundlerAudit::Auditor:
|
8
|
+
# 1. updates the local ruby security database, and then
|
9
|
+
# 2. runs {Pronto::BundlerAudit::Scanner#call} on the given `patch`.
|
10
|
+
class Auditor
|
11
|
+
def initialize(patch)
|
12
|
+
@patch = patch
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return (see: #run_scan)
|
16
|
+
def call
|
17
|
+
update_ruby_advisory_db
|
18
|
+
run_scanner
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update_ruby_advisory_db
|
24
|
+
Bundler::Audit::Database.update!(quiet: true)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array>] if no advisories were found
|
28
|
+
# @return [Array<Pronto::Message>] if advisories were found
|
29
|
+
def run_scanner
|
30
|
+
scanner = Scanner.new(@patch)
|
31
|
+
scanner.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pronto
|
4
|
+
class BundlerAudit
|
5
|
+
module Results
|
6
|
+
# Pronto::BundlerAudit::Results::BaseResult is an abstract base class for
|
7
|
+
# the various Bundler::Audit::Scanner::* issue types.
|
8
|
+
class BaseResult
|
9
|
+
def initialize(scan_result)
|
10
|
+
@scan_result = scan_result
|
11
|
+
@gem = scan_result.gem
|
12
|
+
@advisory = scan_result.advisory
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
report_result
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def report_result
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_message(message, level:, line:)
|
26
|
+
Message.new(
|
27
|
+
GEMFILE_LOCK_FILENAME,
|
28
|
+
line,
|
29
|
+
level,
|
30
|
+
message,
|
31
|
+
nil,
|
32
|
+
Pronto::BundlerAudit)
|
33
|
+
end
|
34
|
+
|
35
|
+
def message
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_result"
|
4
|
+
|
5
|
+
module Pronto
|
6
|
+
class BundlerAudit
|
7
|
+
module Results
|
8
|
+
# Pronto::BundlerAudit::Results::InsecureSource builds a Pronto::Message
|
9
|
+
# for Bundler::Audit::Scanner::InsecureSource issues.
|
10
|
+
class InsecureSource < BaseResult
|
11
|
+
private
|
12
|
+
|
13
|
+
def report_result
|
14
|
+
build_message(
|
15
|
+
message,
|
16
|
+
line: nil,
|
17
|
+
level: :warning)
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
"Insecure Source URI found: #{@scan_result.source}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_result"
|
4
|
+
require "pronto/bundler_audit/advisory_formatters/verbose"
|
5
|
+
require "pronto/bundler_audit/advisory_formatters/compact"
|
6
|
+
|
7
|
+
module Pronto
|
8
|
+
class BundlerAudit
|
9
|
+
module Results
|
10
|
+
# Pronto::BundlerAudit::Results::UnpatchedGem builds a Pronto::Message for
|
11
|
+
# Bundler::Audit::Scanner::UnpatchedGem issues.
|
12
|
+
class UnpatchedGem < BaseResult
|
13
|
+
def initialize(scan_result, patch:)
|
14
|
+
super(scan_result)
|
15
|
+
@patch = patch
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def report_result
|
21
|
+
build_message(
|
22
|
+
message,
|
23
|
+
level: :error,
|
24
|
+
line: find_relevant_line)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Pronto::Git::Line]
|
28
|
+
def find_relevant_line
|
29
|
+
first_added_line_for_affected_gem_name(@gem.name)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Pronto::Git::Line]
|
33
|
+
def first_added_line_for_affected_gem_name(gem_name)
|
34
|
+
@patch.added_lines.detect { |line| line.content.include?(gem_name) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
advisory_formatter.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def advisory_formatter
|
42
|
+
# TODO: Switch type based on configuration options, once available.
|
43
|
+
AdvisoryFormatters::Verbose.new(gem: @gem, advisory: @advisory)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "results/insecure_source"
|
4
|
+
require_relative "results/unpatched_gem"
|
5
|
+
|
6
|
+
module Pronto
|
7
|
+
class BundlerAudit
|
8
|
+
# Pronto::BundlerAudit::Scanner runs runs Bundler::Audit::Scanner#scan on
|
9
|
+
# the given patch and then calls a {Pronto::BundlerAudit::BaseResult} based
|
10
|
+
# for each scan result.
|
11
|
+
class Scanner
|
12
|
+
def initialize(patch)
|
13
|
+
@patch = patch
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Array>] if no advisories were found
|
17
|
+
# @return [Array<Pronto::Message>] if advisories were found)
|
18
|
+
def call
|
19
|
+
run_scan
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def run_scan
|
25
|
+
run_scanner.map do |scan_result|
|
26
|
+
match_result(scan_result).call
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def run_scanner
|
31
|
+
Bundler::Audit::Scanner.new.scan
|
32
|
+
end
|
33
|
+
|
34
|
+
def match_result(scan_result)
|
35
|
+
case scan_result
|
36
|
+
when Bundler::Audit::Scanner::InsecureSource
|
37
|
+
Results::InsecureSource.new(scan_result, patch: @patch)
|
38
|
+
when Bundler::Audit::Scanner::UnpatchedGem
|
39
|
+
Results::UnpatchedGem.new(scan_result, patch: @patch)
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Unexpected type: #{scan_result.class}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/pronto/bundler_audit.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pronto"
|
2
4
|
require "bundler/audit/database"
|
3
5
|
require "bundler/audit/scanner"
|
4
|
-
require "pronto/bundler_audit/version"
|
5
|
-
require "pronto/bundler_audit/patch_handler"
|
6
6
|
|
7
7
|
module Pronto
|
8
8
|
# Pronto::BundlerAudit is a Pronto::Runner that:
|
@@ -12,13 +12,13 @@ module Pronto
|
|
12
12
|
# 3. Runs bundle-audit to scan the Gemfile.lock
|
13
13
|
# 4. Returns an Array of Pronto::Message objects if any advisories are found
|
14
14
|
class BundlerAudit < Runner
|
15
|
-
GEMFILE_LOCK_FILENAME = "Gemfile.lock"
|
15
|
+
GEMFILE_LOCK_FILENAME = "Gemfile.lock"
|
16
16
|
|
17
17
|
# @return [Array] per Pronto expectation
|
18
18
|
def run
|
19
19
|
if (patch = find_relevant_patch)
|
20
|
-
|
21
|
-
|
20
|
+
auditor = Auditor.new(patch)
|
21
|
+
auditor.call
|
22
22
|
else
|
23
23
|
[]
|
24
24
|
end
|
@@ -38,3 +38,6 @@ module Pronto
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
require "pronto/bundler_audit/version"
|
43
|
+
require "pronto/bundler_audit/auditor"
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require "pronto/bundler_audit/version"
|
4
6
|
|
@@ -8,40 +10,31 @@ Gem::Specification.new do |spec|
|
|
8
10
|
spec.authors = ["Paul Dobbins"]
|
9
11
|
spec.email = ["paul.dobbins@icloud.com"]
|
10
12
|
|
11
|
-
spec.summary =
|
13
|
+
spec.summary =
|
14
|
+
"Pronto runner for bundler-audit, patch-level verification for bundler."
|
12
15
|
spec.homepage = "http://github.com/pdobb/pronto-bundler_audit"
|
13
16
|
spec.license = "MIT"
|
14
17
|
|
15
|
-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
16
|
-
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
17
|
-
# if spec.respond_to?(:metadata)
|
18
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
19
|
-
|
20
|
-
# spec.metadata["homepage_uri"] = spec.homepage
|
21
|
-
# spec.metadata["source_code_uri"] = "https://github.com/pdobb/pronto-bundler_audit"
|
22
|
-
# spec.metadata["changelog_uri"] = "https://github.com/pdobb/pronto-bundler_audit/blob/master/CHANGELOG.md"
|
23
|
-
# else
|
24
|
-
# raise "RubyGems 2.0 or newer is required to protect against " \
|
25
|
-
# "public gem pushes."
|
26
|
-
# end
|
27
|
-
|
28
18
|
# Specify which files should be added to the gem when it is released.
|
29
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
30
|
-
|
31
|
-
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
20
|
+
# into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f|
|
23
|
+
f.match(%r{^(test|spec|features)/})
|
24
|
+
}
|
32
25
|
end
|
33
26
|
spec.bindir = "exe"
|
34
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
28
|
spec.require_paths = ["lib"]
|
36
29
|
|
37
|
-
spec.add_runtime_dependency "pronto", "~> 0"
|
38
30
|
spec.add_runtime_dependency "bundler-audit", "~> 0"
|
31
|
+
spec.add_runtime_dependency "pronto", "~> 0"
|
39
32
|
|
40
33
|
spec.add_development_dependency "bundler", "~> 2"
|
41
34
|
spec.add_development_dependency "byebug", "~> 11"
|
42
35
|
spec.add_development_dependency "minitest", "~> 5"
|
43
36
|
spec.add_development_dependency "minitest-reporters", "~> 1"
|
44
|
-
spec.add_development_dependency "
|
37
|
+
spec.add_development_dependency "much-stub", "~> 0"
|
45
38
|
spec.add_development_dependency "pry", "~> 0"
|
46
39
|
spec.add_development_dependency "pry-byebug", "~> 3"
|
47
40
|
spec.add_development_dependency "rake", "~> 12"
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pronto-bundler_audit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Dobbins
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler-audit
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pronto
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
@@ -95,19 +95,19 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: much-stub
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: pry
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,6 +192,8 @@ extensions: []
|
|
192
192
|
extra_rdoc_files: []
|
193
193
|
files:
|
194
194
|
- ".gitignore"
|
195
|
+
- ".rubocop"
|
196
|
+
- ".rubocop.yml"
|
195
197
|
- ".travis.yml"
|
196
198
|
- CHANGELOG.md
|
197
199
|
- Gemfile
|
@@ -204,7 +206,14 @@ files:
|
|
204
206
|
- images/github-check.png
|
205
207
|
- images/github-comment.png
|
206
208
|
- lib/pronto/bundler_audit.rb
|
207
|
-
- lib/pronto/bundler_audit/
|
209
|
+
- lib/pronto/bundler_audit/advisory_formatters/base_advisory_formatter.rb
|
210
|
+
- lib/pronto/bundler_audit/advisory_formatters/compact.rb
|
211
|
+
- lib/pronto/bundler_audit/advisory_formatters/verbose.rb
|
212
|
+
- lib/pronto/bundler_audit/auditor.rb
|
213
|
+
- lib/pronto/bundler_audit/results/base_result.rb
|
214
|
+
- lib/pronto/bundler_audit/results/insecure_source.rb
|
215
|
+
- lib/pronto/bundler_audit/results/unpatched_gem.rb
|
216
|
+
- lib/pronto/bundler_audit/scanner.rb
|
208
217
|
- lib/pronto/bundler_audit/version.rb
|
209
218
|
- pronto-bundler_audit.gemspec
|
210
219
|
homepage: http://github.com/pdobb/pronto-bundler_audit
|
@@ -1,165 +0,0 @@
|
|
1
|
-
module Pronto
|
2
|
-
class BundlerAudit < Runner
|
3
|
-
# Pronto::BundlerAudit::PatchHandler runs Bundle Audit on the given patch
|
4
|
-
# and returns an Array of Pronto::Message objects if any advisories are
|
5
|
-
# found.
|
6
|
-
class PatchHandler
|
7
|
-
def initialize(patch, runner:)
|
8
|
-
@patch = patch
|
9
|
-
@runner = runner
|
10
|
-
end
|
11
|
-
|
12
|
-
# @return (see: #run_scan)
|
13
|
-
def call
|
14
|
-
update_ruby_advisory_db
|
15
|
-
run_scan
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def update_ruby_advisory_db
|
21
|
-
Bundler::Audit::Database.update!(quiet: true)
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Array>] if no advisories were found
|
25
|
-
# @return [Array<Pronto::Message>] if advisories were found
|
26
|
-
def run_scan
|
27
|
-
scanner = Bundler::Audit::Scanner.new
|
28
|
-
|
29
|
-
scanner.scan.inject([]) do |acc, scan_result|
|
30
|
-
acc << process_scan_result(scan_result)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def process_scan_result(scan_result)
|
35
|
-
case scan_result
|
36
|
-
when Bundler::Audit::Scanner::InsecureSource
|
37
|
-
report_insecure_source_scan_result
|
38
|
-
when Bundler::Audit::Scanner::UnpatchedGem
|
39
|
-
report_unpatched_gem_scan_result(scan_result)
|
40
|
-
else
|
41
|
-
raise ArgumentError, "unexpected type: #{scan_result.class}"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def report_insecure_source_scan_result(scan_result)
|
46
|
-
build_warning_message(
|
47
|
-
"Insecure Source URI found: #{scan_result.source}")
|
48
|
-
end
|
49
|
-
|
50
|
-
def report_unpatched_gem_scan_result(scan_result)
|
51
|
-
advisory =
|
52
|
-
AdvisoryFormatter.new(
|
53
|
-
gem: scan_result.gem, advisory: scan_result.advisory)
|
54
|
-
message = advisory.to_compact_s
|
55
|
-
line = find_relevant_line(advisory)
|
56
|
-
|
57
|
-
build_error_message(message, line: line)
|
58
|
-
end
|
59
|
-
|
60
|
-
# @return [Pronto::Git::Line]
|
61
|
-
def find_relevant_line(advisory)
|
62
|
-
first_added_line_for_affected_gem_name(advisory.gem_name)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @return [Pronto::Git::Line]
|
66
|
-
def first_added_line_for_affected_gem_name(gem_name)
|
67
|
-
@patch.added_lines.detect { |line| line.content.include?(gem_name) }
|
68
|
-
end
|
69
|
-
|
70
|
-
def build_warning_message(message)
|
71
|
-
build_message(message, level: :warning)
|
72
|
-
end
|
73
|
-
|
74
|
-
def build_error_message(message, line:)
|
75
|
-
build_message(message, level: :error, line: line)
|
76
|
-
end
|
77
|
-
|
78
|
-
def build_message(message, level:, line:)
|
79
|
-
Message.new(
|
80
|
-
GEMFILE_LOCK_FILENAME, line, level, message, nil, @runner.class)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Pronto::BundlerAudit::PatchHandler::AdvisoryFormatter is a message
|
84
|
-
# formatter for the given gem object and Bundler::Audit::Advisory#advisory
|
85
|
-
# object.
|
86
|
-
class AdvisoryFormatter
|
87
|
-
# param gem [Bundler::LazySpecification]
|
88
|
-
# param advisory [Bundler::Audit::Advisory]
|
89
|
-
def initialize(gem:, advisory:)
|
90
|
-
@gem = gem
|
91
|
-
@advisory = advisory
|
92
|
-
end
|
93
|
-
|
94
|
-
def to_s
|
95
|
-
[
|
96
|
-
"Name: #{gem_name}",
|
97
|
-
"Version: #{gem_version}",
|
98
|
-
"Advisory: #{advisory_description}",
|
99
|
-
"Criticality: #{advisory_criticality}",
|
100
|
-
"URL: #{advisory_url}",
|
101
|
-
"Title: #{advisory_title}",
|
102
|
-
"Solution: #{advisory_solution}"
|
103
|
-
].join("\n")
|
104
|
-
end
|
105
|
-
|
106
|
-
def to_compact_s
|
107
|
-
[
|
108
|
-
"Gem: #{gem_name} v#{gem_version}",
|
109
|
-
"#{advisory_criticality} Advisory: #{advisory_title} -- "\
|
110
|
-
"#{advisory_description} (#{advisory_url})",
|
111
|
-
"Solution: #{advisory_solution}"
|
112
|
-
].join(" | ")
|
113
|
-
end
|
114
|
-
|
115
|
-
def gem_name
|
116
|
-
@gem.name
|
117
|
-
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
def gem_version
|
122
|
-
@gem.version
|
123
|
-
end
|
124
|
-
|
125
|
-
def advisory_description
|
126
|
-
if @advisory.cve
|
127
|
-
"CVE-#{@advisory.cve}"
|
128
|
-
elsif @advisory.osvdb
|
129
|
-
@advisory.osvdb
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def advisory_criticality
|
134
|
-
str = @advisory.criticality.to_s.capitalize
|
135
|
-
str = "Unknown" if str.empty?
|
136
|
-
str
|
137
|
-
end
|
138
|
-
|
139
|
-
def advisory_url
|
140
|
-
@advisory.url
|
141
|
-
end
|
142
|
-
|
143
|
-
def advisory_title
|
144
|
-
@advisory.title
|
145
|
-
end
|
146
|
-
|
147
|
-
def advisory_solution
|
148
|
-
if any_patched_versions?
|
149
|
-
"Upgrade to #{patched_versions}."
|
150
|
-
else
|
151
|
-
"Remove or disable this gem until a patch is available!"
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def patched_versions
|
156
|
-
@advisory.patched_versions.join(', ')
|
157
|
-
end
|
158
|
-
|
159
|
-
def any_patched_versions?
|
160
|
-
!@advisory.patched_versions.empty?
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|