brakeman-lib 4.2.1 → 4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7070db1a411e9196bbb90fe7a34209ecd8cf15210e5fa21be1442c668e8f00d0
4
- data.tar.gz: 940132f27d3803e1fa445d51c869cf426a712d065892f49b6e138222dc66ba71
3
+ metadata.gz: '08b3a1c2f84d6667c990309480405c61baa2f2ac878dfdc12dbefacdff64a52e'
4
+ data.tar.gz: 58ba877ea3b52c2e464bb4cca233edf1823f199db71e009df8eb92ce748ee9cf
5
5
  SHA512:
6
- metadata.gz: dfbb4f4f11b1e8b00a206a6185e3e58862061f954a43cdf611fb38d6bd97b0e04ef6439adcc68b6ea6dc1675b5853f8a9af03c378be3d7cc6d9f4f49a61f5d5d
7
- data.tar.gz: 77cb923ae34ee9a094200e1dc08a120ca001b4f646907fcb896bc147694041c989bfea7dc3023d876c0ee1461bedd5dc35cae53b94ef36ee6971baff980ceaae
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
@@ -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 = Match.new(:interp, exp)
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 == :url_for or
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
@@ -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
@@ -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
- options[:output_color] = color
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(nil, nil)
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
- { :target => target,
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 and less_options.include? "-R "
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
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.2.1"
2
+ Version = "4.3.0"
3
3
  end
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.2.1
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-03-24 00:00:00.000000000 Z
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.3
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.