brakeman-lib 4.2.1 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +16 -0
- data/lib/brakeman.rb +4 -2
- data/lib/brakeman/checks/base_check.rb +4 -1
- data/lib/brakeman/checks/check_execute.rb +3 -2
- data/lib/brakeman/checks/check_link_to_href.rb +3 -2
- data/lib/brakeman/checks/check_sanitize_methods.rb +2 -8
- data/lib/brakeman/checks/check_sql.rb +1 -1
- data/lib/brakeman/codeclimate/engine_configuration.rb +2 -2
- data/lib/brakeman/file_parser.rb +5 -1
- data/lib/brakeman/options.rb +9 -1
- data/lib/brakeman/parsers/template_parser.rb +1 -1
- data/lib/brakeman/processors/alias_processor.rb +69 -1
- data/lib/brakeman/processors/lib/find_all_calls.rb +18 -5
- data/lib/brakeman/report/pager.rb +3 -1
- data/lib/brakeman/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08b3a1c2f84d6667c990309480405c61baa2f2ac878dfdc12dbefacdff64a52e'
|
4
|
+
data.tar.gz: 58ba877ea3b52c2e464bb4cca233edf1823f199db71e009df8eb92ce748ee9cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 905adec5711738aaddf2fbfd2bebd9ce9aa039a556fcacbef39177e1d34b44dd953952bf56904444211876466da5c16ae2c2bd8d42ebca00b6a7755a26e600de
|
7
|
+
data.tar.gz: da402133851cb1f3035cbe1784bf4c2e36831bd5167914c2bac82665b57430e2b9dbe9a4aa283e1365556dfcbfb07c933726049f9ee96377b83a676e56f1566f
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# 4.3.0
|
2
|
+
|
3
|
+
* Check exec-type calls even if they are targets
|
4
|
+
* Convert `Array#join` to string interpolation
|
5
|
+
* `BaseCheck#include_interp?` should return first string interpolation
|
6
|
+
* Add `--parser-timeout` option
|
7
|
+
* Track parent calls in CallIndex
|
8
|
+
* Warn about dangerous `link_to` href with `sanitize()`
|
9
|
+
* Ignore `params#to_h` and `params#to_hash` in SQL checks
|
10
|
+
* Change "".freeze to just ""
|
11
|
+
* Ignore `Process.pid` in system calls
|
12
|
+
* Index Kernel#\` calls even if they are targets
|
13
|
+
* Code Climate: omit leading dot from `only_files` (Todd Mazierski)
|
14
|
+
* `--color` can be used to force color output
|
15
|
+
* Fix reported line numbers for CVE-2018-3741 and CVE-2018-8048
|
16
|
+
|
1
17
|
# 4.2.1
|
2
18
|
|
3
19
|
* Add warning for CVE-2018-3741
|
data/lib/brakeman.rb
CHANGED
@@ -51,6 +51,7 @@ module Brakeman
|
|
51
51
|
# * :output_files - files for output
|
52
52
|
# * :output_formats - formats for output (:to_s, :to_tabs, :to_csv, :to_html)
|
53
53
|
# * :parallel_checks - run checks in parallel (default: true)
|
54
|
+
# * :parser_timeout - set timeout for parsing an individual file (default: 10 seconds)
|
54
55
|
# * :print_report - if no output file specified, print to stdout (default: false)
|
55
56
|
# * :quiet - suppress most messages (default: true)
|
56
57
|
# * :rails3 - force Rails 3 mode (automatic)
|
@@ -174,6 +175,7 @@ module Brakeman
|
|
174
175
|
:output_color => true,
|
175
176
|
:pager => true,
|
176
177
|
:parallel_checks => true,
|
178
|
+
:parser_timeout => 10,
|
177
179
|
:relative_path => false,
|
178
180
|
:report_progress => true,
|
179
181
|
:safe_methods => Set.new,
|
@@ -376,7 +378,7 @@ module Brakeman
|
|
376
378
|
|
377
379
|
def self.write_report_to_files tracker, output_files
|
378
380
|
require 'fileutils'
|
379
|
-
tracker.options[:output_color] = false
|
381
|
+
tracker.options[:output_color] = false unless tracker.options[:output_color] == :force
|
380
382
|
|
381
383
|
output_files.each_with_index do |output_file, idx|
|
382
384
|
dir = File.dirname(output_file)
|
@@ -393,7 +395,7 @@ module Brakeman
|
|
393
395
|
private_class_method :write_report_to_files
|
394
396
|
|
395
397
|
def self.write_report_to_formats tracker, output_formats
|
396
|
-
unless $stdout.tty?
|
398
|
+
unless $stdout.tty? or tracker.options[:output_color] == :force
|
397
399
|
tracker.options[:output_color] = false
|
398
400
|
end
|
399
401
|
|
@@ -119,7 +119,10 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
119
119
|
|
120
120
|
#Does not actually process string interpolation, but notes that it occurred.
|
121
121
|
def process_dstr exp
|
122
|
-
@string_interp
|
122
|
+
unless @string_interp # don't overwrite existing value
|
123
|
+
@string_interp = Match.new(:interp, exp)
|
124
|
+
end
|
125
|
+
|
123
126
|
process_default exp
|
124
127
|
end
|
125
128
|
|
@@ -15,7 +15,8 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
15
15
|
|
16
16
|
SAFE_VALUES = [s(:const, :RAILS_ROOT),
|
17
17
|
s(:call, s(:const, :Rails), :root),
|
18
|
-
s(:call, s(:const, :Rails), :env)
|
18
|
+
s(:call, s(:const, :Rails), :env),
|
19
|
+
s(:call, s(:const, :Process), :pid)]
|
19
20
|
|
20
21
|
SHELL_ESCAPES = [:escape, :shellescape, :join]
|
21
22
|
|
@@ -32,7 +33,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
32
33
|
calls = tracker.find_call :targets => [:IO, :Open3, :Kernel, :'POSIX::Spawn', :Process, nil],
|
33
34
|
:methods => [:capture2, :capture2e, :capture3, :exec, :pipeline, :pipeline_r,
|
34
35
|
:pipeline_rw, :pipeline_start, :pipeline_w, :popen, :popen2, :popen2e,
|
35
|
-
:popen3, :spawn, :syscall, :system]
|
36
|
+
:popen3, :spawn, :syscall, :system], :nested => true
|
36
37
|
|
37
38
|
Brakeman.debug "Processing system calls"
|
38
39
|
calls.each do |result|
|
@@ -71,14 +71,15 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
CHECK_INSIDE_METHODS = [:url_for, :h, :sanitize]
|
75
|
+
|
74
76
|
def check_argument? url_arg
|
75
77
|
return unless call? url_arg
|
76
78
|
|
77
79
|
target = url_arg.target
|
78
80
|
method = url_arg.method
|
79
81
|
|
80
|
-
method
|
81
|
-
method == :h or
|
82
|
+
CHECK_INSIDE_METHODS.include? method or
|
82
83
|
cgi_escaped? target, method
|
83
84
|
end
|
84
85
|
|
@@ -46,12 +46,6 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
|
|
46
46
|
|
47
47
|
message = "Rails #{rails_version} has a vulnerability in #{method}: upgrade to #{@fix_version} or patch"
|
48
48
|
|
49
|
-
if include_user_input? result[:call]
|
50
|
-
confidence = :high
|
51
|
-
else
|
52
|
-
confidence = :medium
|
53
|
-
end
|
54
|
-
|
55
49
|
warn :result => result,
|
56
50
|
:warning_type => "Cross-Site Scripting",
|
57
51
|
:warning_code => code,
|
@@ -87,7 +81,7 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
|
|
87
81
|
warn :warning_type => "Cross-Site Scripting",
|
88
82
|
:warning_code => :CVE_2018_8048,
|
89
83
|
:message => message,
|
90
|
-
:gem_info => gemfile_or_environment,
|
84
|
+
:gem_info => gemfile_or_environment(:loofah),
|
91
85
|
:confidence => confidence,
|
92
86
|
:link_path => "https://github.com/flavorjones/loofah/issues/144"
|
93
87
|
end
|
@@ -111,7 +105,7 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
|
|
111
105
|
warn :warning_type => "Cross-Site Scripting",
|
112
106
|
:warning_code => cve.tr('-', '_').to_sym,
|
113
107
|
:message => message,
|
114
|
-
:gem_info => gemfile_or_environment,
|
108
|
+
:gem_info => gemfile_or_environment(:'rails-html-sanitizer'),
|
115
109
|
:confidence => confidence,
|
116
110
|
:link_path => link
|
117
111
|
end
|
@@ -290,7 +290,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
290
290
|
end
|
291
291
|
|
292
292
|
if request_value? arg
|
293
|
-
unless call? arg and params? arg.target and [:permit, :slice].include? arg.method
|
293
|
+
unless call? arg and params? arg.target and [:permit, :slice, :to_h, :to_hash].include? arg.method
|
294
294
|
# Model.where(params[:where])
|
295
295
|
arg
|
296
296
|
end
|
@@ -87,9 +87,9 @@ module Brakeman
|
|
87
87
|
|
88
88
|
def stripped_include_path(prefix, subprefixes, path)
|
89
89
|
if path.start_with?(prefix)
|
90
|
-
path.sub(%r{^#{prefix}/?}, "
|
90
|
+
path.sub(%r{^#{prefix}/?}, "/")
|
91
91
|
elsif subprefixes.any? { |subprefix| path =~ %r{^#{subprefix}/?$} }
|
92
|
-
"
|
92
|
+
"/"
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
data/lib/brakeman/file_parser.rb
CHANGED
@@ -7,6 +7,7 @@ module Brakeman
|
|
7
7
|
|
8
8
|
def initialize tracker, app_tree
|
9
9
|
@tracker = tracker
|
10
|
+
@timeout = @tracker.options[:parser_timeout]
|
10
11
|
@app_tree = app_tree
|
11
12
|
@file_list = {}
|
12
13
|
end
|
@@ -33,10 +34,13 @@ module Brakeman
|
|
33
34
|
def parse_ruby input, path
|
34
35
|
begin
|
35
36
|
Brakeman.debug "Parsing #{path}"
|
36
|
-
RubyParser.new.parse input, path
|
37
|
+
RubyParser.new.parse input, path, @timeout
|
37
38
|
rescue Racc::ParseError => e
|
38
39
|
@tracker.error e, "Could not parse #{path}"
|
39
40
|
nil
|
41
|
+
rescue Timeout::Error => e
|
42
|
+
@tracker.error Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout"), caller
|
43
|
+
nil
|
40
44
|
rescue => e
|
41
45
|
@tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
42
46
|
nil
|
data/lib/brakeman/options.rb
CHANGED
@@ -127,6 +127,10 @@ module Brakeman::Options
|
|
127
127
|
options[:branch_limit] = limit
|
128
128
|
end
|
129
129
|
|
130
|
+
opts.on "--parser-timeout SECONDS", Integer, "Set parse timeout (Default: 10)" do |timeout|
|
131
|
+
options[:parser_timeout] = timeout
|
132
|
+
end
|
133
|
+
|
130
134
|
opts.on "-r", "--report-direct", "Only report direct use of untrusted data" do |option|
|
131
135
|
options[:check_arguments] = !option
|
132
136
|
end
|
@@ -229,7 +233,11 @@ module Brakeman::Options
|
|
229
233
|
end
|
230
234
|
|
231
235
|
opts.on "--[no-]color", "Use ANSI colors in report (Default)" do |color|
|
232
|
-
|
236
|
+
if color
|
237
|
+
options[:output_color] = :force
|
238
|
+
else
|
239
|
+
options[:output_color] = color
|
240
|
+
end
|
233
241
|
end
|
234
242
|
|
235
243
|
opts.on "-m", "--routes", "Report controller information" do
|
@@ -93,7 +93,7 @@ module Brakeman
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def self.parse_inline_erb tracker, text
|
96
|
-
fp = Brakeman::FileParser.new(
|
96
|
+
fp = Brakeman::FileParser.new(tracker, nil)
|
97
97
|
tp = self.new(tracker, fp)
|
98
98
|
src = tp.parse_erb '_inline_', text
|
99
99
|
type = tp.erubis? ? :erubis : :erb
|
@@ -197,7 +197,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
197
197
|
return Sexp.new(:false)
|
198
198
|
end
|
199
199
|
|
200
|
-
|
201
200
|
#See if it is possible to simplify some basic cases
|
202
201
|
#of addition/concatenation.
|
203
202
|
case method
|
@@ -287,11 +286,80 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
287
286
|
if array? target and first_arg.nil? and sexp? target[1]
|
288
287
|
exp = target[1]
|
289
288
|
end
|
289
|
+
when :freeze
|
290
|
+
if string? target
|
291
|
+
exp = process exp.target
|
292
|
+
end
|
293
|
+
when :join
|
294
|
+
if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
|
295
|
+
exp = process_array_join(target, first_arg)
|
296
|
+
end
|
290
297
|
end
|
291
298
|
|
292
299
|
exp
|
293
300
|
end
|
294
301
|
|
302
|
+
# Painful conversion of Array#join into string interpolation
|
303
|
+
def process_array_join array, join_str
|
304
|
+
result = s()
|
305
|
+
|
306
|
+
join_value = if string? join_str
|
307
|
+
join_str.value
|
308
|
+
else
|
309
|
+
nil
|
310
|
+
end
|
311
|
+
|
312
|
+
array[1..-2].each do |e|
|
313
|
+
result << join_item(e, join_value)
|
314
|
+
end
|
315
|
+
|
316
|
+
result << join_item(array.last, nil)
|
317
|
+
|
318
|
+
# Combine the strings at the beginning because that's what RubyParser does
|
319
|
+
combined_first = ""
|
320
|
+
result.each do |e|
|
321
|
+
if string? e
|
322
|
+
combined_first << e.value
|
323
|
+
elsif e.is_a? String
|
324
|
+
combined_first << e
|
325
|
+
else
|
326
|
+
break
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Remove the strings at the beginning
|
331
|
+
result.reject! do |e|
|
332
|
+
if e.is_a? String or string? e
|
333
|
+
true
|
334
|
+
else
|
335
|
+
break
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
result.unshift combined_first
|
340
|
+
|
341
|
+
# Have to fix up strings that follow interpolation
|
342
|
+
result.reduce(s(:dstr)) do |memo, e|
|
343
|
+
if string? e and node_type? memo.last, :evstr
|
344
|
+
e.value = "#{join_value}#{e.value}"
|
345
|
+
elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
|
346
|
+
memo << s(:str, join_value)
|
347
|
+
end
|
348
|
+
|
349
|
+
memo << e
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def join_item item, join_value
|
354
|
+
if item.is_a? String
|
355
|
+
"#{item}#{join_value}"
|
356
|
+
elsif string? item or symbol? item or number? item
|
357
|
+
s(:str, "#{item.value}#{join_value}")
|
358
|
+
else
|
359
|
+
s(:evstr, item)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
295
363
|
def process_iter exp
|
296
364
|
@exp_context.push exp
|
297
365
|
exp[1] = process exp.block_call
|
@@ -19,6 +19,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
19
19
|
@current_method = opts[:method]
|
20
20
|
@current_template = opts[:template]
|
21
21
|
@current_file = opts[:file]
|
22
|
+
@current_call = nil
|
22
23
|
process exp
|
23
24
|
end
|
24
25
|
|
@@ -111,7 +112,8 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
111
112
|
:method => method_name,
|
112
113
|
:call => exp,
|
113
114
|
:nested => false,
|
114
|
-
:location => make_location
|
115
|
+
:location => make_location,
|
116
|
+
:parent => @current_call }
|
115
117
|
end
|
116
118
|
|
117
119
|
#Gets the target of a call as a Symbol
|
@@ -189,7 +191,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
189
191
|
def create_call_hash exp
|
190
192
|
target = get_target exp.target
|
191
193
|
|
192
|
-
if call? target
|
194
|
+
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
193
195
|
already_in_target = @in_target
|
194
196
|
@in_target = true
|
195
197
|
process target
|
@@ -199,13 +201,24 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
199
201
|
end
|
200
202
|
|
201
203
|
method = exp.method
|
202
|
-
process_call_args exp
|
203
204
|
|
204
|
-
|
205
|
+
call_hash = {
|
206
|
+
:target => target,
|
205
207
|
:method => method,
|
206
208
|
:call => exp,
|
207
209
|
:nested => @in_target,
|
208
210
|
:chain => get_chain(exp),
|
209
|
-
:location => make_location
|
211
|
+
:location => make_location,
|
212
|
+
:parent => @current_call
|
213
|
+
}
|
214
|
+
|
215
|
+
old_parent = @current_call
|
216
|
+
@current_call = call_hash
|
217
|
+
|
218
|
+
process_call_args exp
|
219
|
+
|
220
|
+
@current_call = old_parent
|
221
|
+
|
222
|
+
call_hash
|
210
223
|
end
|
211
224
|
end
|
@@ -102,7 +102,9 @@ module Brakeman
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def set_color
|
105
|
-
unless @tracker
|
105
|
+
return unless @tracker
|
106
|
+
|
107
|
+
unless less_options.include? "-R " or @tracker.options[:output_color] == :force
|
106
108
|
@tracker.options[:output_color] = false
|
107
109
|
end
|
108
110
|
end
|
data/lib/brakeman/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brakeman-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Collins
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain:
|
11
11
|
- brakeman-public_cert.pem
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-05-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -380,7 +380,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
380
380
|
version: '0'
|
381
381
|
requirements: []
|
382
382
|
rubyforge_project:
|
383
|
-
rubygems_version: 2.7.
|
383
|
+
rubygems_version: 2.7.6
|
384
384
|
signing_key:
|
385
385
|
specification_version: 4
|
386
386
|
summary: Security vulnerability scanner for Ruby on Rails.
|