brakeman 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -1
- data/bin/brakeman +2 -2
- data/lib/brakeman.rb +0 -9
- data/lib/brakeman/checks.rb +24 -14
- data/lib/brakeman/checks/check_cross_site_scripting.rb +4 -2
- data/lib/brakeman/checks/check_link_to.rb +9 -11
- data/lib/brakeman/checks/check_link_to_href.rb +90 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +2 -1
- data/lib/brakeman/checks/check_sql.rb +1 -1
- data/lib/brakeman/options.rb +5 -0
- data/lib/brakeman/processors/controller_processor.rb +2 -2
- data/lib/brakeman/processors/erb_template_processor.rb +1 -1
- data/lib/brakeman/processors/lib/find_all_calls.rb +1 -1
- data/lib/brakeman/processors/lib/find_call.rb +1 -1
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +1 -1
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +2 -2
- data/lib/brakeman/processors/lib/render_helper.rb +1 -1
- data/lib/brakeman/processors/template_alias_processor.rb +2 -2
- data/lib/brakeman/report.rb +9 -11
- data/lib/brakeman/rescanner.rb +19 -16
- data/lib/brakeman/scanner.rb +55 -3
- data/lib/brakeman/util.rb +5 -0
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +32 -3
- metadata +73 -115
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
|
-
|
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
|
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
|
|
data/lib/brakeman/checks.rb
CHANGED
@@ -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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
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
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
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
|
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
|
@@ -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
|
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
|
data/lib/brakeman/options.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
data/lib/brakeman/report.rb
CHANGED
@@ -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
|
-
|
482
|
+
summary[warning.warning_type.to_s] += 1
|
487
483
|
|
488
|
-
|
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
|
data/lib/brakeman/rescanner.rb
CHANGED
@@ -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
|
-
|
259
|
+
<<-OUTPUT
|
260
260
|
Total warnings: #{all_warnings.length}
|
261
261
|
Fixed warnings: #{fixed_warnings.length}
|
262
262
|
New warnings: #{new_warnings.length}
|
263
|
-
|
263
|
+
OUTPUT
|
264
264
|
else
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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
|
data/lib/brakeman/scanner.rb
CHANGED
@@ -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::
|
303
|
+
src = Brakeman::Rails3XSSErubis.new(text).src
|
304
304
|
else
|
305
|
-
src = Brakeman::
|
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::
|
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?
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
@@ -51,7 +51,7 @@ class Brakeman::Warning
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def hash
|
54
|
-
self.
|
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
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
117
|
-
segments:
|
118
|
-
- 3
|
119
|
-
- 0
|
120
|
-
version: "3.0"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.0'
|
121
88
|
type: :runtime
|
122
|
-
|
123
|
-
|
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
|
-
|
219
|
-
|
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
|
-
|
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
|
-
|