brakeman 2.1.0 → 2.1.1

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/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