brakeman-min 0.5.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGES +529 -0
  2. data/README.md +74 -28
  3. data/bin/brakeman +60 -266
  4. data/lib/brakeman.rb +422 -0
  5. data/lib/brakeman/app_tree.rb +101 -0
  6. data/lib/brakeman/brakeman.rake +10 -0
  7. data/lib/brakeman/call_index.rb +215 -0
  8. data/lib/brakeman/checks.rb +180 -0
  9. data/lib/brakeman/checks/base_check.rb +538 -0
  10. data/lib/brakeman/checks/check_basic_auth.rb +89 -0
  11. data/lib/brakeman/checks/check_content_tag.rb +162 -0
  12. data/lib/brakeman/checks/check_cross_site_scripting.rb +334 -0
  13. data/lib/{checks → brakeman/checks}/check_default_routes.rb +13 -6
  14. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  15. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  16. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  17. data/lib/brakeman/checks/check_evaluation.rb +33 -0
  18. data/lib/brakeman/checks/check_execute.rb +98 -0
  19. data/lib/brakeman/checks/check_file_access.rb +62 -0
  20. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  21. data/lib/brakeman/checks/check_forgery_setting.rb +54 -0
  22. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  23. data/lib/brakeman/checks/check_json_parsing.rb +102 -0
  24. data/lib/brakeman/checks/check_link_to.rb +132 -0
  25. data/lib/brakeman/checks/check_link_to_href.rb +92 -0
  26. data/lib/{checks → brakeman/checks}/check_mail_to.rb +14 -13
  27. data/lib/brakeman/checks/check_mass_assignment.rb +143 -0
  28. data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
  29. data/lib/brakeman/checks/check_model_attributes.rb +118 -0
  30. data/lib/brakeman/checks/check_model_serialize.rb +66 -0
  31. data/lib/{checks → brakeman/checks}/check_nested_attributes.rb +10 -6
  32. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  33. data/lib/brakeman/checks/check_redirect.rb +177 -0
  34. data/lib/brakeman/checks/check_render.rb +62 -0
  35. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  36. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  37. data/lib/brakeman/checks/check_sanitize_methods.rb +54 -0
  38. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  39. data/lib/brakeman/checks/check_select_vulnerability.rb +58 -0
  40. data/lib/brakeman/checks/check_send.rb +35 -0
  41. data/lib/brakeman/checks/check_send_file.rb +19 -0
  42. data/lib/brakeman/checks/check_session_settings.rb +145 -0
  43. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  44. data/lib/brakeman/checks/check_skip_before_filter.rb +62 -0
  45. data/lib/brakeman/checks/check_sql.rb +577 -0
  46. data/lib/brakeman/checks/check_strip_tags.rb +64 -0
  47. data/lib/brakeman/checks/check_symbol_dos.rb +67 -0
  48. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  49. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  50. data/lib/brakeman/checks/check_validation_regex.rb +88 -0
  51. data/lib/brakeman/checks/check_without_protection.rb +64 -0
  52. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  53. data/lib/brakeman/differ.rb +66 -0
  54. data/lib/{format → brakeman/format}/style.css +28 -0
  55. data/lib/brakeman/options.rb +256 -0
  56. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  57. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  58. data/lib/{scanner_erubis.rb → brakeman/parsers/rails3_erubis.rb} +8 -21
  59. data/lib/brakeman/processor.rb +102 -0
  60. data/lib/brakeman/processors/alias_processor.rb +780 -0
  61. data/lib/{processors → brakeman/processors}/base_processor.rb +90 -74
  62. data/lib/brakeman/processors/config_processor.rb +14 -0
  63. data/lib/brakeman/processors/controller_alias_processor.rb +334 -0
  64. data/lib/brakeman/processors/controller_processor.rb +265 -0
  65. data/lib/{processors → brakeman/processors}/erb_template_processor.rb +21 -19
  66. data/lib/brakeman/processors/erubis_template_processor.rb +96 -0
  67. data/lib/brakeman/processors/gem_processor.rb +59 -0
  68. data/lib/{processors → brakeman/processors}/haml_template_processor.rb +26 -21
  69. data/lib/brakeman/processors/lib/find_all_calls.rb +185 -0
  70. data/lib/{processors → brakeman/processors}/lib/find_call.rb +23 -28
  71. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  72. data/lib/brakeman/processors/lib/processor_helper.rb +82 -0
  73. data/lib/{processors/config_processor.rb → brakeman/processors/lib/rails2_config_processor.rb} +32 -35
  74. data/lib/{processors → brakeman/processors}/lib/rails2_route_processor.rb +60 -52
  75. data/lib/brakeman/processors/lib/rails3_config_processor.rb +129 -0
  76. data/lib/brakeman/processors/lib/rails3_route_processor.rb +282 -0
  77. data/lib/{processors → brakeman/processors}/lib/render_helper.rb +54 -20
  78. data/lib/brakeman/processors/lib/route_helper.rb +62 -0
  79. data/lib/{processors → brakeman/processors}/library_processor.rb +24 -17
  80. data/lib/{processors → brakeman/processors}/model_processor.rb +46 -22
  81. data/lib/{processors → brakeman/processors}/output_processor.rb +34 -40
  82. data/lib/brakeman/processors/route_processor.rb +17 -0
  83. data/lib/brakeman/processors/slim_template_processor.rb +113 -0
  84. data/lib/brakeman/processors/template_alias_processor.rb +120 -0
  85. data/lib/{processors → brakeman/processors}/template_processor.rb +10 -7
  86. data/lib/brakeman/report.rb +68 -0
  87. data/lib/brakeman/report/ignore/config.rb +130 -0
  88. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  89. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  90. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  91. data/lib/brakeman/report/renderer.rb +24 -0
  92. data/lib/brakeman/report/report_base.rb +279 -0
  93. data/lib/brakeman/report/report_csv.rb +56 -0
  94. data/lib/brakeman/report/report_hash.rb +22 -0
  95. data/lib/brakeman/report/report_html.rb +203 -0
  96. data/lib/brakeman/report/report_json.rb +46 -0
  97. data/lib/brakeman/report/report_table.rb +109 -0
  98. data/lib/brakeman/report/report_tabs.rb +17 -0
  99. data/lib/brakeman/report/templates/controller_overview.html.erb +18 -0
  100. data/lib/brakeman/report/templates/controller_warnings.html.erb +17 -0
  101. data/lib/brakeman/report/templates/error_overview.html.erb +25 -0
  102. data/lib/brakeman/report/templates/header.html.erb +44 -0
  103. data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
  104. data/lib/brakeman/report/templates/model_warnings.html.erb +17 -0
  105. data/lib/brakeman/report/templates/overview.html.erb +34 -0
  106. data/lib/brakeman/report/templates/security_warnings.html.erb +19 -0
  107. data/lib/brakeman/report/templates/template_overview.html.erb +17 -0
  108. data/lib/brakeman/report/templates/view_warnings.html.erb +30 -0
  109. data/lib/brakeman/report/templates/warning_overview.html.erb +13 -0
  110. data/lib/brakeman/rescanner.rb +446 -0
  111. data/lib/brakeman/scanner.rb +362 -0
  112. data/lib/brakeman/tracker.rb +296 -0
  113. data/lib/brakeman/util.rb +413 -0
  114. data/lib/brakeman/version.rb +3 -0
  115. data/lib/brakeman/warning.rb +217 -0
  116. data/lib/brakeman/warning_codes.rb +68 -0
  117. data/lib/ruby_parser/bm_sexp.rb +562 -0
  118. data/lib/ruby_parser/bm_sexp_processor.rb +230 -0
  119. metadata +152 -66
  120. data/lib/checks.rb +0 -71
  121. data/lib/checks/base_check.rb +0 -357
  122. data/lib/checks/check_cross_site_scripting.rb +0 -336
  123. data/lib/checks/check_evaluation.rb +0 -27
  124. data/lib/checks/check_execute.rb +0 -110
  125. data/lib/checks/check_file_access.rb +0 -46
  126. data/lib/checks/check_forgery_setting.rb +0 -42
  127. data/lib/checks/check_mass_assignment.rb +0 -74
  128. data/lib/checks/check_model_attributes.rb +0 -36
  129. data/lib/checks/check_redirect.rb +0 -98
  130. data/lib/checks/check_render.rb +0 -65
  131. data/lib/checks/check_send_file.rb +0 -15
  132. data/lib/checks/check_session_settings.rb +0 -79
  133. data/lib/checks/check_sql.rb +0 -146
  134. data/lib/checks/check_validation_regex.rb +0 -60
  135. data/lib/processor.rb +0 -86
  136. data/lib/processors/alias_processor.rb +0 -384
  137. data/lib/processors/controller_alias_processor.rb +0 -237
  138. data/lib/processors/controller_processor.rb +0 -202
  139. data/lib/processors/erubis_template_processor.rb +0 -85
  140. data/lib/processors/lib/find_model_call.rb +0 -39
  141. data/lib/processors/lib/processor_helper.rb +0 -36
  142. data/lib/processors/lib/rails3_route_processor.rb +0 -184
  143. data/lib/processors/lib/route_helper.rb +0 -34
  144. data/lib/processors/params_processor.rb +0 -77
  145. data/lib/processors/route_processor.rb +0 -11
  146. data/lib/processors/template_alias_processor.rb +0 -86
  147. data/lib/report.rb +0 -680
  148. data/lib/scanner.rb +0 -227
  149. data/lib/tracker.rb +0 -144
  150. data/lib/util.rb +0 -141
  151. data/lib/version.rb +0 -1
  152. data/lib/warning.rb +0 -99
@@ -1,71 +0,0 @@
1
- #Collects up results from running different checks.
2
- #
3
- #Checks can be added with +Check.add(check_class)+
4
- #
5
- #All .rb files in checks/ will be loaded.
6
- class Checks
7
- @checks = []
8
-
9
- attr_reader :warnings, :controller_warnings, :model_warnings, :template_warnings, :checks_run
10
-
11
- #Add a check. This will call +_klass_.new+ when running tests
12
- def self.add klass
13
- @checks << klass
14
- end
15
-
16
- def self.checks
17
- @checks
18
- end
19
-
20
- #No need to use this directly.
21
- def initialize
22
- @warnings = []
23
- @template_warnings = []
24
- @model_warnings = []
25
- @controller_warnings = []
26
- @checks_run = []
27
- end
28
-
29
- #Add Warning to list of warnings to report.
30
- #Warnings are split into four different arrays
31
- #for template, controller, model, and generic warnings.
32
- def add_warning warning
33
- case warning.warning_set
34
- when :template
35
- @template_warnings << warning
36
- when :warning
37
- @warnings << warning
38
- when :controller
39
- @controller_warnings << warning
40
- when :model
41
- @model_warnings << warning
42
- else
43
- raise "Unknown warning: #{warning.warning_set}"
44
- end
45
- end
46
-
47
- #Run all the checks on the given Tracker.
48
- #Returns a new instance of Checks with the results.
49
- def self.run_checks tracker
50
- checks = self.new
51
- @checks.each do |c|
52
- #Run or don't run check based on options
53
- unless OPTIONS[:skip_checks].include? c.to_s or
54
- (OPTIONS[:run_checks] and not OPTIONS[:run_checks].include? c.to_s)
55
-
56
- warn " - #{c}"
57
- c.new(checks, tracker).run_check
58
-
59
- #Maintain list of which checks were run
60
- #mainly for reporting purposes
61
- checks.checks_run << c.to_s[5..-1]
62
- end
63
- end
64
- checks
65
- end
66
- end
67
-
68
- #Load all files in checks/ directory
69
- Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/checks/*.rb").sort.each do |f|
70
- require f.match(/(checks\/.*)\.rb$/)[0]
71
- end
@@ -1,357 +0,0 @@
1
- require 'rubygems'
2
- require 'sexp_processor'
3
- require 'processors/output_processor'
4
- require 'warning'
5
- require 'util'
6
-
7
- #Basis of vulnerability checks.
8
- class BaseCheck < SexpProcessor
9
- include ProcessorHelper
10
- include Util
11
- attr_reader :checks, :tracker
12
-
13
- CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
14
-
15
- #Initialize Check with Checks.
16
- def initialize checks, tracker
17
- super()
18
- @results = [] #only to check for duplicates
19
- @checks = checks
20
- @tracker = tracker
21
- @string_interp = false
22
- @current_template = @current_module = @current_class = @current_method = nil
23
- self.strict = false
24
- self.auto_shift_type = false
25
- self.require_empty = false
26
- self.default_method = :process_default
27
- self.warn_on_default = false
28
- end
29
-
30
- #Add result to result list, which is used to check for duplicates
31
- def add_result result, location = nil
32
- location ||= (@current_template && @current_template[:name]) || @current_class || @current_module || @current_set || result[1]
33
- location = location[:name] if location.is_a? Hash
34
- location = location.to_sym
35
-
36
- @results << [result.line, location, result]
37
- end
38
-
39
- #Default Sexp processing. Iterates over each value in the Sexp
40
- #and processes them if they are also Sexps.
41
- def process_default exp
42
- type = exp.shift
43
- exp.each_with_index do |e, i|
44
- if sexp? e
45
- process e
46
- else
47
- e
48
- end
49
- end
50
-
51
- exp.unshift type
52
- end
53
-
54
- #Process calls and check if they include user input
55
- def process_call exp
56
- process exp[1] if sexp? exp[1]
57
- process exp[3]
58
-
59
- if ALL_PARAMETERS.include? exp[1] or ALL_PARAMETERS.include? exp or params? exp[1]
60
- @has_user_input = :params
61
- elsif exp[1] == COOKIES or exp == COOKIES or cookies? exp[1]
62
- @has_user_input = :cookies
63
- elsif sexp? exp[1] and model_name? exp[1][1]
64
- @has_user_input = :model
65
- end
66
-
67
- exp
68
- end
69
-
70
- #Note that params are included in current expression
71
- def process_params exp
72
- @has_user_input = :params
73
- exp
74
- end
75
-
76
- #Note that cookies are included in current expression
77
- def process_cookies exp
78
- @has_user_input = :cookies
79
- exp
80
- end
81
-
82
- private
83
-
84
- #Report a warning
85
- def warn options
86
- @checks.add_warning Warning.new(options.merge({ :check => self.class.to_s }))
87
- end
88
-
89
- #Run _exp_ through OutputProcessor to get a nice String.
90
- def format_output exp
91
- OutputProcessor.new.format(exp).gsub(/\r|\n/, "")
92
- end
93
-
94
- #Checks if the model inherits from parent,
95
- def parent? tracker, model, parent
96
- if model == nil
97
- false
98
- elsif model[:parent] == parent
99
- true
100
- elsif model[:parent]
101
- parent? tracker, tracker.models[model[:parent]], parent
102
- else
103
- false
104
- end
105
- end
106
-
107
- #Checks if mass assignment is disabled globally in an initializer.
108
- def mass_assign_disabled? tracker
109
- matches = tracker.check_initializers(:"ActiveRecord::Base", :send)
110
- if matches.empty?
111
- false
112
- else
113
- matches.each do |result|
114
- if result[3][3] == Sexp.new(:arg_list, Sexp.new(:lit, :attr_accessible), Sexp.new(:nil))
115
- return true
116
- end
117
- end
118
- end
119
- end
120
-
121
- #This is to avoid reporting duplicates. Checks if the result has been
122
- #reported already from the same line number.
123
- def duplicate? result, location = nil
124
- line = result.line
125
- location ||= (@current_template && @current_template[:name]) || @current_class || @current_module || @current_set || result[1]
126
-
127
- location = location[:name] if location.is_a? Hash
128
- location = location.to_sym
129
- @results.each do |r|
130
- if r[0] == line and r[1] == location
131
- if OPTIONS[:combine_locations]
132
- return true
133
- elsif r[2] == result
134
- return true
135
- end
136
- end
137
- end
138
-
139
- false
140
- end
141
-
142
- #Ignores ignores
143
- def process_ignore exp
144
- exp
145
- end
146
-
147
- #Does not actually process string interpolation, but notes that it occurred.
148
- def process_string_interp exp
149
- @string_interp = true
150
- exp
151
- end
152
-
153
- #Checks if an expression contains string interpolation.
154
- def include_interp? exp
155
- @string_interp = false
156
- process exp
157
- @string_interp
158
- end
159
-
160
- #Checks if _exp_ includes parameters or cookies, but this only works
161
- #with the base process_default.
162
- def include_user_input? exp
163
- @has_user_input = false
164
- process exp
165
- @has_user_input
166
- end
167
-
168
- #This is used to check for user input being used directly.
169
- #
170
- #Returns false if none is found, otherwise it returns an array
171
- #where the first element is the type of user input
172
- #(either :params or :cookies) and the second element is the matching
173
- #expression
174
- def has_immediate_user_input? exp
175
- if params? exp
176
- return :params, exp
177
- elsif cookies? exp
178
- return :cookies, exp
179
- elsif call? exp
180
- if sexp? exp[1]
181
- if ALL_PARAMETERS.include? exp[1] or params? exp[1]
182
- return :params, exp
183
- elsif exp[1] == COOKIES
184
- return :cookies, exp
185
- else
186
- false
187
- end
188
- else
189
- false
190
- end
191
- elsif sexp? exp
192
- case exp.node_type
193
- when :string_interp
194
- exp.each do |e|
195
- if sexp? e
196
- type, match = has_immediate_user_input?(e)
197
- if type
198
- return type, match
199
- end
200
- end
201
- end
202
- false
203
- when :string_eval
204
- if sexp? exp[1]
205
- if exp[1].node_type == :rlist
206
- exp[1].each do |e|
207
- if sexp? e
208
- type, match = has_immediate_user_input?(e)
209
- if type
210
- return type, match
211
- end
212
- end
213
- end
214
- false
215
- else
216
- has_immediate_user_input? exp[1]
217
- end
218
- end
219
- when :format
220
- has_immediate_user_input? exp[1]
221
- when :if
222
- (sexp? exp[2] and has_immediate_user_input? exp[2]) or
223
- (sexp? exp[3] and has_immediate_user_input? exp[3])
224
- else
225
- false
226
- end
227
- end
228
- end
229
-
230
- #Checks for a model attribute at the top level of the
231
- #expression.
232
- def has_immediate_model? exp, out = nil
233
- out = exp if out.nil?
234
-
235
- if sexp? exp and exp.node_type == :output
236
- exp = exp[1]
237
- end
238
-
239
- if call? exp
240
- target = exp[1]
241
- method = exp[2]
242
-
243
- if call? target and not method.to_s[-1,1] == "?"
244
- has_immediate_model? target, out
245
- elsif model_name? target
246
- exp
247
- else
248
- false
249
- end
250
- elsif sexp? exp
251
- case exp.node_type
252
- when :string_interp
253
- exp.each do |e|
254
- if sexp? e and match = has_immediate_model?(e, out)
255
- return match
256
- end
257
- end
258
- false
259
- when :string_eval
260
- if sexp? exp[1]
261
- if exp[1].node_type == :rlist
262
- exp[1].each do |e|
263
- if sexp? e and match = has_immediate_model?(e, out)
264
- return match
265
- end
266
- end
267
- false
268
- else
269
- has_immediate_model? exp[1], out
270
- end
271
- end
272
- when :format
273
- has_immediate_model? exp[1], out
274
- when :if
275
- ((sexp? exp[2] and has_immediate_model? exp[2], out) or
276
- (sexp? exp[3] and has_immediate_model? exp[3], out))
277
- else
278
- false
279
- end
280
- end
281
- end
282
-
283
- #Checks if +exp+ is a model name.
284
- #
285
- #Prior to using this method, either @tracker must be set to
286
- #the current tracker, or else @models should contain an array of the model
287
- #names, which is available via tracker.models.keys
288
- def model_name? exp
289
- @models ||= @tracker.models.keys
290
-
291
- if exp.is_a? Symbol
292
- @models.include? exp
293
- elsif sexp? exp
294
- klass = nil
295
- begin
296
- klass = class_name exp
297
- rescue StandardError
298
- end
299
-
300
- klass and @models.include? klass
301
- else
302
- false
303
- end
304
- end
305
-
306
- #Finds entire method call chain where +target+ is a target in the chain
307
- def find_chain exp, target
308
- return unless sexp? exp
309
-
310
- case exp.node_type
311
- when :output, :format
312
- find_chain exp[1], target
313
- when :call
314
- if exp == target or include_target? exp, target
315
- return exp
316
- end
317
- else
318
- exp.each do |e|
319
- if sexp? e
320
- res = find_chain e, target
321
- return res if res
322
- end
323
- end
324
- nil
325
- end
326
- end
327
-
328
- #Returns true if +target+ is in +exp+
329
- def include_target? exp, target
330
- return false unless call? exp
331
-
332
- exp.each do |e|
333
- return true if e == target or include_target? e, target
334
- end
335
-
336
- false
337
- end
338
-
339
- #Returns true if low_version <= RAILS_VERSION <= high_version
340
- #
341
- #If the Rails version is unknown, returns false.
342
- def version_between? low_version, high_version
343
- return false unless tracker.config[:rails_version]
344
-
345
- version = tracker.config[:rails_version].split(".").map! { |n| n.to_i }
346
- low_version = low_version.split(".").map! { |n| n.to_i }
347
- high_version = high_version.split(".").map! { |n| n.to_i }
348
-
349
- version.each_with_index do |n, i|
350
- if n < low_version[i] or n > high_version[i]
351
- return false
352
- end
353
- end
354
-
355
- return true
356
- end
357
- end
@@ -1,336 +0,0 @@
1
- require 'checks/base_check'
2
- require 'processors/lib/find_call'
3
- require 'processors/lib/processor_helper'
4
- require 'util'
5
- require 'set'
6
-
7
- #This check looks for unescaped output in templates which contains
8
- #parameters or model attributes.
9
- #
10
- #For example:
11
- #
12
- # <%= User.find(:id).name %>
13
- # <%= params[:id] %>
14
- class CheckCrossSiteScripting < BaseCheck
15
- Checks.add self
16
-
17
- #Ignore these methods and their arguments.
18
- #It is assumed they will take care of escaping their output.
19
- IGNORE_METHODS = Set.new([:h, :escapeHTML, :link_to, :text_field_tag, :hidden_field_tag,
20
- :image_tag, :select, :submit_tag, :hidden_field, :url_encode,
21
- :radio_button, :will_paginate, :button_to, :url_for, :mail_to,
22
- :fields_for, :label, :text_area, :text_field, :hidden_field, :check_box,
23
- :field_field])
24
-
25
- IGNORE_MODEL_METHODS = Set.new([:average, :count, :maximum, :minimum, :sum])
26
-
27
- MODEL_METHODS = Set.new([:all, :find, :first, :last, :new])
28
-
29
- IGNORE_LIKE = /^link_to_|(_path|_tag|_url)$/
30
-
31
- HAML_HELPERS = Sexp.new(:colon2, Sexp.new(:const, :Haml), :Helpers)
32
-
33
- XML_HELPER = Sexp.new(:colon2, Sexp.new(:const, :Erubis), :XmlHelper)
34
-
35
- URI = Sexp.new(:const, :URI)
36
-
37
- CGI = Sexp.new(:const, :CGI)
38
-
39
- FORM_BUILDER = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
40
-
41
- #Run check
42
- def run_check
43
- IGNORE_METHODS.merge OPTIONS[:safe_methods]
44
- @models = tracker.models.keys
45
- @inspect_arguments = OPTIONS[:check_arguments]
46
-
47
- CheckLinkTo.new(checks, tracker).run_check
48
-
49
- tracker.each_template do |name, template|
50
- @current_template = template
51
-
52
- template[:outputs].each do |out|
53
- type, match = (out[0] == :output and has_immediate_user_input?(out[1]))
54
- if type and not duplicate? out
55
- add_result out
56
- case type
57
- when :params
58
- message = "Unescaped parameter value"
59
- when :cookies
60
- message = "Unescaped cookie value"
61
- else
62
- message = "Unescaped user input value"
63
- end
64
-
65
- warn :template => @current_template,
66
- :warning_type => "Cross Site Scripting",
67
- :message => message,
68
- :line => match.line,
69
- :code => match,
70
- :confidence => CONFIDENCE[:high]
71
-
72
- elsif not OPTIONS[:ignore_model_output] and match = has_immediate_model?(out[1])
73
- method = match[2]
74
-
75
- unless duplicate? out or IGNORE_MODEL_METHODS.include? method
76
- add_result out
77
-
78
- if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
79
- confidence = CONFIDENCE[:high]
80
- else
81
- confidence = CONFIDENCE[:med]
82
- end
83
-
84
- code = find_chain out, match
85
- warn :template => @current_template,
86
- :warning_type => "Cross Site Scripting",
87
- :message => "Unescaped model attribute",
88
- :line => code.line,
89
- :code => code,
90
- :confidence => confidence
91
- end
92
-
93
- else
94
- @matched = false
95
- @mark = false
96
- process out
97
- end
98
- end
99
- end
100
- end
101
-
102
- #Process an output Sexp
103
- def process_output exp
104
- process exp[1].dup
105
- end
106
-
107
- #Look for calls to raw()
108
- #Otherwise, ignore
109
- def process_escaped_output exp
110
- if exp[1].node_type == :call and exp[1][2] == :raw
111
- process_output exp
112
- else
113
- exp
114
- end
115
- end
116
-
117
- #Check a call for user input
118
- #
119
- #
120
- #Since we want to report an entire call and not just part of one, use @mark
121
- #to mark when a call is started. Any dangerous values inside will then
122
- #report the entire call chain.
123
- def process_call exp
124
- if @mark
125
- actually_process_call exp
126
- else
127
- @mark = true
128
- actually_process_call exp
129
- message = nil
130
-
131
- if @matched == :model and not OPTIONS[:ignore_model_output]
132
- message = "Unescaped model attribute"
133
- elsif @matched == :params
134
- message = "Unescaped parameter value"
135
- end
136
-
137
- if message and not duplicate? exp
138
- add_result exp
139
-
140
- warn :template => @current_template,
141
- :warning_type => "Cross Site Scripting",
142
- :message => message,
143
- :line => exp.line,
144
- :code => exp,
145
- :confidence => CONFIDENCE[:low]
146
- end
147
-
148
- @mark = @matched = false
149
- end
150
-
151
- exp
152
- end
153
-
154
- def actually_process_call exp
155
- return if @matched
156
- target = exp[1]
157
- if sexp? target
158
- target = process target
159
- end
160
-
161
- method = exp[2]
162
- args = exp[3]
163
-
164
- #Ignore safe items
165
- if (target.nil? and (IGNORE_METHODS.include? method or method.to_s =~ IGNORE_LIKE)) or
166
- (@matched == :model and IGNORE_MODEL_METHODS.include? method) or
167
- (target == HAML_HELPERS and method == :html_escape) or
168
- ((target == URI or target == CGI) and method == :escape) or
169
- (target == XML_HELPER and method == :escape_xml) or
170
- (target == FORM_BUILDER and IGNORE_METHODS.include? method) or
171
- (method.to_s[-1,1] == "?")
172
-
173
- exp[0] = :ignore
174
- @matched = false
175
- elsif sexp? exp[1] and model_name? exp[1][1]
176
-
177
- @matched = :model
178
- elsif @inspect_arguments and (ALL_PARAMETERS.include?(exp) or params? exp)
179
- @matched = :params
180
- elsif @inspect_arguments
181
- process args
182
- end
183
- end
184
-
185
- #Note that params have been found
186
- def process_params exp
187
- @matched = :params
188
- exp
189
- end
190
-
191
- #Note that cookies have been found
192
- def process_cookies exp
193
- @matched = :cookies
194
- exp
195
- end
196
-
197
- #Ignore calls to render
198
- def process_render exp
199
- exp
200
- end
201
-
202
- #Process as default
203
- def process_string_interp exp
204
- process_default exp
205
- end
206
-
207
- #Process as default
208
- def process_format exp
209
- process_default exp
210
- end
211
-
212
- #Ignore output HTML escaped via HAML
213
- def process_format_escaped exp
214
- exp
215
- end
216
-
217
- #Ignore condition in if Sexp
218
- def process_if exp
219
- exp[2..-1].each do |e|
220
- process e if sexp? e
221
- end
222
- exp
223
- end
224
- end
225
-
226
- #This _only_ checks calls to link_to
227
- class CheckLinkTo < CheckCrossSiteScripting
228
- IGNORE_METHODS = IGNORE_METHODS - [:link_to]
229
-
230
- def run_check
231
- #Ideally, I think this should also check to see if people are setting
232
- #:escape => false
233
- methods = tracker.find_call [], :link_to
234
-
235
- @models = tracker.models.keys
236
- @inspect_arguments = OPTIONS[:check_arguments]
237
-
238
- methods.each do |call|
239
- process_result call
240
- end
241
- end
242
-
243
- def process_result result
244
- #Have to make a copy of this, otherwise it will be changed to
245
- #an ignored method call by the code above.
246
- call = result[-1] = result[-1].dup
247
-
248
- @matched = false
249
-
250
- return if call[3][1].nil?
251
-
252
- #Only check first argument for +link_to+, as the second
253
- #will *usually* be a record or escaped.
254
- first_arg = process call[3][1]
255
-
256
- type, match = has_immediate_user_input? first_arg
257
-
258
- if type
259
- case type
260
- when :params
261
- message = "Unescaped parameter value in link_to"
262
- when :cookies
263
- message = "Unescaped cookie value in link_to"
264
- else
265
- message = "Unescaped user input value in link_to"
266
- end
267
-
268
- unless duplicate? result
269
- add_result result
270
-
271
- warn :result => result,
272
- :warning_type => "Cross Site Scripting",
273
- :message => message,
274
- :confidence => CONFIDENCE[:high]
275
- end
276
-
277
- elsif not OPTIONS[:ignore_model_output] and match = has_immediate_model?(first_arg)
278
- method = match[2]
279
-
280
- unless duplicate? result or IGNORE_MODEL_METHODS.include? method
281
- add_result result
282
-
283
- if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
284
- confidence = CONFIDENCE[:high]
285
- else
286
- confidence = CONFIDENCE[:med]
287
- end
288
-
289
- warn :result => result,
290
- :warning_type => "Cross Site Scripting",
291
- :message => "Unescaped model attribute in link_to",
292
- :confidence => confidence
293
- end
294
-
295
- elsif @matched
296
- if @matched == :model and not OPTIONS[:ignore_model_output]
297
- message = "Unescaped model attribute in link_to"
298
- elsif @matched == :params
299
- message = "Unescaped parameter value in link_to"
300
- end
301
-
302
- if message and not duplicate? result
303
- add_result result
304
-
305
- warn :result => result,
306
- :warning_type => "Cross Site Scripting",
307
- :message => message,
308
- :confidence => CONFIDENCE[:med]
309
- end
310
- end
311
- end
312
-
313
- def process_call exp
314
- @mark = true
315
- actually_process_call exp
316
- exp
317
- end
318
-
319
-
320
- def actually_process_call exp
321
- return if @matched
322
-
323
- target = exp[1]
324
- if sexp? target
325
- target = process target.dup
326
- end
327
-
328
- #Bare records create links to the model resource,
329
- #not a string that could have injection
330
- if model_name? target and context == [:call, :arglist]
331
- return exp
332
- end
333
-
334
- super
335
- end
336
- end