brakeman 1.1.0 → 1.2.0
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/bin/brakeman +32 -173
- data/lib/brakeman.rb +47 -25
- data/lib/brakeman/call_index.rb +37 -1
- data/lib/brakeman/checks.rb +17 -0
- data/lib/brakeman/checks/base_check.rb +5 -1
- data/lib/brakeman/checks/check_cross_site_scripting.rb +18 -22
- data/lib/brakeman/checks/check_execute.rb +11 -24
- data/lib/brakeman/checks/check_render.rb +15 -26
- data/lib/brakeman/checks/check_sql.rb +48 -3
- data/lib/brakeman/options.rb +204 -0
- data/lib/brakeman/processor.rb +2 -2
- data/lib/brakeman/processors/controller_alias_processor.rb +9 -1
- data/lib/brakeman/processors/lib/find_all_calls.rb +36 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +1 -0
- data/lib/brakeman/processors/model_processor.rb +1 -1
- data/lib/brakeman/report.rb +36 -122
- data/lib/brakeman/rescanner.rb +247 -0
- data/lib/brakeman/scanner.rb +94 -76
- data/lib/brakeman/tracker.rb +103 -2
- data/lib/brakeman/util.rb +106 -0
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +26 -11
- metadata +5 -3
data/lib/brakeman/scanner.rb
CHANGED
@@ -30,12 +30,12 @@ class Brakeman::Scanner
|
|
30
30
|
RUBY_1_9 = !!(RUBY_VERSION =~ /^1\.9/)
|
31
31
|
|
32
32
|
#Pass in path to the root of the Rails application
|
33
|
-
def initialize options
|
33
|
+
def initialize options, processor = nil
|
34
34
|
@options = options
|
35
35
|
@report_progress = options[:report_progress]
|
36
36
|
@path = options[:app_path]
|
37
37
|
@app_path = File.join(@path, "app")
|
38
|
-
@processor = Brakeman::Processor.new
|
38
|
+
@processor = processor || Brakeman::Processor.new(options)
|
39
39
|
|
40
40
|
if RUBY_1_9
|
41
41
|
@ruby_parser = ::Ruby19Parser
|
@@ -124,13 +124,18 @@ class Brakeman::Scanner
|
|
124
124
|
#Adds parsed information to tracker.initializers
|
125
125
|
def process_initializers
|
126
126
|
Dir.glob(@path + "/config/initializers/**/*.rb").sort.each do |f|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
127
|
+
process_initializer f
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#Process an initializer
|
132
|
+
def process_initializer path
|
133
|
+
begin
|
134
|
+
@processor.process_initializer(path, parse_ruby(File.read(path)))
|
135
|
+
rescue Racc::ParseError => e
|
136
|
+
tracker.error e, "could not parse #{path}. There is probably a typo in the file. Test it with 'ruby_parse #{path}'"
|
137
|
+
rescue Exception => e
|
138
|
+
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
134
139
|
end
|
135
140
|
end
|
136
141
|
|
@@ -154,13 +159,18 @@ class Brakeman::Scanner
|
|
154
159
|
current += 1
|
155
160
|
end
|
156
161
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
162
|
+
process_lib f
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
#Process a library
|
167
|
+
def process_lib path
|
168
|
+
begin
|
169
|
+
@processor.process_lib parse_ruby(File.read(path)), path
|
170
|
+
rescue Racc::ParseError => e
|
171
|
+
tracker.error e, "could not parse #{path}. There is probably a typo in the file. Test it with 'ruby_parse #{path}'"
|
172
|
+
rescue Exception => e
|
173
|
+
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
164
174
|
end
|
165
175
|
end
|
166
176
|
|
@@ -196,13 +206,7 @@ class Brakeman::Scanner
|
|
196
206
|
current += 1
|
197
207
|
end
|
198
208
|
|
199
|
-
|
200
|
-
@processor.process_controller(parse_ruby(File.read(f)), f)
|
201
|
-
rescue Racc::ParseError => e
|
202
|
-
tracker.error e, "could not parse #{f}. There is probably a typo in the file. Test it with 'ruby_parse #{f}'"
|
203
|
-
rescue Exception => e
|
204
|
-
tracker.error e.exception(e.message + "\nWhile processing #{f}"), e.backtrace
|
205
|
-
end
|
209
|
+
process_controller f
|
206
210
|
end
|
207
211
|
|
208
212
|
current = 0
|
@@ -220,6 +224,16 @@ class Brakeman::Scanner
|
|
220
224
|
end
|
221
225
|
end
|
222
226
|
|
227
|
+
def process_controller path
|
228
|
+
begin
|
229
|
+
@processor.process_controller(parse_ruby(File.read(path)), path)
|
230
|
+
rescue Racc::ParseError => e
|
231
|
+
tracker.error e, "could not parse #{path}. There is probably a typo in the file. Test it with 'ruby_parse #{path}'"
|
232
|
+
rescue Exception => e
|
233
|
+
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
223
237
|
#Process all views and partials in views/
|
224
238
|
#
|
225
239
|
#Adds processed views to tracker.views
|
@@ -232,52 +246,13 @@ class Brakeman::Scanner
|
|
232
246
|
template_files = Dir.glob(views_path).sort
|
233
247
|
total = template_files.length
|
234
248
|
|
235
|
-
template_files.each do |
|
249
|
+
template_files.each do |path|
|
236
250
|
if @report_progress
|
237
251
|
$stderr.print " #{count}/#{total} files processed\r"
|
238
252
|
count += 1
|
239
253
|
end
|
240
254
|
|
241
|
-
|
242
|
-
type = :erb if type == :rhtml
|
243
|
-
name = template_path_to_name f
|
244
|
-
text = File.read f
|
245
|
-
|
246
|
-
begin
|
247
|
-
if type == :erb
|
248
|
-
if tracker.config[:escape_html]
|
249
|
-
type = :erubis
|
250
|
-
if options[:rails3]
|
251
|
-
src = Brakeman::RailsXSSErubis.new(text).src
|
252
|
-
else
|
253
|
-
src = Brakeman::ErubisEscape.new(text).src
|
254
|
-
end
|
255
|
-
elsif tracker.config[:erubis]
|
256
|
-
type = :erubis
|
257
|
-
src = Brakeman::ScannerErubis.new(text).src
|
258
|
-
else
|
259
|
-
src = ERB.new(text, nil, "-").src
|
260
|
-
src.sub!(/^#.*\n/, '') if RUBY_1_9
|
261
|
-
end
|
262
|
-
|
263
|
-
parsed = parse_ruby src
|
264
|
-
elsif type == :haml
|
265
|
-
src = Haml::Engine.new(text,
|
266
|
-
:escape_html => !!tracker.config[:escape_html]).precompiled
|
267
|
-
parsed = parse_ruby src
|
268
|
-
else
|
269
|
-
tracker.error "Unkown template type in #{f}"
|
270
|
-
end
|
271
|
-
|
272
|
-
@processor.process_template(name, parsed, type, nil, f)
|
273
|
-
|
274
|
-
rescue Racc::ParseError => e
|
275
|
-
tracker.error e, "could not parse #{f}"
|
276
|
-
rescue Haml::Error => e
|
277
|
-
tracker.error e, ["While compiling HAML in #{f}"] << e.backtrace
|
278
|
-
rescue Exception => e
|
279
|
-
tracker.error e.exception(e.message + "\nWhile processing #{f}"), e.backtrace
|
280
|
-
end
|
255
|
+
process_template path
|
281
256
|
end
|
282
257
|
|
283
258
|
total = tracker.templates.length
|
@@ -293,7 +268,49 @@ class Brakeman::Scanner
|
|
293
268
|
|
294
269
|
@processor.process_template_alias tracker.templates[name]
|
295
270
|
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def process_template path
|
274
|
+
type = path.match(/.*\.(erb|haml|rhtml)$/)[1].to_sym
|
275
|
+
type = :erb if type == :rhtml
|
276
|
+
name = template_path_to_name path
|
277
|
+
text = File.read path
|
278
|
+
|
279
|
+
begin
|
280
|
+
if type == :erb
|
281
|
+
if tracker.config[:escape_html]
|
282
|
+
type = :erubis
|
283
|
+
if options[:rails3]
|
284
|
+
src = Brakeman::RailsXSSErubis.new(text).src
|
285
|
+
else
|
286
|
+
src = Brakeman::ErubisEscape.new(text).src
|
287
|
+
end
|
288
|
+
elsif tracker.config[:erubis]
|
289
|
+
type = :erubis
|
290
|
+
src = Brakeman::ScannerErubis.new(text).src
|
291
|
+
else
|
292
|
+
src = ERB.new(text, nil, "-").src
|
293
|
+
src.sub!(/^#.*\n/, '') if RUBY_1_9
|
294
|
+
end
|
295
|
+
|
296
|
+
parsed = parse_ruby src
|
297
|
+
elsif type == :haml
|
298
|
+
src = Haml::Engine.new(text,
|
299
|
+
:escape_html => !!tracker.config[:escape_html]).precompiled
|
300
|
+
parsed = parse_ruby src
|
301
|
+
else
|
302
|
+
tracker.error "Unkown template type in #{path}"
|
303
|
+
end
|
296
304
|
|
305
|
+
@processor.process_template(name, parsed, type, nil, path)
|
306
|
+
|
307
|
+
rescue Racc::ParseError => e
|
308
|
+
tracker.error e, "could not parse #{path}"
|
309
|
+
rescue Haml::Error => e
|
310
|
+
tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
|
311
|
+
rescue Exception => e
|
312
|
+
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
313
|
+
end
|
297
314
|
end
|
298
315
|
|
299
316
|
#Convert path/filename to view name
|
@@ -320,13 +337,18 @@ class Brakeman::Scanner
|
|
320
337
|
current += 1
|
321
338
|
end
|
322
339
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
340
|
+
process_model f
|
341
|
+
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def process_model path
|
346
|
+
begin
|
347
|
+
@processor.process_model(parse_ruby(File.read(path)), path)
|
348
|
+
rescue Racc::ParseError => e
|
349
|
+
tracker.error e, "could not parse #{path}"
|
350
|
+
rescue Exception => e
|
351
|
+
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
330
352
|
end
|
331
353
|
end
|
332
354
|
|
@@ -335,11 +357,7 @@ class Brakeman::Scanner
|
|
335
357
|
end
|
336
358
|
|
337
359
|
def parse_ruby input
|
338
|
-
|
339
|
-
Ruby19Parser.new.parse input
|
340
|
-
else
|
341
|
-
Ruby18Parser.new.parse input
|
342
|
-
end
|
360
|
+
@ruby_parser.new.parse input
|
343
361
|
end
|
344
362
|
end
|
345
363
|
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -67,7 +67,7 @@ class Brakeman::Tracker
|
|
67
67
|
[self.controllers, self.models].each do |set|
|
68
68
|
set.each do |set_name, info|
|
69
69
|
[:private, :public, :protected].each do |visibility|
|
70
|
-
info[visibility].each do |method_name, definition|
|
70
|
+
info[visibility].each do |method_name, definition|
|
71
71
|
if definition.node_type == :selfdef
|
72
72
|
method_name = "#{definition[1]}.#{method_name}"
|
73
73
|
end
|
@@ -118,7 +118,7 @@ class Brakeman::Tracker
|
|
118
118
|
# User.human.active.all(...)
|
119
119
|
#
|
120
120
|
def find_call options
|
121
|
-
|
121
|
+
index_call_sites unless @call_index
|
122
122
|
@call_index.find_calls options
|
123
123
|
end
|
124
124
|
|
@@ -151,4 +151,105 @@ class Brakeman::Tracker
|
|
151
151
|
|
152
152
|
@call_index = Brakeman::CallIndex.new finder.calls
|
153
153
|
end
|
154
|
+
|
155
|
+
#Reindex call sites
|
156
|
+
#
|
157
|
+
#Takes a set of symbols which can include :templates, :models,
|
158
|
+
#or :controllers
|
159
|
+
#
|
160
|
+
#This will limit reindexing to the given sets
|
161
|
+
def reindex_call_sites locations
|
162
|
+
#If reindexing templates, models, and controllers, just redo
|
163
|
+
#everything
|
164
|
+
if locations.length == 3
|
165
|
+
return index_call_sites
|
166
|
+
end
|
167
|
+
|
168
|
+
if locations.include? :templates
|
169
|
+
@call_index.remove_template_indexes
|
170
|
+
end
|
171
|
+
|
172
|
+
classes_to_reindex = Set.new
|
173
|
+
method_sets = []
|
174
|
+
|
175
|
+
if locations.include? :models
|
176
|
+
classes_to_reindex.merge self.models.keys
|
177
|
+
method_sets << self.models
|
178
|
+
end
|
179
|
+
|
180
|
+
if locations.include? :controllers
|
181
|
+
classes_to_reindex.merge self.controllers.keys
|
182
|
+
method_sets << self.controllers
|
183
|
+
end
|
184
|
+
|
185
|
+
@call_index.remove_indexes_by_class classes_to_reindex
|
186
|
+
|
187
|
+
finder = Brakeman::FindAllCalls.new self
|
188
|
+
|
189
|
+
method_sets.each do |set|
|
190
|
+
set.each do |set_name, info|
|
191
|
+
[:private, :public, :protected].each do |visibility|
|
192
|
+
info[visibility].each do |method_name, definition|
|
193
|
+
if definition.node_type == :selfdef
|
194
|
+
method_name = "#{definition[1]}.#{method_name}"
|
195
|
+
end
|
196
|
+
|
197
|
+
finder.process_source definition, set_name, method_name
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
if locations.include? :templates
|
205
|
+
self.each_template do |name, template|
|
206
|
+
finder.process_source template[:src], nil, nil, template
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
@call_index.index_calls finder.calls
|
211
|
+
end
|
212
|
+
|
213
|
+
#Clear information related to templates.
|
214
|
+
#If :only_rendered => true, will delete templates rendered from
|
215
|
+
#controllers (but not those rendered from other templates)
|
216
|
+
def reset_templates options = { :only_rendered => false }
|
217
|
+
if options[:only_rendered]
|
218
|
+
@templates.delete_if do |name, template|
|
219
|
+
name.to_s.include? "Controller#"
|
220
|
+
end
|
221
|
+
else
|
222
|
+
@templates = {}
|
223
|
+
end
|
224
|
+
@processed = nil
|
225
|
+
@rest = nil
|
226
|
+
@template_cache.clear
|
227
|
+
end
|
228
|
+
|
229
|
+
#Clear information related to template
|
230
|
+
def reset_template name
|
231
|
+
name = name.to_sym
|
232
|
+
@templates.delete name
|
233
|
+
@processed = nil
|
234
|
+
@rest = nil
|
235
|
+
end
|
236
|
+
|
237
|
+
#Clear information related to model
|
238
|
+
def reset_model path
|
239
|
+
model_name = nil
|
240
|
+
|
241
|
+
@models.each do |name, model|
|
242
|
+
if model[:file] == path
|
243
|
+
model_name = name
|
244
|
+
break
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
@models.delete model_name
|
249
|
+
end
|
250
|
+
|
251
|
+
#Clear information about routes
|
252
|
+
def reset_routes
|
253
|
+
@routes = {}
|
254
|
+
end
|
154
255
|
end
|
data/lib/brakeman/util.rb
CHANGED
@@ -186,4 +186,110 @@ module Brakeman::Util
|
|
186
186
|
def sexp? exp
|
187
187
|
exp.is_a? Sexp
|
188
188
|
end
|
189
|
+
|
190
|
+
#Return file name related to given warning. Uses +warning.file+ if it exists
|
191
|
+
def file_for warning, tracker = nil
|
192
|
+
if tracker.nil?
|
193
|
+
tracker = @tracker || self.tracker
|
194
|
+
end
|
195
|
+
|
196
|
+
if warning.file
|
197
|
+
File.expand_path warning.file, tracker.options[:app_path]
|
198
|
+
else
|
199
|
+
case warning.warning_set
|
200
|
+
when :controller
|
201
|
+
file_by_name warning.controller, :controller, tracker
|
202
|
+
when :template
|
203
|
+
file_by_name warning.template[:name], :template, tracker
|
204
|
+
when :model
|
205
|
+
file_by_name warning.model, :model, tracker
|
206
|
+
when :warning
|
207
|
+
file_by_name warning.class, nil, tracker
|
208
|
+
else
|
209
|
+
nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
#Attempt to determine path to context file based on the reported name
|
215
|
+
#in the warning.
|
216
|
+
#
|
217
|
+
#For example,
|
218
|
+
#
|
219
|
+
# file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
|
220
|
+
def file_by_name name, type, tracker = nil
|
221
|
+
return nil unless name
|
222
|
+
string_name = name.to_s
|
223
|
+
name = name.to_sym
|
224
|
+
|
225
|
+
unless type
|
226
|
+
if string_name =~ /Controller$/
|
227
|
+
type = :controller
|
228
|
+
elsif camelize(string_name) == string_name
|
229
|
+
type = :model
|
230
|
+
else
|
231
|
+
type = :template
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
path = tracker.options[:app_path]
|
236
|
+
|
237
|
+
case type
|
238
|
+
when :controller
|
239
|
+
if tracker.controllers[name] and tracker.controllers[name][:file]
|
240
|
+
path = tracker.controllers[name][:file]
|
241
|
+
else
|
242
|
+
path += "/app/controllers/#{underscore(string_name)}.rb"
|
243
|
+
end
|
244
|
+
when :model
|
245
|
+
if tracker.models[name] and tracker.models[name][:file]
|
246
|
+
path = tracker.models[name][:file]
|
247
|
+
else
|
248
|
+
path += "/app/controllers/#{underscore(string_name)}.rb"
|
249
|
+
end
|
250
|
+
when :template
|
251
|
+
if tracker.templates[name] and tracker.templates[name][:file]
|
252
|
+
path = tracker.templates[name][:file]
|
253
|
+
elsif string_name.include? " "
|
254
|
+
name = string_name.split[0].to_sym
|
255
|
+
path = file_for tracker, name, :template
|
256
|
+
else
|
257
|
+
path = nil
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
path
|
262
|
+
end
|
263
|
+
|
264
|
+
#Return array of lines surrounding the warning location from the original
|
265
|
+
#file.
|
266
|
+
def context_for warning, tracker = nil
|
267
|
+
file = file_for warning, tracker
|
268
|
+
context = []
|
269
|
+
return context unless warning.line and file and File.exist? file
|
270
|
+
|
271
|
+
current_line = 0
|
272
|
+
start_line = warning.line - 5
|
273
|
+
end_line = warning.line + 5
|
274
|
+
|
275
|
+
start_line = 1 if start_line < 0
|
276
|
+
|
277
|
+
File.open file do |f|
|
278
|
+
f.each_line do |line|
|
279
|
+
current_line += 1
|
280
|
+
|
281
|
+
next if line.strip == ""
|
282
|
+
|
283
|
+
if current_line > end_line
|
284
|
+
break
|
285
|
+
end
|
286
|
+
|
287
|
+
if current_line >= start_line
|
288
|
+
context << [current_line, line]
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context
|
294
|
+
end
|
189
295
|
end
|