brakeman 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +112 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +266 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +338 -0
  7. data/lib/checks/check_cross_site_scripting.rb +216 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +29 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +25 -0
  13. data/lib/checks/check_mass_assignment.rb +72 -0
  14. data/lib/checks/check_model_attributes.rb +36 -0
  15. data/lib/checks/check_redirect.rb +98 -0
  16. data/lib/checks/check_render.rb +65 -0
  17. data/lib/checks/check_send_file.rb +15 -0
  18. data/lib/checks/check_session_settings.rb +36 -0
  19. data/lib/checks/check_sql.rb +124 -0
  20. data/lib/checks/check_validation_regex.rb +60 -0
  21. data/lib/format/style.css +105 -0
  22. data/lib/processor.rb +83 -0
  23. data/lib/processors/alias_processor.rb +384 -0
  24. data/lib/processors/base_processor.rb +235 -0
  25. data/lib/processors/config_processor.rb +146 -0
  26. data/lib/processors/controller_alias_processor.rb +222 -0
  27. data/lib/processors/controller_processor.rb +175 -0
  28. data/lib/processors/erb_template_processor.rb +84 -0
  29. data/lib/processors/erubis_template_processor.rb +62 -0
  30. data/lib/processors/haml_template_processor.rb +115 -0
  31. data/lib/processors/lib/find_call.rb +176 -0
  32. data/lib/processors/lib/find_model_call.rb +39 -0
  33. data/lib/processors/lib/processor_helper.rb +36 -0
  34. data/lib/processors/lib/render_helper.rb +118 -0
  35. data/lib/processors/library_processor.rb +117 -0
  36. data/lib/processors/model_processor.rb +125 -0
  37. data/lib/processors/output_processor.rb +204 -0
  38. data/lib/processors/params_processor.rb +77 -0
  39. data/lib/processors/route_processor.rb +338 -0
  40. data/lib/processors/template_alias_processor.rb +86 -0
  41. data/lib/processors/template_processor.rb +55 -0
  42. data/lib/report.rb +628 -0
  43. data/lib/scanner.rb +232 -0
  44. data/lib/tracker.rb +144 -0
  45. data/lib/util.rb +141 -0
  46. data/lib/warning.rb +97 -0
  47. metadata +191 -0
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'sexp_processor'
3
+ require 'set'
4
+
5
+ #Looks for request parameters. Not used currently.
6
+ class ParamsProcessor < SexpProcessor
7
+ attr_reader :result
8
+
9
+ def initialize
10
+ super()
11
+ self.strict = false
12
+ self.auto_shift_type = false
13
+ self.require_empty = false
14
+ self.default_method = :process_default
15
+ self.warn_on_default = false
16
+ @result = []
17
+ @matched = false
18
+ @mark = false
19
+ @watch_nodes = Set.new([:call, :iasgn, :lasgn, :gasgn, :cvasgn, :return, :attrasgn])
20
+ @params = Sexp.new(:call, nil, :params, Sexp.new(:arglist))
21
+ end
22
+
23
+ def process_default exp
24
+ if @watch_nodes.include?(exp.node_type) and not @mark
25
+ @mark = true
26
+ @matched = false
27
+ process_these exp[1..-1]
28
+ if @matched
29
+ @result << exp
30
+ @matched = false
31
+ end
32
+ @mark = false
33
+ else
34
+ process_these exp[1..-1]
35
+ end
36
+
37
+ exp
38
+ end
39
+
40
+ def process_these exp
41
+ exp.each do |e|
42
+ if sexp? e and not e.empty?
43
+ process e
44
+ end
45
+ end
46
+ end
47
+
48
+ def process_call exp
49
+ if @mark
50
+ actually_process_call exp
51
+ else
52
+ @mark = true
53
+ actually_process_call exp
54
+ if @matched
55
+ @result << exp
56
+ end
57
+ @mark = @matched = false
58
+ end
59
+
60
+ exp
61
+ end
62
+
63
+ def actually_process_call exp
64
+ process exp[1]
65
+ process exp[3]
66
+ if exp[1] == @params or exp == @params
67
+ @matched = true
68
+ end
69
+ end
70
+
71
+ #Don't really care about condition
72
+ def process_if exp
73
+ process_these exp[2..-1]
74
+ exp
75
+ end
76
+
77
+ end
@@ -0,0 +1,338 @@
1
+ require 'processors/base_processor'
2
+ require 'processors/alias_processor'
3
+ require 'util'
4
+ require 'set'
5
+
6
+ #Processes the Sexp from routes.rb. Stores results in tracker.routes.
7
+ #
8
+ #Note that it is only interested in determining what methods on which
9
+ #controllers are used as routes, not the generated URLs for routes.
10
+ class RoutesProcessor < BaseProcessor
11
+ attr_reader :map, :nested, :current_controller
12
+
13
+ def initialize tracker
14
+ super
15
+ @map = Sexp.new(:lvar, :map)
16
+ @nested = nil #used for identifying nested targets
17
+ @prefix = [] #Controller name prefix (a module name, usually)
18
+ @current_controller = nil
19
+ @with_options = nil #For use inside map.with_options
20
+ end
21
+
22
+ #Call this with parsed route file information.
23
+ #
24
+ #This method first calls RouteAliasProcessor#process_safely on the +exp+,
25
+ #so it does not modify the +exp+.
26
+ def process_routes exp
27
+ process RouteAliasProcessor.new.process_safely(exp)
28
+ end
29
+
30
+ #Looking for mapping of routes
31
+ def process_call exp
32
+ target = exp[1]
33
+
34
+ if target == map or target == nested
35
+ process_map exp
36
+
37
+ else
38
+ process_default exp
39
+ end
40
+
41
+ exp
42
+ end
43
+
44
+ #Process a map.something call
45
+ #based on the method used
46
+ def process_map exp
47
+ args = exp[3][1..-1]
48
+
49
+ case exp[2]
50
+ when :resource
51
+ process_resource args
52
+ when :resources
53
+ process_resources args
54
+ when :connect, :root
55
+ process_connect args
56
+ else
57
+ process_named_route args
58
+ end
59
+
60
+ exp
61
+ end
62
+
63
+ #Look for map calls that take a block.
64
+ #Otherwise, just do the default processing.
65
+ def process_iter exp
66
+ if exp[1][1] == map or exp[1][1] == nested
67
+ method = exp[1][2]
68
+ case method
69
+ when :namespace
70
+ process_namespace exp
71
+ when :resources, :resource
72
+ process_resources exp[1][3][1..-1]
73
+ process_default exp[3]
74
+ when :with_options
75
+ process_with_options exp
76
+ end
77
+ exp
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ #Process
84
+ # map.resources :x, :controller => :y, :member => ...
85
+ #etc.
86
+ def process_resources exp
87
+ controller = check_for_controller_name exp
88
+ if controller
89
+ self.current_controller = controller
90
+ process_resource_options exp[-1]
91
+ else
92
+ exp.each do |argument|
93
+ if sexp? argument and argument.node_type == :lit
94
+ self.current_controller = exp[0][1]
95
+ add_resources_routes
96
+ process_resource_options exp[-1]
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ #Add default routes
103
+ def add_resources_routes
104
+ @tracker.routes[@current_controller].merge [:index, :new, :create, :show, :edit, :update, :destroy]
105
+ end
106
+
107
+ #Process all the options that might be in the hash passed to
108
+ #map.resource, et al.
109
+ def process_resource_options exp
110
+ if exp.nil? and @with_options
111
+ exp = @with_options
112
+ elsif @with_options
113
+ exp = exp.concat @with_options[1..-1]
114
+ end
115
+ return unless exp.node_type == :hash
116
+
117
+ hash_iterate(exp) do |option, value|
118
+ case option[1]
119
+ when :controller, :requirements, :singular, :path_prefix, :as,
120
+ :path_names, :shallow, :name_prefix
121
+ #should be able to skip
122
+ when :collection, :member, :new
123
+ process_collection value
124
+ when :has_one
125
+ save_controller = current_controller
126
+ process_resource value[1..-1]
127
+ self.current_controller = save_controller
128
+ when :has_many
129
+ save_controller = current_controller
130
+ process_resources value[1..-1]
131
+ self.current_controller = save_controller
132
+ when :only
133
+ process_option_only value
134
+ when :except
135
+ process_option_except value
136
+ else
137
+ raise "Unhandled resource option: #{option}"
138
+ end
139
+ end
140
+ end
141
+
142
+ #Process route option :only => ...
143
+ def process_option_only exp
144
+ routes = @tracker.routes[@current_controller]
145
+ [:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
146
+ routes.delete r
147
+ end
148
+
149
+ if exp.node_type == :array
150
+ exp[1..-1].each do |e|
151
+ routes << e[1]
152
+ end
153
+ end
154
+ end
155
+
156
+ #Process route option :except => ...
157
+ def process_option_except exp
158
+ return unless exp.node_type == :array
159
+ routes = @tracker.routes[@current_controller]
160
+
161
+ exp[1..-1].each do |e|
162
+ routes.delete e[1]
163
+ end
164
+ end
165
+
166
+ # map.resource :x, ..
167
+ def process_resource exp
168
+ controller = check_for_controller_name exp
169
+ if controller
170
+ self.current_controller = controller
171
+ process_resource_options exp[-1]
172
+ else
173
+ exp.each do |argument|
174
+ if argument.node_type == :lit
175
+ self.current_controller = pluralize(exp[0][1].to_s)
176
+ add_resource_routes
177
+ process_resource_options exp[-1]
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ #Add default routes minus :index
184
+ def add_resource_routes
185
+ @tracker.routes[@current_controller].merge [:new, :create, :show, :edit, :update, :destroy]
186
+ end
187
+
188
+ #Process
189
+ # map.connect '/something', :controller => 'blah', :action => 'whatever'
190
+ def process_connect exp
191
+ controller = check_for_controller_name exp
192
+ self.current_controller = controller if controller
193
+
194
+ #Check for default route
195
+ if string? exp[0]
196
+ if exp[0][1] == ":controller/:action/:id"
197
+ @tracker.routes[:allow_all_actions] = exp[0]
198
+ elsif exp[0][1].include? ":action"
199
+ @tracker.routes[@current_controller] = :allow_all_actions
200
+ return
201
+ end
202
+ end
203
+
204
+ #This -seems- redundant, but people might connect actions
205
+ #to a controller which already allows them all
206
+ return if @tracker.routes[@current_controller] == :allow_all_actions
207
+
208
+ exp[-1].each_with_index do |e,i|
209
+ if symbol? e and e[1] == :action
210
+ @tracker.routes[@current_controller] << exp[-1][i + 1][1].to_sym
211
+ return
212
+ end
213
+ end
214
+ end
215
+
216
+ # map.with_options :controller => 'something' do |something|
217
+ # something.resources :blah
218
+ # end
219
+ def process_with_options exp
220
+ @with_options = exp[1][3][-1]
221
+ @nested = Sexp.new(:lvar, exp[2][1])
222
+
223
+ self.current_controller = check_for_controller_name exp[1][3]
224
+
225
+ #process block
226
+ process exp[3]
227
+
228
+ @with_options = nil
229
+ @nested = nil
230
+ end
231
+
232
+ # map.namespace :something do |something|
233
+ # something.resources :blah
234
+ # end
235
+ def process_namespace exp
236
+ call = exp[1]
237
+ formal_args = exp[2]
238
+ block = exp[3]
239
+
240
+ @prefix << camelize(call[3][1][1])
241
+
242
+ @nested = Sexp.new(:lvar, formal_args[1])
243
+
244
+ process block
245
+
246
+ @prefix.pop
247
+ end
248
+
249
+ # map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
250
+ def process_named_route exp
251
+ process_connect exp
252
+ end
253
+
254
+ #Process collection option
255
+ # :collection => { :some_action => :http_actions }
256
+ def process_collection exp
257
+ return unless exp.node_type == :hash
258
+ routes = @tracker.routes[@current_controller]
259
+
260
+ hash_iterate(exp) do |action, type|
261
+ routes << action[1]
262
+ end
263
+ end
264
+
265
+ #Manage Controller prefixes
266
+ #@prefix is an Array, but this method returns a string
267
+ #suitable for prefixing onto a controller name.
268
+ def prefix
269
+ if @prefix.length > 0
270
+ @prefix.join("::") << "::"
271
+ else
272
+ ''
273
+ end
274
+ end
275
+
276
+ #Sets the controller name to a proper class name.
277
+ #For example
278
+ # self.current_controller = :session
279
+ # @controller == :SessionController #true
280
+ #
281
+ #Also prepends the prefix if there is one set.
282
+ def current_controller= name
283
+ @current_controller = (prefix + camelize(name) + "Controller").to_sym
284
+ @tracker.routes[@current_controller] ||= Set.new
285
+ end
286
+
287
+ private
288
+
289
+ #Checks an argument list for a hash that has a key :controller.
290
+ #If it does, returns the value.
291
+ #
292
+ #Otherwise, returns nil.
293
+ def check_for_controller_name args
294
+ args.each do |a|
295
+ if hash? a
296
+ hash_iterate(a) do |k, v|
297
+ if k[1] == :controller
298
+ return v[1]
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ nil
305
+ end
306
+ end
307
+
308
+ #This is for a really specific case where a hash is used as arguments
309
+ #to one of the map methods.
310
+ class RouteAliasProcessor < AliasProcessor
311
+
312
+ #This replaces
313
+ # { :some => :hash }.keys
314
+ #with
315
+ # [:some]
316
+ def process_call exp
317
+ process_default exp
318
+
319
+ if hash? exp[1] and exp[2] == :keys
320
+ keys = get_keys exp[1]
321
+ exp.clear
322
+ keys.each_with_index do |e,i|
323
+ exp[i] = e
324
+ end
325
+ end
326
+ exp
327
+ end
328
+
329
+ #Returns an array Sexp containing the keys from the hash
330
+ def get_keys hash
331
+ keys = Sexp.new(:array)
332
+ hash_iterate(hash) do |key, value|
333
+ keys << key
334
+ end
335
+
336
+ keys
337
+ end
338
+ end
@@ -0,0 +1,86 @@
1
+ require 'set'
2
+ require 'processors/alias_processor'
3
+ require 'processors/lib/render_helper'
4
+
5
+ #Processes aliasing in templates.
6
+ #Handles calls to +render+.
7
+ class TemplateAliasProcessor < AliasProcessor
8
+ include RenderHelper
9
+
10
+ FORM_METHODS = Set.new([:form_for, :remote_form_for, :form_remote_for])
11
+
12
+ def initialize tracker, template
13
+ super()
14
+ @tracker = tracker
15
+ @template = template
16
+ end
17
+
18
+ #Process template
19
+ def process_template name, args
20
+ super name, args, "Template:#{@template[:name]}"
21
+ end
22
+
23
+ #Determine template name
24
+ def template_name name
25
+ unless name.to_s.include? "/"
26
+ name = "#{@template[:name].to_s.match(/^(.*\/).*$/)[1]}#{name}"
27
+ end
28
+ name
29
+ end
30
+
31
+ #Looks for form methods and iterating over collections of Models
32
+ def process_call_with_block exp
33
+ process_default exp
34
+
35
+ call = exp[1]
36
+ target = call[1]
37
+ method = call[2]
38
+ args = exp[2]
39
+ block = exp[3]
40
+
41
+ #Check for e.g. Model.find.each do ... end
42
+ if method == :each and args and block and model = get_model_target(target)
43
+ if sexp? args and args.node_type == :lasgn
44
+ if model == target[1]
45
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, model, :new, Sexp.new(:arglist))
46
+ else
47
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, Tracker::UNKNOWN_MODEL), :new, Sexp.new(:arglist))
48
+ end
49
+
50
+ process block if sexp? block
51
+ end
52
+ elsif FORM_METHODS.include? method
53
+ if sexp? args and args.node_type == :lasgn
54
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
55
+
56
+ process block if sexp? block
57
+ end
58
+ end
59
+
60
+ exp
61
+ end
62
+
63
+ alias process_iter process_call_with_block
64
+
65
+ #Checks if +exp+ is a call to Model.all or Model.find*
66
+ def get_model_target exp
67
+ if call? exp
68
+ target = exp[1]
69
+
70
+ if exp[2] == :all or exp[2].to_s[0,4] == "find"
71
+ models = Set.new @tracker.models.keys
72
+
73
+ begin
74
+ name = class_name target
75
+ return target if models.include?(name)
76
+ rescue StandardError
77
+ end
78
+
79
+ end
80
+
81
+ return get_model_target(target)
82
+ end
83
+
84
+ false
85
+ end
86
+ end