brakeman 1.8.3 → 1.9.0.pre1

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.
Files changed (63) hide show
  1. data/README.md +3 -27
  2. data/lib/brakeman.rb +36 -38
  3. data/lib/brakeman/app_tree.rb +90 -0
  4. data/lib/brakeman/call_index.rb +5 -38
  5. data/lib/brakeman/checks.rb +11 -11
  6. data/lib/brakeman/checks/base_check.rb +53 -29
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +11 -9
  8. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  9. data/lib/brakeman/checks/check_execute.rb +3 -3
  10. data/lib/brakeman/checks/check_link_to.rb +15 -13
  11. data/lib/brakeman/checks/check_link_to_href.rb +1 -1
  12. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  13. data/lib/brakeman/checks/check_mass_assignment.rb +27 -13
  14. data/lib/brakeman/checks/check_redirect.rb +4 -4
  15. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  16. data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
  17. data/lib/brakeman/checks/check_send.rb +2 -2
  18. data/lib/brakeman/checks/check_session_settings.rb +12 -5
  19. data/lib/brakeman/checks/check_single_quotes.rb +3 -3
  20. data/lib/brakeman/checks/check_skip_before_filter.rb +4 -3
  21. data/lib/brakeman/checks/check_sql.rb +30 -30
  22. data/lib/brakeman/checks/check_translate_bug.rb +11 -10
  23. data/lib/brakeman/checks/check_validation_regex.rb +36 -11
  24. data/lib/brakeman/checks/check_without_protection.rb +1 -1
  25. data/lib/brakeman/options.rb +6 -2
  26. data/lib/brakeman/processor.rb +6 -5
  27. data/lib/brakeman/processors/alias_processor.rb +153 -38
  28. data/lib/brakeman/processors/base_processor.rb +16 -21
  29. data/lib/brakeman/processors/controller_alias_processor.rb +24 -11
  30. data/lib/brakeman/processors/controller_processor.rb +25 -25
  31. data/lib/brakeman/processors/erb_template_processor.rb +6 -7
  32. data/lib/brakeman/processors/erubis_template_processor.rb +2 -3
  33. data/lib/brakeman/processors/gem_processor.rb +5 -4
  34. data/lib/brakeman/processors/haml_template_processor.rb +4 -6
  35. data/lib/brakeman/processors/lib/find_all_calls.rb +3 -3
  36. data/lib/brakeman/processors/lib/find_call.rb +2 -2
  37. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  38. data/lib/brakeman/processors/lib/processor_helper.rb +24 -2
  39. data/lib/brakeman/processors/lib/rails2_config_processor.rb +13 -14
  40. data/lib/brakeman/processors/lib/rails2_route_processor.rb +9 -4
  41. data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -8
  42. data/lib/brakeman/processors/lib/rails3_route_processor.rb +23 -21
  43. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  44. data/lib/brakeman/processors/library_processor.rb +2 -2
  45. data/lib/brakeman/processors/model_processor.rb +16 -12
  46. data/lib/brakeman/processors/output_processor.rb +2 -1
  47. data/lib/brakeman/processors/template_alias_processor.rb +12 -8
  48. data/lib/brakeman/report.rb +28 -14
  49. data/lib/brakeman/rescanner.rb +5 -5
  50. data/lib/brakeman/scanner.rb +56 -94
  51. data/lib/brakeman/templates/header.html.erb +7 -2
  52. data/lib/brakeman/tracker.rb +14 -4
  53. data/lib/brakeman/util.rb +38 -17
  54. data/lib/brakeman/version.rb +1 -1
  55. data/lib/brakeman/warning.rb +14 -6
  56. data/lib/ruby_parser/bm_sexp.rb +157 -57
  57. data/lib/ruby_parser/bm_sexp_processor.rb +1 -2
  58. metadata +26 -25
  59. data/lib/ruby_parser/ruby18_parser.rb +0 -5544
  60. data/lib/ruby_parser/ruby19_parser.rb +0 -5756
  61. data/lib/ruby_parser/ruby_lexer.rb +0 -1349
  62. data/lib/ruby_parser/ruby_parser.rb +0 -5
  63. data/lib/ruby_parser/ruby_parser_extras.rb +0 -1057
@@ -31,7 +31,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
31
31
 
32
32
  CGI = Sexp.new(:const, :CGI)
33
33
 
34
- FORM_BUILDER = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
34
+ FORM_BUILDER = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new)
35
35
 
36
36
  #Run check
37
37
  def run_check
@@ -60,7 +60,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
60
60
 
61
61
  json_escape_on = false
62
62
  initializers = tracker.check_initializers :ActiveSupport, :escape_html_entities_in_json=
63
- initializers.each {|result| json_escape_on = true?(result[-1].first_arg) }
63
+ initializers.each {|result| json_escape_on = true?(result.call.first_arg) }
64
64
 
65
65
  if !json_escape_on or version_between? "0.0.0", "2.0.99"
66
66
  @known_dangerous << :to_json
@@ -115,7 +115,11 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
115
115
  :confidence => CONFIDENCE[:high]
116
116
 
117
117
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out)
118
- method = match[2]
118
+ method = if call? match
119
+ match.method
120
+ else
121
+ nil
122
+ end
119
123
 
120
124
  unless IGNORE_MODEL_METHODS.include? method
121
125
  add_result out
@@ -227,7 +231,6 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
227
231
  end
228
232
 
229
233
  method = exp.method
230
- args = exp.arglist
231
234
 
232
235
  #Ignore safe items
233
236
  if (target.nil? and (@ignore_methods.include? method or method.to_s =~ IGNORE_LIKE)) or
@@ -241,14 +244,14 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
241
244
 
242
245
  #exp[0] = :ignore #should not be necessary
243
246
  @matched = false
244
- elsif sexp? target and model_name? target[1]
247
+ elsif sexp? target and model_name? target[1] #TODO: use method call?
245
248
  @matched = Match.new(:model, exp)
246
249
  elsif cookies? exp
247
250
  @matched = Match.new(:cookies, exp)
248
251
  elsif @inspect_arguments and params? exp
249
252
  @matched = Match.new(:params, exp)
250
253
  elsif @inspect_arguments
251
- process args
254
+ process_call_args exp
252
255
  end
253
256
  end
254
257
 
@@ -286,9 +289,8 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
286
289
 
287
290
  #Ignore condition in if Sexp
288
291
  def process_if exp
289
- exp[2..-1].each do |e|
290
- process e if sexp? e
291
- end
292
+ process exp.then_clause if sexp? exp.then_clause
293
+ process exp.else_clause if sexp? exp.else_clause
292
294
  exp
293
295
  end
294
296
 
@@ -20,7 +20,7 @@ class Brakeman::CheckEvaluation < Brakeman::BaseCheck
20
20
 
21
21
  #Warns if eval includes user input
22
22
  def process_result result
23
- if input = include_user_input?(result[:call][-1])
23
+ if input = include_user_input?(result[:call].arglist)
24
24
  warn :result => result,
25
25
  :warning_type => "Dangerous Eval",
26
26
  :message => "User input in eval",
@@ -30,12 +30,12 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
30
30
  #Processes results from Tracker#find_call.
31
31
  def process_result result
32
32
  call = result[:call]
33
-
34
- args = process call[3]
33
+ args = call.arglist
34
+ first_arg = call.first_arg
35
35
 
36
36
  case call.method
37
37
  when :system, :exec
38
- failure = include_user_input?(args[1]) || include_interp?(args[1])
38
+ failure = include_user_input?(first_arg) || include_interp?(first_arg)
39
39
  else
40
40
  failure = include_user_input?(args) || include_interp?(args)
41
41
  end
@@ -23,7 +23,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
23
23
  @known_dangerous = []
24
24
  #Ideally, I think this should also check to see if people are setting
25
25
  #:escape => false
26
- methods = tracker.find_call :target => false, :method => :link_to
26
+ methods = tracker.find_call :target => false, :method => :link_to
27
27
 
28
28
  @models = tracker.models.keys
29
29
  @inspect_arguments = tracker.options[:check_arguments]
@@ -40,23 +40,24 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
40
40
  #an ignored method call by the code above.
41
41
  call = result[:call] = result[:call].dup
42
42
 
43
- args = call.args
43
+ first_arg = call.first_arg
44
+ second_arg = call.second_arg
44
45
 
45
46
  @matched = false
46
47
 
47
48
  #Skip if no arguments(?) or first argument is a hash
48
- return if args.first.nil? or hash? args.first
49
+ return if first_arg.nil? or hash? first_arg
49
50
 
50
51
  if version_between? "2.0.0", "2.2.99"
51
- check_argument result, args.first
52
+ check_argument result, first_arg
52
53
 
53
- if args.second and not hash? args.second
54
- check_argument result, args.second
54
+ if second_arg and not hash? second_arg
55
+ check_argument result, second_arg
55
56
  end
56
- elsif args.second
57
+ elsif second_arg
57
58
  #Only check first argument if there is a second argument
58
59
  #in Rails 2.3.x
59
- check_argument result, args.first
60
+ check_argument result, first_arg
60
61
  end
61
62
  end
62
63
 
@@ -75,14 +76,14 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
75
76
 
76
77
  add_result result
77
78
  warn :result => result,
78
- :warning_type => "Cross Site Scripting",
79
+ :warning_type => "Cross Site Scripting",
79
80
  :message => message,
80
81
  :user_input => input.match,
81
82
  :confidence => CONFIDENCE[:high],
82
83
  :link_path => "link_to"
83
84
 
84
85
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
85
- method = match[2]
86
+ method = match.method
86
87
 
87
88
  unless IGNORE_MODEL_METHODS.include? method
88
89
  add_result result
@@ -94,7 +95,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
94
95
  end
95
96
 
96
97
  warn :result => result,
97
- :warning_type => "Cross Site Scripting",
98
+ :warning_type => "Cross Site Scripting",
98
99
  :message => "Unescaped model attribute in link_to",
99
100
  :user_input => match,
100
101
  :confidence => confidence,
@@ -111,8 +112,8 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
111
112
  if message
112
113
  add_result result
113
114
 
114
- warn :result => result,
115
- :warning_type => "Cross Site Scripting",
115
+ warn :result => result,
116
+ :warning_type => "Cross Site Scripting",
116
117
  :message => message,
117
118
  :user_input => @matched.match,
118
119
  :confidence => CONFIDENCE[:med],
@@ -137,6 +138,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
137
138
 
138
139
  #Bare records create links to the model resource,
139
140
  #not a string that could have injection
141
+ #TODO: Needs test? I think this is broken?
140
142
  if model_name? target and context == [:call, :arglist]
141
143
  return exp
142
144
  end
@@ -34,7 +34,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
34
34
  #an ignored method call by the code above.
35
35
  call = result[:call] = result[:call].dup
36
36
  @matched = false
37
- url_arg = process call.args.second
37
+ url_arg = process call.second_arg
38
38
 
39
39
  #Ignore situations where the href is an interpolated string
40
40
  #with something before the user input
@@ -34,7 +34,7 @@ class Brakeman::CheckMailTo < Brakeman::BaseCheck
34
34
  Brakeman.debug "Checking calls to mail_to for javascript encoding"
35
35
 
36
36
  tracker.find_call(:target => false, :method => :mail_to).each do |result|
37
- result[:call].arglist.each do |arg|
37
+ result[:call].each_arg do |arg|
38
38
  if hash? arg
39
39
  if option = hash_access(arg, :encode)
40
40
  return result if symbol? option and option.value == :javascript
@@ -78,13 +78,14 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
78
78
 
79
79
  #Want to ignore calls to Model.new that have no arguments
80
80
  def check_call call
81
- args = process_all call.args
81
+ process_call_args call
82
+ first_arg = call.first_arg
82
83
 
83
- if args.empty? #empty new()
84
+ if first_arg.nil? #empty new()
84
85
  false
85
- elsif hash? args.first and not include_user_input? args.first
86
+ elsif hash? first_arg and not include_user_input? first_arg
86
87
  false
87
- elsif all_literals? args
88
+ elsif all_literal_args? call
88
89
  false
89
90
  else
90
91
  true
@@ -93,17 +94,30 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
93
94
 
94
95
  LITERALS = Set[:lit, :true, :false, :nil, :string]
95
96
 
96
- def all_literals? args
97
- args.all? do |arg|
98
- if sexp? arg
99
- if arg.node_type == :hash
100
- all_literals? arg
101
- else
102
- LITERALS.include? arg.node_type
103
- end
97
+ def all_literal_args? exp
98
+ if call? exp
99
+ exp.each_arg do |arg|
100
+ return false unless literal? arg
101
+ end
102
+
103
+ true
104
+ else
105
+ exp.all? do |arg|
106
+ literal? arg
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ def literal? exp
113
+ if sexp? exp
114
+ if exp.node_type == :hash
115
+ all_literal_args? exp
104
116
  else
105
- true
117
+ LITERALS.include? exp.node_type
106
118
  end
119
+ else
120
+ true
107
121
  end
108
122
  end
109
123
  end
@@ -73,12 +73,12 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
73
73
  elsif call? arg
74
74
  if request_value? arg
75
75
  return Match.new(immediate, arg)
76
- elsif request_value? arg[1]
77
- return Match.new(immediate, arg[1])
78
- elsif arg[2] == :url_for and include_user_input? arg
76
+ elsif request_value? arg.target
77
+ return Match.new(immediate, arg.target)
78
+ elsif arg.method == :url_for and include_user_input? arg
79
79
  return Match.new(immediate, arg)
80
80
  #Ignore helpers like some_model_url?
81
- elsif arg[2].to_s =~ /_(url|path)\z/
81
+ elsif arg.method.to_s =~ /_(url|path)\z/
82
82
  return false
83
83
  end
84
84
  elsif request_value? arg
@@ -38,7 +38,7 @@ class Brakeman::CheckSelectTag < Brakeman::BaseCheck
38
38
  add_result result
39
39
 
40
40
  #Only concerned if user input is supplied for :prompt option
41
- last_arg = result[:call].arglist.last
41
+ last_arg = result[:call].last_arg
42
42
 
43
43
  if hash? last_arg
44
44
  prompt_option = hash_access last_arg, :prompt
@@ -35,7 +35,7 @@ class Brakeman::CheckSelectVulnerability < Brakeman::BaseCheck
35
35
  def process_result result
36
36
  return if duplicate? result
37
37
 
38
- third_arg = result[:call].args[2]
38
+ third_arg = result[:call].third_arg
39
39
 
40
40
  #Check for user input in options parameter
41
41
  if sexp? third_arg and include_user_input? third_arg
@@ -16,10 +16,10 @@ class Brakeman::CheckSend < Brakeman::BaseCheck
16
16
  end
17
17
 
18
18
  def process_result result
19
- args = process_all result[:call].args
19
+ process_call_args result[:call]
20
20
  target = process result[:call].target
21
21
 
22
- if input = has_immediate_user_input?(args.first)
22
+ if input = has_immediate_user_input?(result[:call].first_arg)
23
23
  warn :result => result,
24
24
  :warning_type => "Dangerous Send",
25
25
  :message => "User controlled method execution",
@@ -9,10 +9,10 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
9
9
  def initialize *args
10
10
  super
11
11
 
12
- if tracker.options[:rails3]
13
- @session_settings = Sexp.new(:call, Sexp.new(:colon2, Sexp.new(:const, :Rails3), :Application), :config, Sexp.new(:arglist))
14
- else
12
+ unless tracker.options[:rails3]
15
13
  @session_settings = Sexp.new(:colon2, Sexp.new(:const, :ActionController), :Base)
14
+ else
15
+ @session_settings = nil
16
16
  end
17
17
  end
18
18
 
@@ -41,8 +41,8 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
41
41
  #Looks for Rails3::Application.config.session_store :cookie_store, { ... }
42
42
  #in Rails 3.x apps
43
43
  def process_call exp
44
- if tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session_store
45
- check_for_issues exp.args.second, "#{tracker.options[:app_path]}/config/initializers/session_store.rb"
44
+ if tracker.options[:rails3] and settings_target?(exp.target) and exp.method == :session_store
45
+ check_for_issues exp.second_arg, "#{tracker.options[:app_path]}/config/initializers/session_store.rb"
46
46
  end
47
47
 
48
48
  exp
@@ -50,6 +50,13 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
50
50
 
51
51
  private
52
52
 
53
+ def settings_target? exp
54
+ call? exp and
55
+ exp.method == :config and
56
+ node_type? exp.target, :colon2 and
57
+ exp.target.rhs == :Application
58
+ end
59
+
53
60
  def check_for_issues settings, file
54
61
  if settings and hash? settings
55
62
  if value = hash_access(settings, :session_http_only)
@@ -52,7 +52,7 @@ class Brakeman::CheckSingleQuotes < Brakeman::BaseCheck
52
52
  def process_class exp
53
53
  if exp.class_name == :ERB
54
54
  @inside_erb = true
55
- process exp.body
55
+ process_all exp.body
56
56
  @inside_erb = false
57
57
  end
58
58
 
@@ -65,7 +65,7 @@ class Brakeman::CheckSingleQuotes < Brakeman::BaseCheck
65
65
  def process_module exp
66
66
  if @inside_erb and exp.module_name == :Util
67
67
  @inside_util = true
68
- process exp.body
68
+ process_all exp.body
69
69
  @inside_util = false
70
70
  end
71
71
 
@@ -78,7 +78,7 @@ class Brakeman::CheckSingleQuotes < Brakeman::BaseCheck
78
78
  def process_defn exp
79
79
  if @inside_util and exp.method_name == :html_escape
80
80
  @inside_html_escape = true
81
- process exp.body
81
+ process_all exp.body
82
82
  @inside_html_escape = false
83
83
  end
84
84
 
@@ -35,10 +35,11 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
35
35
  def skip_verify_except? filter
36
36
  return false unless call? filter
37
37
 
38
- args = filter.args
38
+ first_arg = filter.first_arg
39
+ last_arg = filter.last_arg
39
40
 
40
- if symbol? args.first and args.first.value == :verify_authenticity_token and hash? args.last
41
- if hash_access(args.last, :except)
41
+ if symbol? first_arg and first_arg.value == :verify_authenticity_token and hash? last_arg
42
+ if hash_access(last_arg, :except)
42
43
  return true
43
44
  end
44
45
  end
@@ -61,7 +61,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
61
61
  active_record_models.each do |name, model|
62
62
  if model[:options][:named_scope]
63
63
  model[:options][:named_scope].each do |args|
64
- call = Sexp.new(:call, nil, :named_scope, args).line(args.line)
64
+ call = make_call(nil, :named_scope, args).line(args.line)
65
65
  scope_calls << { :call => call, :location => [:class, name ], :method => :named_scope }
66
66
  end
67
67
  end
@@ -80,7 +80,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
80
80
  call = second_arg
81
81
  scope_calls << { :call => call, :location => [:class, name ], :method => call.method }
82
82
  else
83
- call = Sexp.new(:call, nil, :scope, args).line(args.line)
83
+ call = make_call(nil, :scope, args).line(args.line)
84
84
  scope_calls << { :call => call, :location => [:class, name ], :method => :scope }
85
85
  end
86
86
  end
@@ -173,37 +173,36 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
173
173
 
174
174
  call = result[:call]
175
175
  method = call.method
176
- args = call.args
177
176
 
178
177
  dangerous_value = case method
179
178
  when :find
180
- check_find_arguments args.second
179
+ check_find_arguments call.second_arg
181
180
  when :exists?
182
- check_find_arguments args.first
181
+ check_find_arguments call.first_arg
183
182
  when :named_scope, :scope
184
- check_scope_arguments call.arglist
183
+ check_scope_arguments call
185
184
  when :find_by_sql, :count_by_sql
186
- check_by_sql_arguments args.first
185
+ check_by_sql_arguments call.first_arg
187
186
  when :calculate
188
- check_find_arguments args[2]
187
+ check_find_arguments call.third_arg
189
188
  when :last, :first, :all
190
- check_find_arguments args.first
189
+ check_find_arguments call.first_arg
191
190
  when :average, :count, :maximum, :minimum, :sum
192
- if args.length > 2
193
- unsafe_sql?(args.first) or check_find_arguments(args.last)
191
+ if call.length > 5
192
+ unsafe_sql?(call.first_arg) or check_find_arguments(call.last_arg)
194
193
  else
195
- check_find_arguments args.last
194
+ check_find_arguments call.last_arg
196
195
  end
197
196
  when :where, :having
198
197
  check_query_arguments call.arglist
199
198
  when :order, :group, :reorder
200
199
  check_order_arguments call.arglist
201
200
  when :joins
202
- check_joins_arguments args.first
201
+ check_joins_arguments call.first_arg
203
202
  when :from, :select
204
- unsafe_sql? args.first
203
+ unsafe_sql? call.first_arg
205
204
  when :lock
206
- check_lock_arguments args.first
205
+ check_lock_arguments call.first_arg
207
206
  else
208
207
  Brakeman.debug "Unhandled SQL method: #{method}"
209
208
  end
@@ -226,8 +225,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
226
225
  :confidence => confidence
227
226
  end
228
227
 
229
- if check_for_limit_or_offset_vulnerability args[-1]
230
- if include_user_input? args[-1]
228
+ if check_for_limit_or_offset_vulnerability call.last_arg
229
+ if include_user_input? call.last_arg
231
230
  confidence = CONFIDENCE[:high]
232
231
  else
233
232
  confidence = CONFIDENCE[:low]
@@ -259,25 +258,26 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
259
258
  unsafe_sql? arg
260
259
  end
261
260
 
262
- def check_scope_arguments args
263
- return unless node_type? args, :arglist
261
+ def check_scope_arguments call
262
+ scope_arg = call.second_arg #first arg is name of scope
264
263
 
265
- if node_type? args[2], :iter
266
- unsafe_sql? args[2][-1]
264
+ if node_type? scope_arg, :iter
265
+ unsafe_sql? scope_arg.block
267
266
  else
268
- unsafe_sql? args[2]
267
+ unsafe_sql? scope_arg
269
268
  end
270
269
  end
271
270
 
272
271
  def check_query_arguments arg
273
272
  return unless sexp? arg
273
+ first_arg = arg[1]
274
274
 
275
275
  if node_type? arg, :arglist
276
- if arg.length > 2 and node_type? arg[1], :string_interp, :dstr
276
+ if arg.length > 2 and node_type? first_arg, :string_interp, :dstr
277
277
  # Model.where("blah = ?", blah)
278
- return check_string_interp arg[1]
278
+ return check_string_interp first_arg
279
279
  else
280
- arg = arg[1]
280
+ arg = first_arg
281
281
  end
282
282
  end
283
283
 
@@ -319,7 +319,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
319
319
  def check_by_sql_arguments arg
320
320
  return unless sexp? arg
321
321
 
322
- #This is kind of necessary, because unsafe_sql? will handle an array
322
+ #This is kind of unnecessary, because unsafe_sql? will handle an array
323
323
  #correctly, but might be better to be explicit.
324
324
  if array? arg
325
325
  unsafe_sql? arg[1]
@@ -457,7 +457,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
457
457
  def check_hash_values exp
458
458
  hash_iterate(exp) do |key, value|
459
459
  if symbol? key
460
- unsafe = case key[1]
460
+ unsafe = case key.value
461
461
  when :conditions, :having, :select
462
462
  check_query_arguments value
463
463
  when :order, :group
@@ -486,9 +486,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
486
486
 
487
487
  target = exp.target
488
488
  method = exp.method
489
- args = exp.args
490
489
 
491
- if string? target or string? args.first
490
+ if string? target or string? exp.first_arg
492
491
  if STRING_METHODS.include? method
493
492
  return exp
494
493
  end
@@ -500,7 +499,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
500
499
  IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :to_i, :to_f,
501
500
  :sanitize_sql, :sanitize_sql_array, :sanitize_sql_for_assignment,
502
501
  :sanitize_sql_for_conditions, :sanitize_sql_hash,
503
- :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions]
502
+ :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
503
+ :to_sql]
504
504
 
505
505
  def safe_value? exp
506
506
  return true unless sexp? exp