brakeman 1.3.0 → 1.4.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/README.md CHANGED
@@ -93,6 +93,14 @@ By default, Brakeman will return 0 as an exit code unless something went very wr
93
93
 
94
94
  brakeman -z
95
95
 
96
+ To skip certain files that Brakeman may have trouble parsing, use:
97
+
98
+ brakeman --skip-files file1,file2,etc
99
+
100
+ Brakeman will raise warnings on models that use `attr_protected`. To suppress these warnings:
101
+
102
+ brakeman --ignore-protected
103
+
96
104
  # Warning information
97
105
 
98
106
  See WARNING_TYPES for more information on the warnings reported by this tool.
@@ -131,7 +139,7 @@ The `-c` option can be used to specify a configuration file to use.
131
139
 
132
140
  The MIT License
133
141
 
134
- Copyright (c) 2010, YELLOWPAGES.COM, LLC
142
+ Copyright (c) 2010-2012, YELLOWPAGES.COM, LLC
135
143
 
136
144
  Permission is hereby granted, free of charge, to any person obtaining a copy
137
145
  of this software and associated documentation files (the "Software"), to deal
data/bin/brakeman CHANGED
@@ -42,9 +42,9 @@ unless options[:app_path]
42
42
  end
43
43
 
44
44
  #Run scan and output a report
45
- clean = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
45
+ tracker = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
46
46
 
47
47
  #Return error code if --exit-on-warn is used and warnings were found
48
- if options[:exit_on_warn] and not clean
48
+ if options[:exit_on_warn] and not tracker.checks.all_warnings.empty?
49
49
  exit Brakeman::Warnings_Found_Exit_Code
50
50
  end
data/lib/brakeman.rb CHANGED
@@ -262,15 +262,6 @@ module Brakeman
262
262
  puts tracker.report.send(options[:output_format])
263
263
  end
264
264
 
265
- if options[:exit_on_warn]
266
- tracker.checks.all_warnings.each do |warning|
267
- next if warning.confidence > options[:min_confidence]
268
- return false
269
- end
270
-
271
- return true
272
- end
273
-
274
265
  tracker
275
266
  end
276
267
 
@@ -20,7 +20,13 @@ class Brakeman::Checks
20
20
  end
21
21
 
22
22
  #No need to use this directly.
23
- def initialize
23
+ def initialize options = { }
24
+ if options[:min_confidence]
25
+ @min_confidence = options[:min_confidence]
26
+ else
27
+ @min_confidence = Brakeman.get_defaults[:min_confidence]
28
+ end
29
+
24
30
  @warnings = []
25
31
  @template_warnings = []
26
32
  @model_warnings = []
@@ -31,18 +37,22 @@ class Brakeman::Checks
31
37
  #Add Warning to list of warnings to report.
32
38
  #Warnings are split into four different arrays
33
39
  #for template, controller, model, and generic warnings.
40
+ #
41
+ #Will not add warnings which are below the minimum confidence level.
34
42
  def add_warning warning
35
- case warning.warning_set
36
- when :template
37
- @template_warnings << warning
38
- when :warning
39
- @warnings << warning
40
- when :controller
41
- @controller_warnings << warning
42
- when :model
43
- @model_warnings << warning
44
- else
45
- raise "Unknown warning: #{warning.warning_set}"
43
+ unless warning.confidence > @min_confidence
44
+ case warning.warning_set
45
+ when :template
46
+ @template_warnings << warning
47
+ when :warning
48
+ @warnings << warning
49
+ when :controller
50
+ @controller_warnings << warning
51
+ when :model
52
+ @model_warnings << warning
53
+ else
54
+ raise "Unknown warning: #{warning.warning_set}"
55
+ end
46
56
  end
47
57
  end
48
58
 
@@ -80,7 +90,7 @@ class Brakeman::Checks
80
90
 
81
91
  #Run checks sequentially
82
92
  def self.run_checks_sequential tracker
83
- check_runner = self.new
93
+ check_runner = self.new :min_confidence => tracker.options[:min_confidence]
84
94
 
85
95
  @checks.each do |c|
86
96
  check_name = get_check_name c
@@ -111,7 +121,7 @@ class Brakeman::Checks
111
121
  def self.run_checks_parallel tracker
112
122
  threads = []
113
123
 
114
- check_runner = self.new
124
+ check_runner = self.new :min_confidence => tracker.options[:min_confidence]
115
125
 
116
126
  @checks.each do |c|
117
127
  check_name = get_check_name c
@@ -71,6 +71,8 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
71
71
  end
72
72
 
73
73
  def check_for_immediate_xss exp
74
+ return if duplicate? exp
75
+
74
76
  if exp[0] == :output
75
77
  out = exp[1]
76
78
  elsif exp[0] == :escaped_output and raw_call? exp
@@ -79,7 +81,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
79
81
 
80
82
  type, match = has_immediate_user_input? out
81
83
 
82
- if type and not duplicate? exp
84
+ if type
83
85
  add_result exp
84
86
  case type
85
87
  when :params
@@ -102,7 +104,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
102
104
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out)
103
105
  method = match[2]
104
106
 
105
- unless duplicate? out or IGNORE_MODEL_METHODS.include? method
107
+ unless IGNORE_MODEL_METHODS.include? method
106
108
  add_result out
107
109
 
108
110
  if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
@@ -34,6 +34,8 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
34
34
  end
35
35
 
36
36
  def process_result result
37
+ return if duplicate? result
38
+
37
39
  #Have to make a copy of this, otherwise it will be changed to
38
40
  #an ignored method call by the code above.
39
41
  call = result[:call] = result[:call].dup
@@ -58,19 +60,15 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
58
60
  message = "Unescaped user input value in link_to"
59
61
  end
60
62
 
61
- unless duplicate? result
62
- add_result result
63
-
64
- warn :result => result,
65
- :warning_type => "Cross Site Scripting",
66
- :message => message,
67
- :confidence => CONFIDENCE[:high]
68
- end
69
-
63
+ add_result result
64
+ warn :result => result,
65
+ :warning_type => "Cross Site Scripting",
66
+ :message => message,
67
+ :confidence => CONFIDENCE[:high]
70
68
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(first_arg)
71
69
  method = match[2]
72
70
 
73
- unless duplicate? result or IGNORE_MODEL_METHODS.include? method
71
+ unless IGNORE_MODEL_METHODS.include? method
74
72
  add_result result
75
73
 
76
74
  if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
@@ -92,7 +90,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
92
90
  message = "Unescaped parameter value in link_to"
93
91
  end
94
92
 
95
- if message and not duplicate? result
93
+ if message
96
94
  add_result result
97
95
 
98
96
  warn :result => result,
@@ -0,0 +1,90 @@
1
+ require 'brakeman/checks/check_cross_site_scripting'
2
+
3
+ #Checks for calls to link_to which pass in potentially hazardous data
4
+ #to the second argument. While this argument must be html_safe to not break
5
+ #the html, it must also be url safe as determined by calling a
6
+ #:url_safe_method. This prevents attacks such as javascript:evil() or
7
+ #data:<encoded XSS> which is html_safe, but not safe as an href
8
+ #Props to Nick Green for the idea.
9
+ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
10
+ Brakeman::Checks.add self
11
+
12
+ @description = "Checks to see if values used for hrefs are sanitized using a :url_safe_method to protect against javascript:/data: XSS"
13
+
14
+ def run_check
15
+ @ignore_methods = Set.new([:button_to, :check_box,
16
+ :field_field, :fields_for, :hidden_field,
17
+ :hidden_field, :hidden_field_tag, :image_tag, :label,
18
+ :mail_to, :radio_button, :select,
19
+ :submit_tag, :text_area, :text_field,
20
+ :text_field_tag, :url_encode, :url_for,
21
+ :will_paginate] ).merge(tracker.options[:url_safe_methods] || [])
22
+
23
+ @models = tracker.models.keys
24
+ @inspect_arguments = tracker.options[:check_arguments]
25
+
26
+ methods = tracker.find_call :target => false, :method => :link_to
27
+ methods.each do |call|
28
+ process_result call
29
+ end
30
+ end
31
+
32
+ def process_result result
33
+ #Have to make a copy of this, otherwise it will be changed to
34
+ #an ignored method call by the code above.
35
+ call = result[:call] = result[:call].dup
36
+ @matched = false
37
+ url_arg = process call[3][2]
38
+
39
+ #Ignore situations where the href is an interpolated string
40
+ #with something before the user input
41
+ return if node_type?(url_arg, :string_interp) && !url_arg[1].chomp.empty?
42
+
43
+ type, match = has_immediate_user_input? url_arg
44
+
45
+ if type
46
+ case type
47
+ when :params
48
+ message = "Unsafe parameter value in link_to href"
49
+ when :cookies
50
+ message = "Unsafe cookie value in link_to href"
51
+ else
52
+ message = "Unsafe user input value in link_to href"
53
+ end
54
+
55
+ unless duplicate? result
56
+ add_result result
57
+ warn :result => result,
58
+ :warning_type => "Cross Site Scripting",
59
+ :message => message,
60
+ :confidence => CONFIDENCE[:high]
61
+ end
62
+ elsif has_immediate_model? url_arg
63
+
64
+ # Decided NOT warn on models. polymorphic_path is called it a model is
65
+ # passed to link_to (which passes it to url_for)
66
+
67
+ elsif hash? url_arg
68
+
69
+ # url_for uses the key/values pretty carefully and I don't see a risk.
70
+ # IF you have default routes AND you accept user input for :controller
71
+ # and :only_path, then MAYBE you could trigger a javascript:/data:
72
+ # attack.
73
+
74
+ elsif @matched
75
+ if @matched == :model and not tracker.options[:ignore_model_output]
76
+ message = "Unsafe model attribute in link_to href"
77
+ elsif @matched == :params
78
+ message = "Unsafe parameter value in link_to href"
79
+ end
80
+
81
+ if message and not duplicate? result
82
+ add_result result
83
+ warn :result => result,
84
+ :warning_type => "Cross Site Scripting",
85
+ :message => message,
86
+ :confidence => CONFIDENCE[:med]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -30,7 +30,8 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
30
30
  :update_attributes,
31
31
  :update_attributes!,
32
32
  :create,
33
- :create!]
33
+ :create!,
34
+ :build]
34
35
 
35
36
  Brakeman.debug "Processing possible mass assignment calls"
36
37
  calls.each do |result|
@@ -181,7 +181,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
181
181
  arg.each do |exp|
182
182
  #For now, don't warn on interpolation of Model.table_name
183
183
  #but check for other 'safe' things in the future
184
- if sexp? exp and (exp.node_type == :string_eval or exp.node_type == :evstr)
184
+ if node_type? exp, :string_eval, :evstr
185
185
  if call? exp[1] and (model_name?(exp[1][1]) or exp[1][1].nil?) and exp[1][2] == :table_name
186
186
  return false
187
187
  end
@@ -84,6 +84,11 @@ module Brakeman::Options
84
84
  options[:safe_methods].merge methods.map {|e| e.to_sym }
85
85
  end
86
86
 
87
+ opts.on "--url-safe-methods method1,method2,etc", Array, "Do not warn of XSS if the link_to href parameter is wrapped in a safe method" do |methods|
88
+ options[:url_safe_methods] ||= Set.new
89
+ options[:url_safe_methods].merge methods.map {|e| e.to_sym }
90
+ end
91
+
87
92
  opts.on "--skip-files file1,file2,etc", Array, "Skip processing of these files" do |files|
88
93
  options[:skip_files] ||= Set.new
89
94
  options[:skip_files].merge files.map {|f| f.to_sym }
@@ -85,7 +85,7 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
85
85
  else
86
86
  Brakeman.debug "[Notice] Layout not found: #{name}"
87
87
  end
88
- elsif sexp? args[-1] and (args[-1][0] == :nil or args[-1][0] == :false)
88
+ elsif node_type? args[-1], :nil, :false
89
89
  #layout :false or layout nil
90
90
  @controller[:layout] = false
91
91
  end
@@ -181,7 +181,7 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
181
181
  block_variable = :temp
182
182
  end
183
183
 
184
- if sexp? exp[3] and exp[3].node_type == :block
184
+ if node_type? exp[3], :block
185
185
  block_inner = exp[3][1..-1]
186
186
  else
187
187
  block_inner = [exp[3]]
@@ -68,7 +68,7 @@ class Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor
68
68
  res = process e
69
69
  if res.empty? or res == ignore
70
70
  nil
71
- elsif sexp? res and res.node_type == :lvar and res[1] == :_erbout
71
+ elsif node_type?(res, :lvar) and res[1] == :_erbout
72
72
  nil
73
73
 
74
74
  else
@@ -138,7 +138,7 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
138
138
  #Returns method chain as an array
139
139
  #For example, User.human.alive.all would return [:User, :human, :alive, :all]
140
140
  def get_chain call
141
- if sexp? call and (call.node_type == :call or call.node_type == :attrasgn)
141
+ if node_type? call, :call, :attrasgn
142
142
  get_chain(call[1]) + [call[2]]
143
143
  else
144
144
  [get_target(call)]
@@ -107,7 +107,7 @@ class Brakeman::FindCall < Brakeman::BaseProcessor
107
107
  # User.find(:first, :conditions => "user = '#{params['user']}').name
108
108
  #
109
109
  #A search for User.find will not match this unless @in_depth is true.
110
- if @in_depth and sexp? exp[1] and exp[1][0] == :call
110
+ if @in_depth and node_type? exp[1], :call
111
111
  process exp[1]
112
112
  end
113
113
 
@@ -103,7 +103,7 @@ class Brakeman::Rails2ConfigProcessor < Brakeman::BaseProcessor
103
103
  #
104
104
  # [:action_controller, :session_store]
105
105
  def get_rails_config exp
106
- if sexp? exp and exp.node_type == :attrasgn
106
+ if node_type? exp, :attrasgn
107
107
  attribute = exp[2].to_s[0..-2].to_sym
108
108
  get_rails_config(exp[1]) << attribute
109
109
  elsif call? exp
@@ -89,7 +89,7 @@ class Brakeman::Rails2RoutesProcessor < Brakeman::BaseProcessor
89
89
  process_resource_options exp[-1]
90
90
  else
91
91
  exp.each do |argument|
92
- if sexp? argument and argument.node_type == :lit
92
+ if node_type? argument, :lit
93
93
  self.current_controller = exp[0][1]
94
94
  add_resources_routes
95
95
  process_resource_options exp[-1]
@@ -165,7 +165,7 @@ class Brakeman::Rails2RoutesProcessor < Brakeman::BaseProcessor
165
165
  process_resource_options exp[-1]
166
166
  else
167
167
  exp.each do |argument|
168
- if sexp? argument and argument.node_type == :lit
168
+ if node_type? argument, :lit
169
169
  self.current_controller = pluralize(exp[0][1].to_s)
170
170
  add_resource_routes
171
171
  process_resource_options exp[-1]
@@ -29,7 +29,7 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BaseProcessor
29
29
 
30
30
  #Look for MyApp::Application.configure do ... end
31
31
  def process_iter exp
32
- if sexp?(exp[1][1]) and exp[1][1][0] == :colon2 and exp[1][1][2] == :Application
32
+ if node_type?(exp[1][1], :colon2) and exp[1][1][2] == :Application
33
33
  @inside_config = true
34
34
  process exp[-1] if sexp? exp[-1]
35
35
  @inside_config = false
@@ -100,7 +100,7 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BaseProcessor
100
100
  #
101
101
  # [:action_controller, :session_store]
102
102
  def get_rails_config exp
103
- if sexp? exp and exp.node_type == :attrasgn
103
+ if node_type? exp, :attrasgn
104
104
  attribute = exp[2].to_s[0..-2].to_sym
105
105
  get_rails_config(exp[1]) << attribute
106
106
  elsif call? exp
@@ -75,7 +75,7 @@ module Brakeman::RenderHelper
75
75
  #Process layout
76
76
  if string? options[:layout]
77
77
  process_template "layouts/#{options[:layout][1]}", nil
78
- elsif sexp? options[:layout] and options[:layout][0] == :false
78
+ elsif node_type? options[:layout], :false
79
79
  #nothing
80
80
  elsif not template[:name].to_s.match(/[^\/_][^\/]+$/)
81
81
  #Don't do this for partials
@@ -40,7 +40,7 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
40
40
 
41
41
  #Check for e.g. Model.find.each do ... end
42
42
  if method == :each and args and block and model = get_model_target(target)
43
- if sexp? args and args.node_type == :lasgn
43
+ if node_type? args, :lasgn
44
44
  if model == target[1]
45
45
  env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, model, :new, Sexp.new(:arglist))
46
46
  else
@@ -50,7 +50,7 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
50
50
  process block if sexp? block
51
51
  end
52
52
  elsif FORM_METHODS.include? method
53
- if sexp? args and args.node_type == :lasgn
53
+ if node_type? args, :lasgn
54
54
  env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
55
55
 
56
56
  process block if sexp? block
@@ -102,7 +102,6 @@ class Brakeman::Report
102
102
  def generate_warnings html = false
103
103
  table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"])
104
104
  checks.warnings.each do |warning|
105
- next if warning.confidence > tracker.options[:min_confidence]
106
105
  w = warning.to_row
107
106
 
108
107
  if html
@@ -132,7 +131,6 @@ class Brakeman::Report
132
131
  unless checks.template_warnings.empty?
133
132
  table = Ruport::Data::Table(["Confidence", "Template", "Warning Type", "Message"])
134
133
  checks.template_warnings.each do |warning|
135
- next if warning.confidence > tracker.options[:min_confidence]
136
134
  w = warning.to_row :template
137
135
 
138
136
  if html
@@ -163,7 +161,6 @@ class Brakeman::Report
163
161
  unless checks.model_warnings.empty?
164
162
  table = Ruport::Data::Table(["Confidence", "Model", "Warning Type", "Message"])
165
163
  checks.model_warnings.each do |warning|
166
- next if warning.confidence > tracker.options[:min_confidence]
167
164
  w = warning.to_row :model
168
165
 
169
166
  if html
@@ -194,7 +191,6 @@ class Brakeman::Report
194
191
  unless checks.controller_warnings.empty?
195
192
  table = Ruport::Data::Table(["Confidence", "Controller", "Warning Type", "Message"])
196
193
  checks.controller_warnings.each do |warning|
197
- next if warning.confidence > tracker.options[:min_confidence]
198
194
  w = warning.to_row :controller
199
195
 
200
196
  if html
@@ -483,13 +479,10 @@ class Brakeman::Report
483
479
  checks.template_warnings].each do |warnings|
484
480
 
485
481
  warnings.each do |warning|
486
- unless warning.confidence > tracker.options[:min_confidence]
482
+ summary[warning.warning_type.to_s] += 1
487
483
 
488
- summary[warning.warning_type.to_s] += 1
489
-
490
- if warning.confidence == 0
491
- high_confidence_warnings += 1
492
- end
484
+ if warning.confidence == 0
485
+ high_confidence_warnings += 1
493
486
  end
494
487
  end
495
488
  end
@@ -586,7 +579,6 @@ class Brakeman::Report
586
579
  [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
587
580
 
588
581
  checks.send(meth).map do |w|
589
- next if w.confidence > tracker.options[:min_confidence]
590
582
  line = w.line || 0
591
583
  w.warning_type.gsub!(/[^\w\s]/, ' ')
592
584
  "#{file_for w}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
@@ -618,4 +610,10 @@ class Brakeman::Report
618
610
 
619
611
  report
620
612
  end
613
+
614
+ def to_json
615
+ require 'json'
616
+
617
+ @checks.all_warnings.map { |w| w.to_hash }.to_json
618
+ end
621
619
  end
@@ -256,31 +256,34 @@ class Brakeman::RescanReport
256
256
  #Output total, fixed, and new warnings
257
257
  def to_s(verbose = false)
258
258
  if !verbose
259
- <<-OUTPUT
259
+ <<-OUTPUT
260
260
  Total warnings: #{all_warnings.length}
261
261
  Fixed warnings: #{fixed_warnings.length}
262
262
  New warnings: #{new_warnings.length}
263
- OUTPUT
263
+ OUTPUT
264
264
  else
265
- existing_warnings = all_warnings - new_warnings
266
-
267
- "".tap do |out|
268
- {:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
269
- if warnings.length > 0
270
- out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
271
- table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"])
272
- warnings.sort_by{|w| w.confidence}.each do |warning|
273
- next if warning.confidence > @tracker.options[:min_confidence]
274
- w = warning.to_row
275
- w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]] if w["Confidence"].is_a?(Numeric)
276
- table << warning.to_row
277
- end
278
- out << table.to_s
265
+ #Eventually move this to different method, or make default to_s
266
+ out = ""
267
+
268
+ {:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
269
+ if warnings.length > 0
270
+ out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
271
+
272
+ table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"])
273
+
274
+ warnings.sort_by { |w| w.confidence}.each do |warning|
275
+ w = warning.to_row
276
+
277
+ w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]]
278
+
279
+ table << w
279
280
  end
280
281
 
282
+ out << table.to_s
281
283
  end
282
284
  end
283
285
 
286
+ out
284
287
  end
285
288
  end
286
289
  end
@@ -300,9 +300,9 @@ class Brakeman::Scanner
300
300
  if tracker.config[:escape_html]
301
301
  type = :erubis
302
302
  if options[:rails3]
303
- src = Brakeman::RailsXSSErubis.new(text).src
303
+ src = Brakeman::Rails3XSSErubis.new(text).src
304
304
  else
305
- src = Brakeman::ErubisEscape.new(text).src
305
+ src = Brakeman::Rails2XSSErubis.new(text).src
306
306
  end
307
307
  elsif tracker.config[:erubis]
308
308
  type = :erubis
@@ -382,12 +382,13 @@ class Brakeman::Scanner
382
382
  end
383
383
 
384
384
  #This is from Rails 3 version of the Erubis handler
385
- class Brakeman::RailsXSSErubis < ::Erubis::Eruby
385
+ class Brakeman::Rails3XSSErubis < ::Erubis::Eruby
386
386
 
387
387
  def add_preamble(src)
388
388
  # src << "_buf = ActionView::SafeBuffer.new;\n"
389
389
  end
390
390
 
391
+ #This is different from Rails 3 - fixes some line number issues
391
392
  def add_text(src, text)
392
393
  if text == "\n"
393
394
  src << "\n"
@@ -441,3 +442,54 @@ class Brakeman::RailsXSSErubis < ::Erubis::Eruby
441
442
  end
442
443
  end
443
444
 
445
+ #This is from the rails_xss plugin for Rails 2
446
+ class Brakeman::Rails2XSSErubis < ::Erubis::Eruby
447
+ def add_preamble(src)
448
+ #src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
449
+ end
450
+
451
+ def add_text(src, text)
452
+ return if text.empty?
453
+ src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
454
+ end
455
+
456
+ #This is different from rails_xss - fixes some line number issues
457
+ def add_text(src, text)
458
+ if text == "\n"
459
+ src << "\n"
460
+ elsif text.include? "\n"
461
+ lines = text.split("\n")
462
+ if text.match(/\n\z/)
463
+ lines.each do |line|
464
+ src << "@output_buffer.safe_concat('" << escape_text(line) << "');\n"
465
+ end
466
+ else
467
+ lines[0..-2].each do |line|
468
+ src << "@output_buffer.safe_concat('" << escape_text(line) << "');\n"
469
+ end
470
+
471
+ src << "@output_buffer.safe_concat('" << escape_text(lines.last) << "');"
472
+ end
473
+ else
474
+ src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
475
+ end
476
+ end
477
+
478
+ BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
479
+
480
+ def add_expr_literal(src, code)
481
+ if code =~ BLOCK_EXPR
482
+ src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
483
+ else
484
+ src << '@output_buffer << ((' << code << ').to_s);'
485
+ end
486
+ end
487
+
488
+ def add_expr_escaped(src, code)
489
+ src << '@output_buffer << ' << escaped_expr(code) << ';'
490
+ end
491
+
492
+ def add_postamble(src)
493
+ #src << '@output_buffer.to_s'
494
+ end
495
+ end
data/lib/brakeman/util.rb CHANGED
@@ -192,6 +192,11 @@ module Brakeman::Util
192
192
  exp.is_a? Sexp
193
193
  end
194
194
 
195
+ #Check if _exp_ is a Sexp and the node type matches one of the given types.
196
+ def node_type? exp, *types
197
+ exp.is_a? Sexp and types.include? exp.node_type
198
+ end
199
+
195
200
  #Return file name related to given warning. Uses +warning.file+ if it exists
196
201
  def file_for warning, tracker = nil
197
202
  if tracker.nil?
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "1.3.0"
2
+ Version = "1.4.0"
3
3
  end
@@ -51,7 +51,7 @@ class Brakeman::Warning
51
51
  end
52
52
 
53
53
  def hash
54
- self.format_message.hash
54
+ self.to_s.hash
55
55
  end
56
56
 
57
57
  def eql? other_warning
@@ -93,8 +93,6 @@ class Brakeman::Warning
93
93
 
94
94
  #Generates a hash suitable for inserting into a Ruport table
95
95
  def to_row type = :warning
96
- return @row if @row
97
-
98
96
  @row = { "Confidence" => self.confidence,
99
97
  "Warning Type" => self.warning_type.to_s,
100
98
  "Message" => self.format_message }
@@ -122,4 +120,35 @@ class Brakeman::Warning
122
120
 
123
121
  output
124
122
  end
123
+
124
+ def to_hash
125
+ case @warning_set
126
+ when :template
127
+ location = { :type => :template, :template => self.view_name }
128
+ when :model
129
+ location = { :type => :model, :model => self.model }
130
+ when :controller
131
+ location = { :type => :controller, :controller => self.controller }
132
+ when :warning
133
+ if self.class
134
+ location = { :type => :method, :class => self.class, :method => self.method }
135
+ else
136
+ location = nil
137
+ end
138
+ end
139
+
140
+ { :warning_type => self.warning_type,
141
+ :message => self.message,
142
+ :file => self.file,
143
+ :code => (@code && self.format_code),
144
+ :location => location,
145
+ :confidence => TEXT_CONFIDENCE[self.confidence]
146
+ }
147
+ end
148
+
149
+ def to_json
150
+ require 'json'
151
+
152
+ JSON.dump self.to_hash
153
+ end
125
154
  end
metadata CHANGED
@@ -1,134 +1,101 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
- version: !ruby/object:Gem::Version
4
- hash: 27
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 3
9
- - 0
10
- version: 1.3.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Justin Collins
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-02-09 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-02-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: activesupport
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &78318730 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
32
22
  type: :runtime
33
- version_requirements: *id001
34
- - !ruby/object:Gem::Dependency
35
- name: i18n
36
23
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *78318730
25
+ - !ruby/object:Gem::Dependency
26
+ name: i18n
27
+ requirement: &78318500 !ruby/object:Gem::Requirement
38
28
  none: false
39
- requirements:
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- hash: 3
43
- segments:
44
- - 0
45
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
46
33
  type: :runtime
47
- version_requirements: *id002
48
- - !ruby/object:Gem::Dependency
49
- name: ruby2ruby
50
34
  prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *78318500
36
+ - !ruby/object:Gem::Dependency
37
+ name: ruby2ruby
38
+ requirement: &78318250 !ruby/object:Gem::Requirement
52
39
  none: false
53
- requirements:
40
+ requirements:
54
41
  - - ~>
55
- - !ruby/object:Gem::Version
56
- hash: 11
57
- segments:
58
- - 1
59
- - 2
60
- version: "1.2"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.2'
61
44
  type: :runtime
62
- version_requirements: *id003
63
- - !ruby/object:Gem::Dependency
64
- name: ruport
65
45
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *78318250
47
+ - !ruby/object:Gem::Dependency
48
+ name: ruport
49
+ requirement: &78318000 !ruby/object:Gem::Requirement
67
50
  none: false
68
- requirements:
51
+ requirements:
69
52
  - - ~>
70
- - !ruby/object:Gem::Version
71
- hash: 3
72
- segments:
73
- - 1
74
- - 6
75
- version: "1.6"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
76
55
  type: :runtime
77
- version_requirements: *id004
78
- - !ruby/object:Gem::Dependency
79
- name: erubis
80
56
  prerelease: false
81
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *78318000
58
+ - !ruby/object:Gem::Dependency
59
+ name: erubis
60
+ requirement: &78317770 !ruby/object:Gem::Requirement
82
61
  none: false
83
- requirements:
62
+ requirements:
84
63
  - - ~>
85
- - !ruby/object:Gem::Version
86
- hash: 15
87
- segments:
88
- - 2
89
- - 6
90
- version: "2.6"
64
+ - !ruby/object:Gem::Version
65
+ version: '2.6'
91
66
  type: :runtime
92
- version_requirements: *id005
93
- - !ruby/object:Gem::Dependency
94
- name: haml
95
67
  prerelease: false
96
- requirement: &id006 !ruby/object:Gem::Requirement
68
+ version_requirements: *78317770
69
+ - !ruby/object:Gem::Dependency
70
+ name: haml
71
+ requirement: &78317540 !ruby/object:Gem::Requirement
97
72
  none: false
98
- requirements:
73
+ requirements:
99
74
  - - ~>
100
- - !ruby/object:Gem::Version
101
- hash: 7
102
- segments:
103
- - 3
104
- - 0
105
- version: "3.0"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.0'
106
77
  type: :runtime
107
- version_requirements: *id006
108
- - !ruby/object:Gem::Dependency
109
- name: sass
110
78
  prerelease: false
111
- requirement: &id007 !ruby/object:Gem::Requirement
79
+ version_requirements: *78317540
80
+ - !ruby/object:Gem::Dependency
81
+ name: sass
82
+ requirement: &78317310 !ruby/object:Gem::Requirement
112
83
  none: false
113
- requirements:
84
+ requirements:
114
85
  - - ~>
115
- - !ruby/object:Gem::Version
116
- hash: 7
117
- segments:
118
- - 3
119
- - 0
120
- version: "3.0"
86
+ - !ruby/object:Gem::Version
87
+ version: '3.0'
121
88
  type: :runtime
122
- version_requirements: *id007
123
- description: Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis.
89
+ prerelease: false
90
+ version_requirements: *78317310
91
+ description: Brakeman detects security vulnerabilities in Ruby on Rails applications
92
+ via static analysis.
124
93
  email:
125
- executables:
94
+ executables:
126
95
  - brakeman
127
96
  extensions: []
128
-
129
97
  extra_rdoc_files: []
130
-
131
- files:
98
+ files:
132
99
  - bin/brakeman
133
100
  - WARNING_TYPES
134
101
  - FEATURES
@@ -184,6 +151,7 @@ files:
184
151
  - lib/brakeman/checks/check_execute.rb
185
152
  - lib/brakeman/checks/check_filter_skipping.rb
186
153
  - lib/brakeman/checks/check_mail_to.rb
154
+ - lib/brakeman/checks/check_link_to_href.rb
187
155
  - lib/brakeman/checks/base_check.rb
188
156
  - lib/brakeman/checks/check_file_access.rb
189
157
  - lib/brakeman/checks/check_response_splitting.rb
@@ -204,36 +172,26 @@ files:
204
172
  - lib/brakeman/format/style.css
205
173
  homepage: http://brakemanscanner.org
206
174
  licenses: []
207
-
208
175
  post_install_message:
209
176
  rdoc_options: []
210
-
211
- require_paths:
177
+ require_paths:
212
178
  - lib
213
- required_ruby_version: !ruby/object:Gem::Requirement
179
+ required_ruby_version: !ruby/object:Gem::Requirement
214
180
  none: false
215
- requirements:
216
- - - ">="
217
- - !ruby/object:Gem::Version
218
- hash: 3
219
- segments:
220
- - 0
221
- version: "0"
222
- required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ! '>='
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
223
186
  none: false
224
- requirements:
225
- - - ">="
226
- - !ruby/object:Gem::Version
227
- hash: 3
228
- segments:
229
- - 0
230
- version: "0"
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
231
191
  requirements: []
232
-
233
192
  rubyforge_project:
234
193
  rubygems_version: 1.8.15
235
194
  signing_key:
236
195
  specification_version: 3
237
196
  summary: Security vulnerability scanner for Ruby on Rails.
238
197
  test_files: []
239
-