brakeman-lib 4.3.0 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08b3a1c2f84d6667c990309480405c61baa2f2ac878dfdc12dbefacdff64a52e'
4
- data.tar.gz: 58ba877ea3b52c2e464bb4cca233edf1823f199db71e009df8eb92ce748ee9cf
3
+ metadata.gz: b66e4adfac1e60bdf4c0f7ba2962202120f879e0b15a456e8c6672ecd1320ad4
4
+ data.tar.gz: 9517cdd9c93e736e9fb8d4cf29c4aab8c9c232fc4166f52111b653982213e2e2
5
5
  SHA512:
6
- metadata.gz: 905adec5711738aaddf2fbfd2bebd9ce9aa039a556fcacbef39177e1d34b44dd953952bf56904444211876466da5c16ae2c2bd8d42ebca00b6a7755a26e600de
7
- data.tar.gz: da402133851cb1f3035cbe1784bf4c2e36831bd5167914c2bac82665b57430e2b9dbe9a4aa283e1365556dfcbfb07c933726049f9ee96377b83a676e56f1566f
6
+ metadata.gz: 6cb3584795f515314616a5f2e1116feba305333672e599b1b4f9d492fe40895f3d6067e13ad6431f5fc5fd3dfce636fbe39644e2f366b61a9ab12b4030ec4592
7
+ data.tar.gz: 455f61c1c33200355090cf541e57109416199b6bfe898c69cb972b537374cf315d485422b2065464a4a7519c5bb0e7f446d0f10356a959e9e0c8d776fcc5fb13
data/CHANGES.md CHANGED
@@ -1,3 +1,16 @@
1
+ # 4.3.1
2
+
3
+ * Ignore `Object#freeze`, use the target instead
4
+ * Ignore `foreign_key` calls in SQL
5
+ * Handle `included` calls outside of classes/modules
6
+ * Add `:BRAKEMAN_SAFE_LITERAL` to represent known-safe literals
7
+ * Handle `Array#map` and `Array#each` over literal arrays
8
+ * Use safe literal when accessing literal hash with unknown key
9
+ * Avoid deprecated use of ERB in Ruby 2.6 (Koichi ITO)
10
+ * Allow `symbolize_keys` to be called on `params` in SQL (Jacob Evelyn)
11
+ * Improve handling of conditionals in shell commands (Jacob Evenlyn)
12
+ * Fix error when setting line number in implicit renders
13
+
1
14
  # 4.3.0
2
15
 
3
16
  * Check exec-type calls even if they are targets
data/README.md CHANGED
@@ -82,9 +82,9 @@ If Brakeman is running a bit slow, try
82
82
 
83
83
  This will disable some features, but will probably be much faster (currently it is the same as `--skip-libs --no-branching`). *WARNING*: This may cause Brakeman to miss some vulnerabilities.
84
84
 
85
- By default, Brakeman will return 0 as an exit code unless something went very wrong. To return an error code when warnings were found:
85
+ By default, Brakeman will return a non-zero exit code if any security warnings are found or scanning errors are encountered. To disable this:
86
86
 
87
- brakeman -z
87
+ brakeman --no-exit-on-warn --no-exit-on-error
88
88
 
89
89
  To skip certain files or directories that Brakeman may have trouble parsing, use:
90
90
 
@@ -143,7 +143,13 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
143
143
  next if SAFE_VALUES.include? e
144
144
  next if shell_escape? e
145
145
 
146
- if node_type? e, :or, :evstr, :dstr
146
+ if node_type? e, :if
147
+ # If we're in a conditional, evaluate the `then` and `else` clauses to
148
+ # see if they're dangerous.
149
+ if res = dangerous?(e.values[1..-1])
150
+ return res
151
+ end
152
+ elsif node_type? e, :or, :evstr, :dstr
147
153
  if res = dangerous?(e)
148
154
  return res
149
155
  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, :to_h, :to_hash].include? arg.method
293
+ unless call? arg and params? arg.target and [:permit, :slice, :to_h, :to_hash, :symbolize_keys].include? arg.method
294
294
  # Model.where(params[:where])
295
295
  arg
296
296
  end
@@ -404,6 +404,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
404
404
  nil
405
405
  elsif call? value and value.method == :to_s
406
406
  unsafe_string_interp? value.target
407
+ elsif call? value and safe_literal_target? value
408
+ nil
407
409
  else
408
410
  case value.node_type
409
411
  when :or
@@ -576,7 +578,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
576
578
  :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
577
579
  :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
578
580
  :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,
579
- :where_values_hash
581
+ :where_values_hash, :foreign_key
580
582
  ]
581
583
 
582
584
  def safe_value? exp
@@ -58,7 +58,11 @@ module Brakeman
58
58
  Brakeman::ScannerErubis.new(text, :filename => path).src
59
59
  else
60
60
  require 'erb'
61
- src = ERB.new(text, nil, path).src
61
+ src = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
62
+ ERB.new(text, trim_mode: path).src
63
+ else
64
+ ERB.new(text, nil, path).src
65
+ end
62
66
  src.sub!(/^#.*\n/, '') if Brakeman::Scanner::RUBY_1_9
63
67
  src
64
68
  end
@@ -2,6 +2,7 @@ require 'brakeman/util'
2
2
  require 'ruby_parser/bm_sexp_processor'
3
3
  require 'brakeman/processors/lib/processor_helper'
4
4
  require 'brakeman/processors/lib/safe_call_helper'
5
+ require 'brakeman/processors/lib/call_conversion_helper'
5
6
 
6
7
  #Returns an s-expression with aliases replaced with their value.
7
8
  #This does not preserve semantics (due to side effects, etc.), but it makes
@@ -10,6 +11,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
10
11
  include Brakeman::ProcessorHelper
11
12
  include Brakeman::SafeCallHelper
12
13
  include Brakeman::Util
14
+ include Brakeman::CallConversionHelper
13
15
 
14
16
  attr_reader :result, :tracker
15
17
 
@@ -122,7 +124,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
122
124
  end
123
125
 
124
126
  if hash? t
125
- if v = hash_access(t, exp.first_arg)
127
+ if v = process_hash_access(t, exp.first_arg)
126
128
  v.deep_clone(exp.line)
127
129
  else
128
130
  case t.node_type
@@ -202,49 +204,19 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
202
204
  case method
203
205
  when :+
204
206
  if array? target and array? first_arg
205
- joined = join_arrays target, first_arg
206
- joined.line(exp.line)
207
- exp = joined
207
+ exp = join_arrays(target, first_arg, exp)
208
208
  elsif string? first_arg
209
- if string? target # "blah" + "blah"
210
- joined = join_strings target, first_arg
211
- joined.line(exp.line)
212
- exp = joined
213
- elsif call? target and target.method == :+ and string? target.first_arg
214
- joined = join_strings target.first_arg, first_arg
215
- joined.line(exp.line)
216
- target.first_arg = joined
217
- exp = target
218
- end
209
+ exp = join_strings(target, first_arg, exp)
219
210
  elsif number? first_arg
220
- if number? target
221
- exp = Sexp.new(:lit, target.value + first_arg.value)
222
- elsif call? target and target.method == :+ and number? target.first_arg
223
- target.first_arg = Sexp.new(:lit, target.first_arg.value + first_arg.value)
224
- exp = target
225
- end
226
- end
227
- when :-
228
- if number? target and number? first_arg
229
- exp = Sexp.new(:lit, target.value - first_arg.value)
230
- end
231
- when :*
232
- if number? target and number? first_arg
233
- exp = Sexp.new(:lit, target.value * first_arg.value)
234
- end
235
- when :/
236
- if number? target and number? first_arg
237
- unless first_arg.value == 0 and not target.value.is_a? Float
238
- exp = Sexp.new(:lit, target.value / first_arg.value)
239
- end
211
+ exp = math_op(:+, target, first_arg, exp)
240
212
  end
213
+ when :-, :*, :/
214
+ exp = math_op(method, target, first_arg, exp)
241
215
  when :[]
242
216
  if array? target
243
- temp_exp = process_array_access target, exp.args
244
- exp = temp_exp if temp_exp
217
+ exp = process_array_access(target, exp.args, exp)
245
218
  elsif hash? target
246
- temp_exp = process_hash_access target, first_arg
247
- exp = temp_exp if temp_exp
219
+ exp = process_hash_access(target, first_arg, exp)
248
220
  end
249
221
  when :merge!, :update
250
222
  if hash? target and hash? first_arg
@@ -287,8 +259,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
287
259
  exp = target[1]
288
260
  end
289
261
  when :freeze
290
- if string? target
291
- exp = process exp.target
262
+ unless target.nil?
263
+ exp = target
292
264
  end
293
265
  when :join
294
266
  if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
@@ -364,28 +336,37 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
364
336
  @exp_context.push exp
365
337
  exp[1] = process exp.block_call
366
338
  if array_detect_all_literals? exp[1]
367
- return exp.block_call.target[1]
339
+ return safe_literal(exp.line)
368
340
  end
369
341
 
370
342
  @exp_context.pop
371
343
 
372
344
  env.scope do
373
- exp.block_args.each do |e|
374
- #Force block arg(s) to be local
375
- if node_type? e, :lasgn
376
- env.current[Sexp.new(:lvar, e.lhs)] = Sexp.new(:lvar, e.lhs)
377
- elsif node_type? e, :kwarg
378
- env.current[Sexp.new(:lvar, e[1])] = e[2]
379
- elsif node_type? e, :masgn, :shadow
380
- e[1..-1].each do |var|
381
- local = Sexp.new(:lvar, var)
345
+ call = exp.block_call
346
+ block_args = exp.block_args
347
+
348
+ if call? call and [:each, :map].include? call.method and all_literals? call.target and block_args.length == 2 and block_args.last.is_a? Symbol
349
+ # Iterating over an array of all literal values
350
+ local = Sexp.new(:lvar, block_args.last)
351
+ env.current[local] = safe_literal(exp.line)
352
+ else
353
+ block_args.each do |e|
354
+ #Force block arg(s) to be local
355
+ if node_type? e, :lasgn
356
+ env.current[Sexp.new(:lvar, e.lhs)] = Sexp.new(:lvar, e.lhs)
357
+ elsif node_type? e, :kwarg
358
+ env.current[Sexp.new(:lvar, e[1])] = e[2]
359
+ elsif node_type? e, :masgn, :shadow
360
+ e[1..-1].each do |var|
361
+ local = Sexp.new(:lvar, var)
362
+ env.current[local] = local
363
+ end
364
+ elsif e.is_a? Symbol
365
+ local = Sexp.new(:lvar, e)
382
366
  env.current[local] = local
367
+ else
368
+ raise "Unexpected value in block args: #{e.inspect}"
383
369
  end
384
- elsif e.is_a? Symbol
385
- local = Sexp.new(:lvar, e)
386
- env.current[local] = local
387
- else
388
- raise "Unexpected value in block args: #{e.inspect}"
389
370
  end
390
371
  end
391
372
 
@@ -715,18 +696,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
715
696
  def array_include_all_literals? exp
716
697
  call? exp and
717
698
  exp.method == :include? and
718
- node_type? exp.target, :array and
719
- exp.target.length > 1 and
720
- exp.target.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
699
+ all_literals? exp.target
721
700
  end
722
701
 
723
702
  def array_detect_all_literals? exp
724
703
  call? exp and
725
704
  [:detect, :find].include? exp.method and
726
- node_type? exp.target, :array and
727
- exp.target.length > 1 and
728
705
  exp.first_arg.nil? and
729
- exp.target.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
706
+ all_literals? exp.target
730
707
  end
731
708
 
732
709
  #Sets @inside_if = true
@@ -767,12 +744,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
767
744
  # set x to "a" inside the true branch
768
745
  var = condition.first_arg
769
746
  previous_value = env.current[var]
770
- env.current[var] = condition.target[1]
747
+ env.current[var] = safe_literal(var.line)
771
748
  exp[branch_index] = process_if_branch branch
772
749
  env.current[var] = previous_value
773
750
  elsif i == 1 and array_include_all_literals? condition and early_return? branch
774
751
  var = condition.first_arg
775
- env.current[var] = condition.target[1]
752
+ env.current[var] = safe_literal(var.line)
776
753
  exp[branch_index] = process_if_branch branch
777
754
  else
778
755
  exp[branch_index] = process_if_branch branch
@@ -911,45 +888,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
911
888
  exp.or_depth >= @or_depth_limit
912
889
  end
913
890
 
914
- #Process single integer access to an array.
915
- #
916
- #Returns the value inside the array, if possible.
917
- def process_array_access target, args
918
- if args.length == 1 and integer? args.first
919
- index = args.first.value
920
-
921
- #Have to do this because first element is :array and we have to skip it
922
- target[1..-1][index]
923
- else
924
- nil
925
- end
926
- end
927
-
928
- #Process hash access by returning the value associated
929
- #with the given argument.
930
- def process_hash_access target, index
931
- hash_access(target, index)
932
- end
933
-
934
- #Join two array literals into one.
935
- def join_arrays array1, array2
936
- result = Sexp.new(:array)
937
- result.concat array1[1..-1]
938
- result.concat array2[1..-1]
939
- end
940
-
941
- #Join two string literals into one.
942
- def join_strings string1, string2
943
- result = Sexp.new(:str)
944
- result.value = string1.value + string2.value
945
-
946
- if result.value.length > 50
947
- string1
948
- else
949
- result
950
- end
951
- end
952
-
953
891
  # Change x.send(:y, 1) to x.y(1)
954
892
  def collapse_send_call exp, first_arg
955
893
  # Handle try(&:id)
@@ -179,8 +179,11 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
179
179
  # method as the line number
180
180
  if line.nil? and controller = @tracker.controllers[@current_class]
181
181
  if meth = controller.get_method(@current_method)
182
- line = meth[:src] && meth[:src].last && meth[:src].last.line
183
- line += 1
182
+ if line = meth[:src] && meth[:src].last && meth[:src].last.line
183
+ line += 1
184
+ else
185
+ line = 1
186
+ end
184
187
  end
185
188
  end
186
189
 
@@ -0,0 +1,90 @@
1
+ module Brakeman
2
+ module CallConversionHelper
3
+ def all_literals? exp, expected_type = :array
4
+ node_type? exp, expected_type and
5
+ exp.length > 1 and
6
+ exp.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
7
+ end
8
+
9
+ # Join two array literals into one.
10
+ def join_arrays lhs, rhs, original_exp = nil
11
+ if array? lhs and array? rhs
12
+ result = Sexp.new(:array).line(lhs.line)
13
+ result.concat lhs[1..-1]
14
+ result.concat rhs[1..-1]
15
+ result
16
+ else
17
+ original_exp
18
+ end
19
+ end
20
+
21
+ # Join two string literals into one.
22
+ def join_strings lhs, rhs, original_exp = nil
23
+ if string? lhs and string? rhs
24
+ result = Sexp.new(:str).line(lhs.line)
25
+ result.value = lhs.value + rhs.value
26
+
27
+ if result.value.length > 50
28
+ # Avoid gigantic strings
29
+ lhs
30
+ else
31
+ result
32
+ end
33
+ elsif call? lhs and lhs.method == :+ and string? lhs.first_arg and string? rhs
34
+ joined = join_strings lhs.first_arg, rhs
35
+ lhs.first_arg = joined
36
+ lhs
37
+ elsif safe_literal? lhs or safe_literal? rhs
38
+ safe_literal(lhs.line)
39
+ else
40
+ original_exp
41
+ end
42
+ end
43
+
44
+ def math_op op, lhs, rhs, original_exp = nil
45
+ if number? lhs and number? rhs
46
+ if op == :/ and rhs.value == 0 and not lhs.value.is_a? Float
47
+ # Avoid division by zero
48
+ return original_exp
49
+ else
50
+ value = lhs.value.send(op, rhs.value)
51
+ Sexp.new(:lit, value).line(lhs.line)
52
+ end
53
+ elsif call? lhs and lhs.method == :+ and number? lhs.first_arg and number? rhs
54
+ # (x + 1) + 2 -> (x + 3)
55
+ lhs.first_arg = Sexp.new(:lit, lhs.first_arg.value + rhs.value).line(lhs.first_arg.line)
56
+ lhs
57
+ elsif safe_literal? lhs or safe_literal? rhs
58
+ safe_literal(lhs.line)
59
+ else
60
+ original_exp
61
+ end
62
+ end
63
+
64
+ # Process single integer access to an array.
65
+ #
66
+ # Returns the value inside the array, if possible.
67
+ def process_array_access array, args, original_exp = nil
68
+ if args.length == 1 and integer? args.first
69
+ index = args.first.value
70
+
71
+ #Have to do this because first element is :array and we have to skip it
72
+ array[1..-1][index] or original_exp
73
+ else
74
+ original_exp
75
+ end
76
+ end
77
+
78
+ # Process hash access by returning the value associated
79
+ # with the given argument.
80
+ def process_hash_access hash, index, original_exp = nil
81
+ if value = hash_access(hash, index)
82
+ value # deep_clone?
83
+ elsif all_literals? hash, :hash
84
+ safe_literal(hash.line)
85
+ else
86
+ original_exp
87
+ end
88
+ end
89
+ end
90
+ end
@@ -64,7 +64,7 @@ class Brakeman::LibraryProcessor < Brakeman::BaseProcessor
64
64
  res = process_default exp
65
65
 
66
66
  if node_type? res, :iter and call? exp.block_call # sometimes this changes after processing
67
- if exp.block_call.method == :included
67
+ if exp.block_call.method == :included and (@current_module or @current_class)
68
68
  (@current_module || @current_class).options[:included] = res.block
69
69
  end
70
70
  end
data/lib/brakeman/util.rb CHANGED
@@ -26,6 +26,8 @@ module Brakeman::Util
26
26
 
27
27
  ALL_COOKIES = Set[COOKIES, REQUEST_COOKIES]
28
28
 
29
+ SAFE_LITERAL = s(:lit, :BRAKEMAN_SAFE_LITERAL)
30
+
29
31
  #Convert a string from "something_like_this" to "SomethingLikeThis"
30
32
  #
31
33
  #Taken from ActiveSupport.
@@ -307,6 +309,22 @@ module Brakeman::Util
307
309
  call
308
310
  end
309
311
 
312
+ def safe_literal line = nil
313
+ s(:lit, :BRAKEMAN_SAFE_LITERAL).line(line || 0)
314
+ end
315
+
316
+ def safe_literal? exp
317
+ exp == SAFE_LITERAL
318
+ end
319
+
320
+ def safe_literal_target? exp
321
+ if call? exp
322
+ safe_literal_target? exp.target
323
+ else
324
+ safe_literal? exp
325
+ end
326
+ end
327
+
310
328
  def rails_version
311
329
  @tracker.config.rails_version
312
330
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.3.0"
2
+ Version = "4.3.1"
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.3.0
4
+ version: 4.3.1
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-05-11 00:00:00.000000000 Z
12
+ date: 2018-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -297,6 +297,7 @@ files:
297
297
  - lib/brakeman/processors/gem_processor.rb
298
298
  - lib/brakeman/processors/haml_template_processor.rb
299
299
  - lib/brakeman/processors/lib/basic_processor.rb
300
+ - lib/brakeman/processors/lib/call_conversion_helper.rb
300
301
  - lib/brakeman/processors/lib/find_all_calls.rb
301
302
  - lib/brakeman/processors/lib/find_call.rb
302
303
  - lib/brakeman/processors/lib/find_return_value.rb