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