brakeman 0.0.2
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.
- data/FEATURES +16 -0
- data/README.md +112 -0
- data/WARNING_TYPES +69 -0
- data/bin/brakeman +266 -0
- data/lib/checks.rb +67 -0
- data/lib/checks/base_check.rb +338 -0
- data/lib/checks/check_cross_site_scripting.rb +216 -0
- data/lib/checks/check_default_routes.rb +29 -0
- data/lib/checks/check_evaluation.rb +29 -0
- data/lib/checks/check_execute.rb +110 -0
- data/lib/checks/check_file_access.rb +46 -0
- data/lib/checks/check_forgery_setting.rb +25 -0
- data/lib/checks/check_mass_assignment.rb +72 -0
- data/lib/checks/check_model_attributes.rb +36 -0
- data/lib/checks/check_redirect.rb +98 -0
- data/lib/checks/check_render.rb +65 -0
- data/lib/checks/check_send_file.rb +15 -0
- data/lib/checks/check_session_settings.rb +36 -0
- data/lib/checks/check_sql.rb +124 -0
- data/lib/checks/check_validation_regex.rb +60 -0
- data/lib/format/style.css +105 -0
- data/lib/processor.rb +83 -0
- data/lib/processors/alias_processor.rb +384 -0
- data/lib/processors/base_processor.rb +235 -0
- data/lib/processors/config_processor.rb +146 -0
- data/lib/processors/controller_alias_processor.rb +222 -0
- data/lib/processors/controller_processor.rb +175 -0
- data/lib/processors/erb_template_processor.rb +84 -0
- data/lib/processors/erubis_template_processor.rb +62 -0
- data/lib/processors/haml_template_processor.rb +115 -0
- data/lib/processors/lib/find_call.rb +176 -0
- data/lib/processors/lib/find_model_call.rb +39 -0
- data/lib/processors/lib/processor_helper.rb +36 -0
- data/lib/processors/lib/render_helper.rb +118 -0
- data/lib/processors/library_processor.rb +117 -0
- data/lib/processors/model_processor.rb +125 -0
- data/lib/processors/output_processor.rb +204 -0
- data/lib/processors/params_processor.rb +77 -0
- data/lib/processors/route_processor.rb +338 -0
- data/lib/processors/template_alias_processor.rb +86 -0
- data/lib/processors/template_processor.rb +55 -0
- data/lib/report.rb +628 -0
- data/lib/scanner.rb +232 -0
- data/lib/tracker.rb +144 -0
- data/lib/util.rb +141 -0
- data/lib/warning.rb +97 -0
- 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
|