brakeman 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 options
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
- begin
128
- @processor.process_initializer(f, parse_ruby(File.read(f)))
129
- rescue Racc::ParseError => e
130
- tracker.error e, "could not parse #{f}. There is probably a typo in the file. Test it with 'ruby_parse #{f}'"
131
- rescue Exception => e
132
- tracker.error e.exception(e.message + "\nWhile processing #{f}"), e.backtrace
133
- end
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
- begin
158
- @processor.process_lib parse_ruby(File.read(f)), f
159
- rescue Racc::ParseError => e
160
- tracker.error e, "could not parse #{f}. There is probably a typo in the file. Test it with 'ruby_parse #{f}'"
161
- rescue Exception => e
162
- tracker.error e.exception(e.message + "\nWhile processing #{f}"), e.backtrace
163
- end
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
- begin
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 |f|
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
- type = f.match(/.*\.(erb|haml|rhtml)$/)[1].to_sym
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
- begin
324
- @processor.process_model(parse_ruby(File.read(f)), f)
325
- rescue Racc::ParseError => e
326
- tracker.error e, "could not parse #{f}"
327
- rescue Exception => e
328
- tracker.error e.exception(e.message + "\nWhile processing #{f}"), e.backtrace
329
- end
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
- if RUBY_1_9
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
 
@@ -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
- index_calls unless @call_index
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