brakeman 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ # 2.1.1
2
+
3
+ * New warning code for dangerous attributes in attr_accessible
4
+ * Do not warn on attr_accessible using roles
5
+ * More accurate results for model attribute warnings
6
+ * Use exit code zero with `-z` if all warnings ignored
7
+ * Respect ignored warnings in rescans
8
+ * Ignore dynamic controller names in routes
9
+ * Fix infinite loop when run as rake task (Matthew Shanley)
10
+ * Respect ignored warnings in tabs format reports
11
+
1
12
  # 2.1.0
2
13
 
3
14
  * Support non-native line endings in Gemfile.lock (Paul Deardorff)
data/README.md CHANGED
@@ -13,7 +13,7 @@ It works with Rails 2.x, 3.x, and 4.x.
13
13
 
14
14
  There is also a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenkins/Hudson.
15
15
 
16
- For even more continuous testing, try the [Guard plugin](https://github.com/oreoshake/guard-brakeman).
16
+ For even more continuous testing, try the [Guard plugin](https://github.com/guard/guard-brakeman).
17
17
 
18
18
  # Homepage/News
19
19
 
data/bin/brakeman CHANGED
@@ -78,7 +78,7 @@ begin
78
78
  tracker = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
79
79
 
80
80
  #Return error code if --exit-on-warn is used and warnings were found
81
- if options[:exit_on_warn] and not tracker.warnings.empty?
81
+ if options[:exit_on_warn] and not tracker.filtered_warnings.empty?
82
82
  exit Brakeman::Warnings_Found_Exit_Code
83
83
  end
84
84
  end
data/lib/brakeman.rb CHANGED
@@ -153,7 +153,7 @@ module Brakeman
153
153
  end
154
154
  end
155
155
  end
156
-
156
+
157
157
  def self.get_formats_from_output_format output_format
158
158
  case output_format
159
159
  when :html, :to_html
@@ -171,7 +171,7 @@ module Brakeman
171
171
  end
172
172
  end
173
173
  private_class_method :get_formats_from_output_format
174
-
174
+
175
175
  def self.get_formats_from_output_files output_files
176
176
  output_files.map do |output_file|
177
177
  case output_file
@@ -196,7 +196,7 @@ module Brakeman
196
196
  def self.list_checks
197
197
  require 'brakeman/scanner'
198
198
  format_length = 30
199
-
199
+
200
200
  $stderr.puts "Available Checks:"
201
201
  $stderr.puts "-" * format_length
202
202
  Checks.checks.each do |check|
@@ -307,7 +307,7 @@ module Brakeman
307
307
 
308
308
  tracker
309
309
  end
310
-
310
+
311
311
  def self.write_report_to_files tracker, output_files
312
312
  output_files.each_with_index do |output_file, idx|
313
313
  File.open output_file, "w" do |f|
@@ -317,7 +317,7 @@ module Brakeman
317
317
  end
318
318
  end
319
319
  private_class_method :write_report_to_files
320
-
320
+
321
321
  def self.write_report_to_formats tracker, output_formats
322
322
  output_formats.each do |output_format|
323
323
  puts tracker.report.format(output_format)
@@ -375,7 +375,7 @@ module Brakeman
375
375
  Brakeman::Differ.new(new_results, previous_results).diff
376
376
  end
377
377
 
378
- def self.load_dependency name
378
+ def self.load_brakeman_dependency name
379
379
  return if @loaded_dependencies.include? name
380
380
 
381
381
  begin
@@ -364,7 +364,11 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
364
364
  if @safe_input_attributes.include? method
365
365
  false
366
366
  elsif call? target and not method.to_s[-1,1] == "?"
367
- has_immediate_model? target, out
367
+ if res = has_immediate_model?(target, out)
368
+ exp
369
+ else
370
+ false
371
+ end
368
372
  elsif model_name? target
369
373
  exp
370
374
  else
@@ -429,28 +433,6 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
429
433
  end
430
434
  end
431
435
 
432
- #Finds entire method call chain where +target+ is a target in the chain
433
- def find_chain exp, target
434
- return unless sexp? exp
435
-
436
- case exp.node_type
437
- when :output, :format
438
- find_chain exp.value, target
439
- when :call
440
- if exp == target or include_target? exp, target
441
- return exp
442
- end
443
- else
444
- exp.each do |e|
445
- if sexp? e
446
- res = find_chain e, target
447
- return res if res
448
- end
449
- end
450
- nil
451
- end
452
- end
453
-
454
436
  #Returns true if +target+ is in +exp+
455
437
  def include_target? exp, target
456
438
  return false unless call? exp
@@ -107,12 +107,10 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
107
107
  :link_path => "content_tag"
108
108
 
109
109
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
110
- method = match[2]
111
-
112
- unless IGNORE_MODEL_METHODS.include? method
110
+ unless IGNORE_MODEL_METHODS.include? match.method
113
111
  add_result result
114
112
 
115
- if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
113
+ if likely_model_attribute? match
116
114
  confidence = CONFIDENCE[:high]
117
115
  else
118
116
  confidence = CONFIDENCE[:med]
@@ -122,7 +122,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
122
122
  unless IGNORE_MODEL_METHODS.include? method
123
123
  add_result exp
124
124
 
125
- if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
125
+ if likely_model_attribute? match
126
126
  confidence = CONFIDENCE[:high]
127
127
  else
128
128
  confidence = CONFIDENCE[:med]
@@ -138,12 +138,17 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
138
138
  warning_code = :xss_to_json
139
139
  end
140
140
 
141
- code = find_chain out, match
141
+ code = if match == out
142
+ nil
143
+ else
144
+ match
145
+ end
146
+
142
147
  warn :template => @current_template,
143
148
  :warning_type => "Cross Site Scripting",
144
149
  :warning_code => warning_code,
145
150
  :message => message,
146
- :code => code,
151
+ :code => match,
147
152
  :confidence => confidence,
148
153
  :link_path => link_path
149
154
  end
@@ -153,6 +158,19 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
153
158
  end
154
159
  end
155
160
 
161
+ #Call already involves a model, but might not be acting on a record
162
+ def likely_model_attribute? exp
163
+ return false unless call? exp
164
+
165
+ method = exp.method
166
+
167
+ if MODEL_METHODS.include? method or method.to_s.start_with? "find_by_"
168
+ true
169
+ else
170
+ likely_model_attribute? exp.target
171
+ end
172
+ end
173
+
156
174
  #Process an output Sexp
157
175
  def process_output exp
158
176
  process exp.value.dup
@@ -82,7 +82,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
82
82
  return false if IGNORE_MODEL_METHODS.include? method
83
83
 
84
84
  confidence = CONFIDENCE[:med]
85
- confidence = CONFIDENCE[:high] if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
85
+ confidence = CONFIDENCE[:high] if likely_model_attribute? match
86
86
  warn_xss(result, "Unescaped model attribute in link_to", match, confidence)
87
87
  end
88
88
 
@@ -1,42 +1,49 @@
1
1
  require 'brakeman/checks/base_check'
2
2
 
3
- # Author: Paul Deardorff (themetric)
4
- # Checks models to see if important foreign keys
5
- # or attributes are exposed as attr_accessible when
6
- # they probably shouldn't be.
3
+ # Author: Paul Deardorff (themetric)
4
+ # Checks models to see if important foreign keys
5
+ # or attributes are exposed as attr_accessible when
6
+ # they probably shouldn't be.
7
7
 
8
8
  class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
9
9
  Brakeman::Checks.add self
10
10
 
11
11
  @description = "Reports models which have dangerous attributes defined under the attr_accessible whitelist."
12
12
 
13
- SUSP_ATTRS = {
14
- /admin/ => CONFIDENCE[:high], # Very dangerous unless some Rails authorization used
15
- /role/ => CONFIDENCE[:med],
16
- /banned/ => CONFIDENCE[:med],
17
- :account_id => CONFIDENCE[:high],
18
- /\S*_id(s?)\z/ => CONFIDENCE[:low] # All other foreign keys have weak/low confidence
19
- }
13
+ SUSP_ATTRS = [
14
+ [/admin/, CONFIDENCE[:high]], # Very dangerous unless some Rails authorization used
15
+ [/role/, CONFIDENCE[:med]],
16
+ [/banned/, CONFIDENCE[:med]],
17
+ [:account_id, CONFIDENCE[:high]],
18
+ [/\S*_id(s?)\z/, CONFIDENCE[:low]] # All other foreign keys have weak/low confidence
19
+ ]
20
20
 
21
21
  def run_check
22
22
  check_models do |name, model|
23
- accessible_attrs = model[:attr_accessible]
24
- accessible_attrs.each do |attribute|
23
+ model[:attr_accessible].each do |attribute|
24
+ next if role_limited? model, attribute
25
+
25
26
  SUSP_ATTRS.each do |susp_attr, confidence|
26
- if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute
27
- warn :model => name,
28
- :file => model[:file],
29
- :warning_type => "Mass Assignment",
30
- :warning_code => :mass_assign_call,
31
- :message => "Potentially dangerous attribute #{attribute} available for mass assignment.",
32
- :confidence => confidence
33
- break # Prevent from matching single attr multiple times
34
- end
35
- end
36
- end
27
+ if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute
28
+ warn :model => name,
29
+ :file => model[:file],
30
+ :warning_type => "Mass Assignment",
31
+ :warning_code => :dangerous_attr_accessible,
32
+ :message => "Potentially dangerous attribute #{attribute} available for mass assignment.",
33
+ :confidence => confidence
34
+ break # Prevent from matching single attr multiple times
35
+ end
36
+ end
37
+ end
37
38
  end
38
39
  end
39
40
 
41
+ def role_limited? model, attribute
42
+ role_accessible = model[:options][:role_accessible]
43
+ return if role_accessible.nil?
44
+ role_accessible.include? attribute
45
+ end
46
+
40
47
  def check_models
41
48
  tracker.models.each do |name, model|
42
49
  if !model[:attr_accessible].nil?
@@ -44,5 +51,4 @@ class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
44
51
  end
45
52
  end
46
53
  end
47
-
48
54
  end
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'erubis'
1
+ Brakeman.load_brakeman_dependency 'erubis'
2
2
 
3
3
  #Erubis processor which ignores any output which is plain text.
4
4
  class Brakeman::ScannerErubis < Erubis::Eruby
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'erubis'
1
+ Brakeman.load_brakeman_dependency 'erubis'
2
2
 
3
3
  #This is from the rails_xss plugin for Rails 2
4
4
  class Brakeman::Rails2XSSPluginErubis < ::Erubis::Eruby
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'erubis'
1
+ Brakeman.load_brakeman_dependency 'erubis'
2
2
 
3
3
  #This is from Rails 3 version of the Erubis handler
4
4
  class Brakeman::Rails3Erubis < ::Erubis::Eruby
@@ -255,13 +255,16 @@ class Brakeman::Rails3RoutesProcessor < Brakeman::BaseProcessor
255
255
  end
256
256
 
257
257
  def process_controller_block exp
258
- self.current_controller = exp.block_call.first_arg.value
258
+ if string? exp or symbol? exp
259
+ self.current_controller = exp.block_call.first_arg.value
259
260
 
260
- in_controller_block do
261
- process exp[-1] if exp[-1]
261
+ in_controller_block do
262
+ process exp[-1] if exp[-1]
263
+ end
264
+
265
+ @current_controller = nil
262
266
  end
263
267
 
264
- @current_controller = nil
265
268
  exp
266
269
  end
267
270
 
@@ -85,6 +85,9 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
85
85
  exp.each_arg do |e|
86
86
  if node_type? e, :lit
87
87
  args << e.value
88
+ elsif hash? e
89
+ @model[:options][:role_accessible] ||= []
90
+ @model[:options][:role_accessible].concat args
88
91
  end
89
92
  end
90
93
 
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'highline'
1
+ Brakeman.load_brakeman_dependency 'highline'
2
2
 
3
3
  module Brakeman
4
4
  class InteractiveIgnorer
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'csv'
1
+ Brakeman.load_brakeman_dependency 'csv'
2
2
  require "brakeman/report/initializers/faster_csv"
3
3
  require "brakeman/report/report_table"
4
4
 
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'multi_json'
1
+ Brakeman.load_brakeman_dependency 'multi_json'
2
2
  require 'brakeman/report/initializers/multi_json'
3
3
 
4
4
  class Brakeman::Report::JSON < Brakeman::Report::Base
@@ -1,4 +1,4 @@
1
- Brakeman.load_dependency 'terminal-table'
1
+ Brakeman.load_brakeman_dependency 'terminal-table'
2
2
 
3
3
  class Brakeman::Report::Table < Brakeman::Report::Base
4
4
  def generate_report
@@ -2,10 +2,10 @@
2
2
  #https://github.com/presidentbeef/brakeman-jenkins-plugin
3
3
  class Brakeman::Report::Tabs < Brakeman::Report::Base
4
4
  def generate_report
5
- [[:warnings, "General"], [:controller_warnings, "Controller"],
5
+ [[:generic_warnings, "General"], [:controller_warnings, "Controller"],
6
6
  [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
7
7
 
8
- checks.send(meth).map do |w|
8
+ self.send(meth).map do |w|
9
9
  line = w.line || 0
10
10
  w.warning_type.gsub!(/[^\w\s]/, ' ')
11
11
  "#{warning_file(w, :absolute)}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
@@ -1,6 +1,7 @@
1
1
  require 'brakeman/scanner'
2
2
  require 'terminal-table'
3
3
  require 'brakeman/util'
4
+ require 'brakeman/differ'
4
5
 
5
6
  #Class for rescanning changed files after an initial scan
6
7
  class Brakeman::Rescanner < Brakeman::Scanner
@@ -13,7 +14,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
13
14
  super(options, processor)
14
15
 
15
16
  @paths = changed_files.map {|f| @app_tree.expand_path(f) }
16
- @old_results = tracker.checks #Old warnings from previous scan
17
+ @old_results = tracker.filtered_warnings #Old warnings from previous scan
17
18
  @changes = nil #True if files had to be rescanned
18
19
  @reindex = Set.new
19
20
  end
@@ -367,7 +368,6 @@ class Brakeman::RescanReport
367
368
  def initialize old_results, tracker
368
369
  @tracker = tracker
369
370
  @old_results = old_results
370
- @new_results = tracker.checks
371
371
  @all_warnings = nil
372
372
  @diff = nil
373
373
  end
@@ -379,7 +379,7 @@ class Brakeman::RescanReport
379
379
 
380
380
  #Returns an array of all warnings found
381
381
  def all_warnings
382
- @all_warnings ||= new_results.all_warnings
382
+ @all_warnings ||= @tracker.filtered_warnings
383
383
  end
384
384
 
385
385
  #Returns an array of warnings which were in the old report but are not in the
@@ -401,7 +401,7 @@ class Brakeman::RescanReport
401
401
 
402
402
  #Returns a hash of arrays for :new and :fixed warnings
403
403
  def diff
404
- @diff ||= @new_results.diff(@old_results)
404
+ @diff ||= Brakeman::Differ.new(all_warnings, @old_results).diff
405
405
  end
406
406
 
407
407
  #Returns an array of warnings which were in the old report and the new report
@@ -282,14 +282,14 @@ class Brakeman::Scanner
282
282
 
283
283
  parsed = parse_ruby src
284
284
  elsif type == :haml
285
- Brakeman.load_dependency 'haml'
286
- Brakeman.load_dependency 'sass'
285
+ Brakeman.load_brakeman_dependency 'haml'
286
+ Brakeman.load_brakeman_dependency 'sass'
287
287
 
288
288
  src = Haml::Engine.new(text,
289
289
  :escape_html => !!tracker.config[:escape_html]).precompiled
290
290
  parsed = parse_ruby src
291
291
  elsif type == :slim
292
- Brakeman.load_dependency 'slim'
292
+ Brakeman.load_brakeman_dependency 'slim'
293
293
 
294
294
  src = Slim::Template.new(:disable_capture => true,
295
295
  :generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template
@@ -156,6 +156,16 @@ class Brakeman::Tracker
156
156
  self.checks.all_warnings
157
157
  end
158
158
 
159
+ def filtered_warnings
160
+ if self.ignored_filter
161
+ self.warnings.reject do |w|
162
+ self.ignored_filter.ignored? w
163
+ end
164
+ else
165
+ self.warnings
166
+ end
167
+ end
168
+
159
169
  def index_call_sites
160
170
  finder = Brakeman::FindAllCalls.new self
161
171
 
data/lib/brakeman/util.rb CHANGED
@@ -385,7 +385,7 @@ module Brakeman::Util
385
385
 
386
386
  def truncate_table str
387
387
  @terminal_width ||= if $stdin && $stdin.tty?
388
- Brakeman.load_dependency 'highline'
388
+ Brakeman.load_brakeman_dependency 'highline'
389
389
  ::HighLine.new.terminal_size[0]
390
390
  else
391
391
  80
@@ -403,7 +403,7 @@ module Brakeman::Util
403
403
 
404
404
  # rely on Terminal::Table to build the structure, extract the data out in CSV format
405
405
  def table_to_csv table
406
- Brakeman.load_dependency 'terminal-table'
406
+ Brakeman.load_brakeman_dependency 'terminal-table'
407
407
  output = CSV.generate_line(table.headings.cells.map{|cell| cell.to_s.strip})
408
408
  table.rows.each do |row|
409
409
  output << CSV.generate_line(row.cells.map{|cell| cell.to_s.strip})
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "2.1.0"
2
+ Version = "2.1.1"
3
3
  end
@@ -59,7 +59,8 @@ module Brakeman::WarningCodes
59
59
  :CVE_2013_1855 => 56,
60
60
  :CVE_2013_1856 => 57,
61
61
  :CVE_2013_1857 => 58,
62
- :unsafe_symbol_creation => 59
62
+ :unsafe_symbol_creation => 59,
63
+ :dangerous_attr_accessible => 60
63
64
  }
64
65
 
65
66
  def self.code name
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 1
9
- - 0
10
- version: 2.1.0
9
+ - 1
10
+ version: 2.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Justin Collins
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-07-17 00:00:00 Z
18
+ date: 2013-08-21 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: ruby_parser