brakeman-lib 3.3.1

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 (159) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +872 -0
  3. data/FEATURES +16 -0
  4. data/README.md +169 -0
  5. data/WARNING_TYPES +95 -0
  6. data/bin/brakeman +89 -0
  7. data/lib/brakeman.rb +495 -0
  8. data/lib/brakeman/app_tree.rb +161 -0
  9. data/lib/brakeman/brakeman.rake +17 -0
  10. data/lib/brakeman/call_index.rb +219 -0
  11. data/lib/brakeman/checks.rb +191 -0
  12. data/lib/brakeman/checks/base_check.rb +518 -0
  13. data/lib/brakeman/checks/check_basic_auth.rb +88 -0
  14. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
  15. data/lib/brakeman/checks/check_content_tag.rb +160 -0
  16. data/lib/brakeman/checks/check_create_with.rb +75 -0
  17. data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
  18. data/lib/brakeman/checks/check_default_routes.rb +86 -0
  19. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  20. data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
  21. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  22. data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
  23. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  24. data/lib/brakeman/checks/check_evaluation.rb +36 -0
  25. data/lib/brakeman/checks/check_execute.rb +167 -0
  26. data/lib/brakeman/checks/check_file_access.rb +63 -0
  27. data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
  28. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  29. data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
  30. data/lib/brakeman/checks/check_header_dos.rb +31 -0
  31. data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
  32. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  33. data/lib/brakeman/checks/check_json_encoding.rb +47 -0
  34. data/lib/brakeman/checks/check_json_parsing.rb +107 -0
  35. data/lib/brakeman/checks/check_link_to.rb +132 -0
  36. data/lib/brakeman/checks/check_link_to_href.rb +115 -0
  37. data/lib/brakeman/checks/check_mail_to.rb +49 -0
  38. data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
  39. data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
  40. data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
  41. data/lib/brakeman/checks/check_model_attributes.rb +119 -0
  42. data/lib/brakeman/checks/check_model_serialize.rb +67 -0
  43. data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
  44. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
  45. data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
  46. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  47. data/lib/brakeman/checks/check_redirect.rb +215 -0
  48. data/lib/brakeman/checks/check_regex_dos.rb +69 -0
  49. data/lib/brakeman/checks/check_render.rb +92 -0
  50. data/lib/brakeman/checks/check_render_dos.rb +37 -0
  51. data/lib/brakeman/checks/check_render_inline.rb +54 -0
  52. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  53. data/lib/brakeman/checks/check_route_dos.rb +42 -0
  54. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  55. data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
  56. data/lib/brakeman/checks/check_secrets.rb +40 -0
  57. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  58. data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
  59. data/lib/brakeman/checks/check_send.rb +48 -0
  60. data/lib/brakeman/checks/check_send_file.rb +19 -0
  61. data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
  62. data/lib/brakeman/checks/check_session_settings.rb +170 -0
  63. data/lib/brakeman/checks/check_simple_format.rb +59 -0
  64. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  65. data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
  66. data/lib/brakeman/checks/check_sql.rb +660 -0
  67. data/lib/brakeman/checks/check_sql_cves.rb +101 -0
  68. data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
  69. data/lib/brakeman/checks/check_strip_tags.rb +89 -0
  70. data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
  71. data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
  72. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  73. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  74. data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
  75. data/lib/brakeman/checks/check_validation_regex.rb +116 -0
  76. data/lib/brakeman/checks/check_weak_hash.rb +151 -0
  77. data/lib/brakeman/checks/check_without_protection.rb +80 -0
  78. data/lib/brakeman/checks/check_xml_dos.rb +51 -0
  79. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  80. data/lib/brakeman/differ.rb +66 -0
  81. data/lib/brakeman/file_parser.rb +50 -0
  82. data/lib/brakeman/format/style.css +133 -0
  83. data/lib/brakeman/options.rb +301 -0
  84. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  85. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  86. data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
  87. data/lib/brakeman/parsers/template_parser.rb +89 -0
  88. data/lib/brakeman/processor.rb +102 -0
  89. data/lib/brakeman/processors/alias_processor.rb +1013 -0
  90. data/lib/brakeman/processors/base_processor.rb +277 -0
  91. data/lib/brakeman/processors/config_processor.rb +14 -0
  92. data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
  93. data/lib/brakeman/processors/controller_processor.rb +326 -0
  94. data/lib/brakeman/processors/erb_template_processor.rb +80 -0
  95. data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
  96. data/lib/brakeman/processors/gem_processor.rb +57 -0
  97. data/lib/brakeman/processors/haml_template_processor.rb +190 -0
  98. data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
  99. data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
  100. data/lib/brakeman/processors/lib/find_call.rb +183 -0
  101. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  102. data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
  103. data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
  104. data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
  105. data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
  106. data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
  107. data/lib/brakeman/processors/lib/render_helper.rb +181 -0
  108. data/lib/brakeman/processors/lib/render_path.rb +107 -0
  109. data/lib/brakeman/processors/lib/route_helper.rb +68 -0
  110. data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
  111. data/lib/brakeman/processors/library_processor.rb +119 -0
  112. data/lib/brakeman/processors/model_processor.rb +191 -0
  113. data/lib/brakeman/processors/output_processor.rb +171 -0
  114. data/lib/brakeman/processors/route_processor.rb +17 -0
  115. data/lib/brakeman/processors/slim_template_processor.rb +107 -0
  116. data/lib/brakeman/processors/template_alias_processor.rb +116 -0
  117. data/lib/brakeman/processors/template_processor.rb +74 -0
  118. data/lib/brakeman/report.rb +78 -0
  119. data/lib/brakeman/report/config/remediation.yml +71 -0
  120. data/lib/brakeman/report/ignore/config.rb +135 -0
  121. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  122. data/lib/brakeman/report/renderer.rb +24 -0
  123. data/lib/brakeman/report/report_base.rb +286 -0
  124. data/lib/brakeman/report/report_codeclimate.rb +70 -0
  125. data/lib/brakeman/report/report_csv.rb +55 -0
  126. data/lib/brakeman/report/report_hash.rb +23 -0
  127. data/lib/brakeman/report/report_html.rb +216 -0
  128. data/lib/brakeman/report/report_json.rb +42 -0
  129. data/lib/brakeman/report/report_markdown.rb +156 -0
  130. data/lib/brakeman/report/report_table.rb +107 -0
  131. data/lib/brakeman/report/report_tabs.rb +17 -0
  132. data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
  133. data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
  134. data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
  135. data/lib/brakeman/report/templates/header.html.erb +58 -0
  136. data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
  137. data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
  138. data/lib/brakeman/report/templates/overview.html.erb +38 -0
  139. data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
  140. data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
  141. data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
  142. data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
  143. data/lib/brakeman/rescanner.rb +483 -0
  144. data/lib/brakeman/scanner.rb +317 -0
  145. data/lib/brakeman/tracker.rb +347 -0
  146. data/lib/brakeman/tracker/collection.rb +93 -0
  147. data/lib/brakeman/tracker/config.rb +101 -0
  148. data/lib/brakeman/tracker/constants.rb +101 -0
  149. data/lib/brakeman/tracker/controller.rb +161 -0
  150. data/lib/brakeman/tracker/library.rb +17 -0
  151. data/lib/brakeman/tracker/model.rb +90 -0
  152. data/lib/brakeman/tracker/template.rb +33 -0
  153. data/lib/brakeman/util.rb +481 -0
  154. data/lib/brakeman/version.rb +3 -0
  155. data/lib/brakeman/warning.rb +255 -0
  156. data/lib/brakeman/warning_codes.rb +111 -0
  157. data/lib/ruby_parser/bm_sexp.rb +610 -0
  158. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  159. metadata +362 -0
@@ -0,0 +1,518 @@
1
+ require 'brakeman/processors/output_processor'
2
+ require 'brakeman/processors/lib/processor_helper'
3
+ require 'brakeman/warning'
4
+ require 'brakeman/util'
5
+
6
+ #Basis of vulnerability checks.
7
+ class Brakeman::BaseCheck < Brakeman::SexpProcessor
8
+ include Brakeman::ProcessorHelper
9
+ include Brakeman::Util
10
+ attr_reader :tracker, :warnings
11
+
12
+ CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
13
+
14
+ Match = Struct.new(:type, :match)
15
+
16
+ class << self
17
+ attr_accessor :name
18
+
19
+ def inherited(subclass)
20
+ subclass.name = subclass.to_s.match(/^Brakeman::(.*)$/)[1]
21
+ end
22
+ end
23
+
24
+ #Initialize Check with Checks.
25
+ def initialize(app_tree, tracker)
26
+ super()
27
+ @app_tree = app_tree
28
+ @results = [] #only to check for duplicates
29
+ @warnings = []
30
+ @tracker = tracker
31
+ @string_interp = false
32
+ @current_set = nil
33
+ @current_template = @current_module = @current_class = @current_method = nil
34
+ @active_record_models = nil
35
+ @mass_assign_disabled = nil
36
+ @has_user_input = nil
37
+ @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
38
+ @comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
39
+ end
40
+
41
+ #Add result to result list, which is used to check for duplicates
42
+ def add_result result, location = nil
43
+ location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
44
+ location = location[:name] if location.is_a? Hash
45
+ location = location.name if location.is_a? Brakeman::Collection
46
+ location = location.to_sym
47
+
48
+ if result.is_a? Hash
49
+ line = result[:call].original_line || result[:call].line
50
+ elsif sexp? result
51
+ line = result.original_line || result.line
52
+ else
53
+ raise ArgumentError
54
+ end
55
+
56
+ @results << [line, location, result]
57
+ end
58
+
59
+ #Default Sexp processing. Iterates over each value in the Sexp
60
+ #and processes them if they are also Sexps.
61
+ def process_default exp
62
+ exp.each_with_index do |e, i|
63
+ if sexp? e
64
+ process e
65
+ else
66
+ e
67
+ end
68
+ end
69
+
70
+ exp
71
+ end
72
+
73
+ #Process calls and check if they include user input
74
+ def process_call exp
75
+ unless @comparison_ops.include? exp.method
76
+ process exp.target if sexp? exp.target
77
+ process_call_args exp
78
+ end
79
+
80
+ target = exp.target
81
+
82
+ unless always_safe_method? exp.method
83
+ if params? target
84
+ @has_user_input = Match.new(:params, exp)
85
+ elsif cookies? target
86
+ @has_user_input = Match.new(:cookies, exp)
87
+ elsif request_env? target
88
+ @has_user_input = Match.new(:request, exp)
89
+ elsif sexp? target and model_name? target[1] #TODO: Can this be target.target?
90
+ @has_user_input = Match.new(:model, exp)
91
+ end
92
+ end
93
+
94
+ exp
95
+ end
96
+
97
+ def process_if exp
98
+ #This is to ignore user input in condition
99
+ current_user_input = @has_user_input
100
+ process exp.condition
101
+ @has_user_input = current_user_input
102
+
103
+ process exp.then_clause if sexp? exp.then_clause
104
+ process exp.else_clause if sexp? exp.else_clause
105
+
106
+ exp
107
+ end
108
+
109
+ #Note that params are included in current expression
110
+ def process_params exp
111
+ @has_user_input = Match.new(:params, exp)
112
+ exp
113
+ end
114
+
115
+ #Note that cookies are included in current expression
116
+ def process_cookies exp
117
+ @has_user_input = Match.new(:cookies, exp)
118
+ exp
119
+ end
120
+
121
+ #Does not actually process string interpolation, but notes that it occurred.
122
+ def process_dstr exp
123
+ @string_interp = Match.new(:interp, exp)
124
+ process_default exp
125
+ end
126
+
127
+ private
128
+
129
+ def always_safe_method? meth
130
+ @safe_input_attributes.include? meth or
131
+ @comparison_ops.include? meth
132
+ end
133
+
134
+ #Report a warning
135
+ def warn options
136
+ extra_opts = { :check => self.class.to_s }
137
+
138
+ warning = Brakeman::Warning.new(options.merge(extra_opts))
139
+ warning.file = file_for warning
140
+ warning.relative_path = relative_path(warning.file)
141
+
142
+ @warnings << warning
143
+ end
144
+
145
+ #Run _exp_ through OutputProcessor to get a nice String.
146
+ def format_output exp
147
+ Brakeman::OutputProcessor.new.format(exp).gsub(/\r|\n/, "")
148
+ end
149
+
150
+ #Checks if mass assignment is disabled globally in an initializer.
151
+ def mass_assign_disabled?
152
+ return @mass_assign_disabled unless @mass_assign_disabled.nil?
153
+
154
+ @mass_assign_disabled = false
155
+
156
+ if version_between?("3.1.0", "3.9.9") and
157
+ tracker.config.whitelist_attributes?
158
+
159
+ @mass_assign_disabled = true
160
+ elsif tracker.options[:rails4] && (!tracker.config.has_gem?(:protected_attributes) || tracker.config.whitelist_attributes?)
161
+
162
+ @mass_assign_disabled = true
163
+ else
164
+ #Check for ActiveRecord::Base.send(:attr_accessible, nil)
165
+ tracker.check_initializers(:"ActiveRecord::Base", :attr_accessible).each do |result|
166
+ call = result.call
167
+ if call? call
168
+ if call.first_arg == Sexp.new(:nil)
169
+ @mass_assign_disabled = true
170
+ break
171
+ end
172
+ end
173
+ end
174
+
175
+ unless @mass_assign_disabled
176
+ tracker.check_initializers(:"ActiveRecord::Base", :send).each do |result|
177
+ call = result.call
178
+ if call? call
179
+ if call.first_arg == Sexp.new(:lit, :attr_accessible) and call.second_arg == Sexp.new(:nil)
180
+ @mass_assign_disabled = true
181
+ break
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ unless @mass_assign_disabled
188
+ #Check for
189
+ # class ActiveRecord::Base
190
+ # attr_accessible nil
191
+ # end
192
+ matches = tracker.check_initializers([], :attr_accessible)
193
+
194
+ matches.each do |result|
195
+ if result.module == "ActiveRecord" and result.result_class == :Base
196
+ arg = result.call.first_arg
197
+
198
+ if arg.nil? or node_type? arg, :nil
199
+ @mass_assign_disabled = true
200
+ break
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ #There is a chance someone is using Rails 3.x and the `strong_parameters`
208
+ #gem and still using hack above, so this is a separate check for
209
+ #including ActiveModel::ForbiddenAttributesProtection in
210
+ #ActiveRecord::Base in an initializer.
211
+ if not @mass_assign_disabled and version_between?("3.1.0", "3.9.9") and tracker.config.has_gem? :strong_parameters
212
+ matches = tracker.check_initializers([], :include)
213
+ forbidden_protection = Sexp.new(:colon2, Sexp.new(:const, :ActiveModel), :ForbiddenAttributesProtection)
214
+
215
+ matches.each do |result|
216
+ if call? result.call and result.call.first_arg == forbidden_protection
217
+ @mass_assign_disabled = true
218
+ end
219
+ end
220
+
221
+ unless @mass_assign_disabled
222
+ matches = tracker.check_initializers(:"ActiveRecord::Base", [:send, :include])
223
+
224
+ matches.each do |result|
225
+ call = result.call
226
+ if call? call and (call.first_arg == forbidden_protection or call.second_arg == forbidden_protection)
227
+ @mass_assign_disabled = true
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ @mass_assign_disabled
234
+ end
235
+
236
+ #This is to avoid reporting duplicates. Checks if the result has been
237
+ #reported already from the same line number.
238
+ def duplicate? result, location = nil
239
+ if result.is_a? Hash
240
+ line = result[:call].original_line || result[:call].line
241
+ elsif sexp? result
242
+ line = result.original_line || result.line
243
+ else
244
+ raise ArgumentError
245
+ end
246
+
247
+ location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
248
+
249
+ location = location[:name] if location.is_a? Hash
250
+ location = location.name if location.is_a? Brakeman::Collection
251
+ location = location.to_sym
252
+
253
+ @results.each do |r|
254
+ if r[0] == line and r[1] == location
255
+ if tracker.options[:combine_locations]
256
+ return true
257
+ elsif r[2] == result
258
+ return true
259
+ end
260
+ end
261
+ end
262
+
263
+ false
264
+ end
265
+
266
+ #Checks if an expression contains string interpolation.
267
+ #
268
+ #Returns Match with :interp type if found.
269
+ def include_interp? exp
270
+ @string_interp = false
271
+ process exp
272
+ @string_interp
273
+ end
274
+
275
+ #Checks if _exp_ includes user input in the form of cookies, parameters,
276
+ #request environment, or model attributes.
277
+ #
278
+ #If found, returns a struct containing a type (:cookies, :params, :request, :model) and
279
+ #the matching expression (Match#type and Match#match).
280
+ #
281
+ #Returns false otherwise.
282
+ def include_user_input? exp
283
+ @has_user_input = false
284
+ process exp
285
+ @has_user_input
286
+ end
287
+
288
+ #This is used to check for user input being used directly.
289
+ #
290
+ ##If found, returns a struct containing a type (:cookies, :params, :request) and
291
+ #the matching expression (Match#type and Match#match).
292
+ #
293
+ #Returns false otherwise.
294
+ def has_immediate_user_input? exp
295
+ if exp.nil?
296
+ false
297
+ elsif call? exp and not always_safe_method? exp.method
298
+ if params? exp
299
+ return Match.new(:params, exp)
300
+ elsif cookies? exp
301
+ return Match.new(:cookies, exp)
302
+ elsif request_env? exp
303
+ return Match.new(:request, exp)
304
+ else
305
+ has_immediate_user_input? exp.target
306
+ end
307
+ elsif sexp? exp
308
+ case exp.node_type
309
+ when :dstr
310
+ exp.each do |e|
311
+ if sexp? e
312
+ match = has_immediate_user_input?(e)
313
+ return match if match
314
+ end
315
+ end
316
+ false
317
+ when :evstr
318
+ if sexp? exp.value
319
+ if exp.value.node_type == :rlist
320
+ exp.value.each_sexp do |e|
321
+ match = has_immediate_user_input?(e)
322
+ return match if match
323
+ end
324
+ false
325
+ else
326
+ has_immediate_user_input? exp.value
327
+ end
328
+ end
329
+ when :format
330
+ has_immediate_user_input? exp.value
331
+ when :if
332
+ (sexp? exp.then_clause and has_immediate_user_input? exp.then_clause) or
333
+ (sexp? exp.else_clause and has_immediate_user_input? exp.else_clause)
334
+ when :or
335
+ has_immediate_user_input? exp.lhs or
336
+ has_immediate_user_input? exp.rhs
337
+ else
338
+ false
339
+ end
340
+ end
341
+ end
342
+
343
+ #Checks for a model attribute at the top level of the
344
+ #expression.
345
+ def has_immediate_model? exp, out = nil
346
+ out = exp if out.nil?
347
+
348
+ if sexp? exp and exp.node_type == :output
349
+ exp = exp.value
350
+ end
351
+
352
+ if call? exp
353
+ target = exp.target
354
+ method = exp.method
355
+
356
+ if always_safe_method? method
357
+ false
358
+ elsif call? target and not method.to_s[-1,1] == "?"
359
+ if has_immediate_model?(target, out)
360
+ exp
361
+ else
362
+ false
363
+ end
364
+ elsif model_name? target
365
+ exp
366
+ else
367
+ false
368
+ end
369
+ elsif sexp? exp
370
+ case exp.node_type
371
+ when :dstr
372
+ exp.each do |e|
373
+ if sexp? e and match = has_immediate_model?(e, out)
374
+ return match
375
+ end
376
+ end
377
+ false
378
+ when :evstr
379
+ if sexp? exp.value
380
+ if exp.value.node_type == :rlist
381
+ exp.value.each_sexp do |e|
382
+ if match = has_immediate_model?(e, out)
383
+ return match
384
+ end
385
+ end
386
+ false
387
+ else
388
+ has_immediate_model? exp.value, out
389
+ end
390
+ end
391
+ when :format
392
+ has_immediate_model? exp.value, out
393
+ when :if
394
+ ((sexp? exp.then_clause and has_immediate_model? exp.then_clause, out) or
395
+ (sexp? exp.else_clause and has_immediate_model? exp.else_clause, out))
396
+ when :or
397
+ has_immediate_model? exp.lhs or
398
+ has_immediate_model? exp.rhs
399
+ else
400
+ false
401
+ end
402
+ end
403
+ end
404
+
405
+ #Checks if +exp+ is a model name.
406
+ #
407
+ #Prior to using this method, either @tracker must be set to
408
+ #the current tracker, or else @models should contain an array of the model
409
+ #names, which is available via tracker.models.keys
410
+ def model_name? exp
411
+ @models ||= @tracker.models.keys
412
+
413
+ if exp.is_a? Symbol
414
+ @models.include? exp
415
+ elsif call? exp and exp.target.nil? and exp.method == :current_user
416
+ true
417
+ elsif sexp? exp
418
+ @models.include? class_name(exp)
419
+ else
420
+ false
421
+ end
422
+ end
423
+
424
+ #Returns true if +target+ is in +exp+
425
+ def include_target? exp, target
426
+ return false unless call? exp
427
+
428
+ exp.each do |e|
429
+ return true if e == target or include_target? e, target
430
+ end
431
+
432
+ false
433
+ end
434
+
435
+ #Returns true if low_version <= RAILS_VERSION <= high_version
436
+ #
437
+ #If the Rails version is unknown, returns false.
438
+ def version_between? low_version, high_version, current_version = nil
439
+ current_version ||= rails_version
440
+ return false unless current_version
441
+
442
+ version = current_version.split(".").map! { |n| n.to_i }
443
+ low_version = low_version.split(".").map! { |n| n.to_i }
444
+ high_version = high_version.split(".").map! { |n| n.to_i }
445
+
446
+ version.each_with_index do |v, i|
447
+ if v < low_version.fetch(i, 0)
448
+ return false
449
+ elsif v > low_version.fetch(i, 0)
450
+ break
451
+ end
452
+ end
453
+
454
+ version.each_with_index do |v, i|
455
+ if v > high_version.fetch(i, 0)
456
+ return false
457
+ elsif v < high_version.fetch(i, 0)
458
+ break
459
+ end
460
+ end
461
+
462
+ true
463
+ end
464
+
465
+ def lts_version? version
466
+ tracker.config.has_gem? :'railslts-version' and
467
+ version_between? version, "2.3.18.99", tracker.config.gem_version(:'railslts-version')
468
+ end
469
+
470
+ def gemfile_or_environment gem_name = :rails
471
+ if gem_name and info = tracker.config.get_gem(gem_name)
472
+ info
473
+ elsif @app_tree.exists?("Gemfile")
474
+ "Gemfile"
475
+ elsif @app_tree.exists?("gems.rb")
476
+ "gems.rb"
477
+ else
478
+ "config/environment.rb"
479
+ end
480
+ end
481
+
482
+ def self.description
483
+ @description
484
+ end
485
+
486
+ def active_record_models
487
+ return @active_record_models if @active_record_models
488
+
489
+ @active_record_models = {}
490
+
491
+ tracker.models.each do |name, model|
492
+ if model.ancestor? :"ActiveRecord::Base"
493
+ @active_record_models[name] = model
494
+ end
495
+ end
496
+
497
+ @active_record_models
498
+ end
499
+
500
+ def friendly_type_of input_type
501
+ if input_type.is_a? Match
502
+ input_type = input_type.type
503
+ end
504
+
505
+ case input_type
506
+ when :params
507
+ "parameter value"
508
+ when :cookies
509
+ "cookie value"
510
+ when :request
511
+ "request value"
512
+ when :model
513
+ "model attribute"
514
+ else
515
+ "user input"
516
+ end
517
+ end
518
+ end