brakeman-lib 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +872 -0
- data/FEATURES +16 -0
- data/README.md +169 -0
- data/WARNING_TYPES +95 -0
- data/bin/brakeman +89 -0
- data/lib/brakeman.rb +495 -0
- data/lib/brakeman/app_tree.rb +161 -0
- data/lib/brakeman/brakeman.rake +17 -0
- data/lib/brakeman/call_index.rb +219 -0
- data/lib/brakeman/checks.rb +191 -0
- data/lib/brakeman/checks/base_check.rb +518 -0
- data/lib/brakeman/checks/check_basic_auth.rb +88 -0
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/brakeman/checks/check_content_tag.rb +160 -0
- data/lib/brakeman/checks/check_create_with.rb +75 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
- data/lib/brakeman/checks/check_default_routes.rb +86 -0
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +36 -0
- data/lib/brakeman/checks/check_execute.rb +167 -0
- data/lib/brakeman/checks/check_file_access.rb +63 -0
- data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
- data/lib/brakeman/checks/check_header_dos.rb +31 -0
- data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_encoding.rb +47 -0
- data/lib/brakeman/checks/check_json_parsing.rb +107 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +115 -0
- data/lib/brakeman/checks/check_mail_to.rb +49 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
- data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
- data/lib/brakeman/checks/check_model_attributes.rb +119 -0
- data/lib/brakeman/checks/check_model_serialize.rb +67 -0
- data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +215 -0
- data/lib/brakeman/checks/check_regex_dos.rb +69 -0
- data/lib/brakeman/checks/check_render.rb +92 -0
- data/lib/brakeman/checks/check_render_dos.rb +37 -0
- data/lib/brakeman/checks/check_render_inline.rb +54 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_route_dos.rb +42 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
- data/lib/brakeman/checks/check_secrets.rb +40 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
- data/lib/brakeman/checks/check_send.rb +48 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
- data/lib/brakeman/checks/check_session_settings.rb +170 -0
- data/lib/brakeman/checks/check_simple_format.rb +59 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
- data/lib/brakeman/checks/check_sql.rb +660 -0
- data/lib/brakeman/checks/check_sql_cves.rb +101 -0
- data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
- data/lib/brakeman/checks/check_strip_tags.rb +89 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/brakeman/checks/check_translate_bug.rb +45 -0
- data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
- data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
- data/lib/brakeman/checks/check_validation_regex.rb +116 -0
- data/lib/brakeman/checks/check_weak_hash.rb +151 -0
- data/lib/brakeman/checks/check_without_protection.rb +80 -0
- data/lib/brakeman/checks/check_xml_dos.rb +51 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/brakeman/file_parser.rb +50 -0
- data/lib/brakeman/format/style.css +133 -0
- data/lib/brakeman/options.rb +301 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
- data/lib/brakeman/parsers/template_parser.rb +89 -0
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +1013 -0
- data/lib/brakeman/processors/base_processor.rb +277 -0
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
- data/lib/brakeman/processors/controller_processor.rb +326 -0
- data/lib/brakeman/processors/erb_template_processor.rb +80 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
- data/lib/brakeman/processors/gem_processor.rb +57 -0
- data/lib/brakeman/processors/haml_template_processor.rb +190 -0
- data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
- data/lib/brakeman/processors/lib/find_call.rb +183 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/brakeman/processors/lib/render_helper.rb +181 -0
- data/lib/brakeman/processors/lib/render_path.rb +107 -0
- data/lib/brakeman/processors/lib/route_helper.rb +68 -0
- data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
- data/lib/brakeman/processors/library_processor.rb +119 -0
- data/lib/brakeman/processors/model_processor.rb +191 -0
- data/lib/brakeman/processors/output_processor.rb +171 -0
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +107 -0
- data/lib/brakeman/processors/template_alias_processor.rb +116 -0
- data/lib/brakeman/processors/template_processor.rb +74 -0
- data/lib/brakeman/report.rb +78 -0
- data/lib/brakeman/report/config/remediation.yml +71 -0
- data/lib/brakeman/report/ignore/config.rb +135 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +286 -0
- data/lib/brakeman/report/report_codeclimate.rb +70 -0
- data/lib/brakeman/report/report_csv.rb +55 -0
- data/lib/brakeman/report/report_hash.rb +23 -0
- data/lib/brakeman/report/report_html.rb +216 -0
- data/lib/brakeman/report/report_json.rb +42 -0
- data/lib/brakeman/report/report_markdown.rb +156 -0
- data/lib/brakeman/report/report_table.rb +107 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
- data/lib/brakeman/report/templates/header.html.erb +58 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/overview.html.erb +38 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
- data/lib/brakeman/rescanner.rb +483 -0
- data/lib/brakeman/scanner.rb +317 -0
- data/lib/brakeman/tracker.rb +347 -0
- data/lib/brakeman/tracker/collection.rb +93 -0
- data/lib/brakeman/tracker/config.rb +101 -0
- data/lib/brakeman/tracker/constants.rb +101 -0
- data/lib/brakeman/tracker/controller.rb +161 -0
- data/lib/brakeman/tracker/library.rb +17 -0
- data/lib/brakeman/tracker/model.rb +90 -0
- data/lib/brakeman/tracker/template.rb +33 -0
- data/lib/brakeman/util.rb +481 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +255 -0
- data/lib/brakeman/warning_codes.rb +111 -0
- data/lib/ruby_parser/bm_sexp.rb +610 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +362 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check for uses of quote_table_name in Rails versions before 2.3.13 and 3.0.10
|
|
4
|
+
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6a1e473744bc389b
|
|
5
|
+
class Brakeman::CheckQuoteTableName < Brakeman::BaseCheck
|
|
6
|
+
Brakeman::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Checks for quote_table_name vulnerability in versions before 2.3.14 and 3.0.10"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
if (version_between?('2.0.0', '2.3.13') or
|
|
12
|
+
version_between?('3.0.0', '3.0.9'))
|
|
13
|
+
|
|
14
|
+
if uses_quote_table_name?
|
|
15
|
+
confidence = CONFIDENCE[:high]
|
|
16
|
+
else
|
|
17
|
+
confidence = CONFIDENCE[:med]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if rails_version =~ /^3/
|
|
21
|
+
message = "Versions before 3.0.10 have a vulnerability in quote_table_name: CVE-2011-2930"
|
|
22
|
+
else
|
|
23
|
+
message = "Versions before 2.3.14 have a vulnerability in quote_table_name: CVE-2011-2930"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
warn :warning_type => "SQL Injection",
|
|
27
|
+
:warning_code => :CVE_2011_2930,
|
|
28
|
+
:message => message,
|
|
29
|
+
:confidence => confidence,
|
|
30
|
+
:gem_info => gemfile_or_environment,
|
|
31
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/ah5HN0S8OJs/discussion"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def uses_quote_table_name?
|
|
36
|
+
Brakeman.debug "Finding calls to quote_table_name()"
|
|
37
|
+
|
|
38
|
+
not tracker.find_call(:target => false, :method => :quote_table_name).empty?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Reports any calls to +redirect_to+ which include parameters in the arguments.
|
|
4
|
+
#
|
|
5
|
+
#For example:
|
|
6
|
+
#
|
|
7
|
+
# redirect_to params.merge(:action => :elsewhere)
|
|
8
|
+
class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
9
|
+
Brakeman::Checks.add self
|
|
10
|
+
|
|
11
|
+
@description = "Looks for calls to redirect_to with user input as arguments"
|
|
12
|
+
|
|
13
|
+
def run_check
|
|
14
|
+
Brakeman.debug "Finding calls to redirect_to()"
|
|
15
|
+
|
|
16
|
+
@model_find_calls = Set[:all, :create, :create!, :find, :find_by_sql, :first, :last, :new]
|
|
17
|
+
|
|
18
|
+
if tracker.options[:rails3]
|
|
19
|
+
@model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if version_between? "4.0.0", "9.9.9"
|
|
23
|
+
@model_find_calls.merge [:find_by, :find_by!, :take]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@tracker.find_call(:target => false, :method => :redirect_to).each do |res|
|
|
27
|
+
process_result res
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_result result
|
|
32
|
+
return if duplicate? result
|
|
33
|
+
|
|
34
|
+
call = result[:call]
|
|
35
|
+
|
|
36
|
+
method = call.method
|
|
37
|
+
|
|
38
|
+
if method == :redirect_to and
|
|
39
|
+
not only_path?(call) and
|
|
40
|
+
not explicit_host?(call.first_arg) and
|
|
41
|
+
not slice_call?(call.first_arg) and
|
|
42
|
+
res = include_user_input?(call)
|
|
43
|
+
|
|
44
|
+
add_result result
|
|
45
|
+
|
|
46
|
+
if res.type == :immediate
|
|
47
|
+
confidence = CONFIDENCE[:high]
|
|
48
|
+
else
|
|
49
|
+
confidence = CONFIDENCE[:low]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
warn :result => result,
|
|
53
|
+
:warning_type => "Redirect",
|
|
54
|
+
:warning_code => :open_redirect,
|
|
55
|
+
:message => "Possible unprotected redirect",
|
|
56
|
+
:code => call,
|
|
57
|
+
:user_input => res,
|
|
58
|
+
:confidence => confidence
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#Custom check for user input. First looks to see if the user input
|
|
63
|
+
#is being output directly. This is necessary because of tracker.options[:check_arguments]
|
|
64
|
+
#which can be used to enable/disable reporting output of method calls which use
|
|
65
|
+
#user input as arguments.
|
|
66
|
+
def include_user_input? call, immediate = :immediate
|
|
67
|
+
Brakeman.debug "Checking if call includes user input"
|
|
68
|
+
|
|
69
|
+
arg = call.first_arg
|
|
70
|
+
|
|
71
|
+
# if the first argument is an array, rails assumes you are building a
|
|
72
|
+
# polymorphic route, which will never jump off-host
|
|
73
|
+
return false if array? arg
|
|
74
|
+
|
|
75
|
+
if tracker.options[:ignore_redirect_to_model]
|
|
76
|
+
if model_instance?(arg) or decorated_model?(arg)
|
|
77
|
+
return false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if res = has_immediate_model?(arg)
|
|
82
|
+
return Match.new(immediate, res)
|
|
83
|
+
elsif call? arg
|
|
84
|
+
if request_value? arg
|
|
85
|
+
return Match.new(immediate, arg)
|
|
86
|
+
elsif request_value? arg.target
|
|
87
|
+
return Match.new(immediate, arg.target)
|
|
88
|
+
elsif arg.method == :url_for and include_user_input? arg
|
|
89
|
+
return Match.new(immediate, arg)
|
|
90
|
+
#Ignore helpers like some_model_url?
|
|
91
|
+
elsif arg.method.to_s =~ /_(url|path)\z/
|
|
92
|
+
return false
|
|
93
|
+
end
|
|
94
|
+
elsif request_value? arg
|
|
95
|
+
return Match.new(immediate, arg)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if tracker.options[:check_arguments] and call? arg
|
|
99
|
+
include_user_input? arg, false #I'm doubting if this is really necessary...
|
|
100
|
+
else
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#Checks +redirect_to+ arguments for +only_path => true+ which essentially
|
|
106
|
+
#nullifies the danger posed by redirecting with user input
|
|
107
|
+
def only_path? call
|
|
108
|
+
arg = call.first_arg
|
|
109
|
+
|
|
110
|
+
if hash? arg
|
|
111
|
+
if value = hash_access(arg, :only_path)
|
|
112
|
+
return true if true?(value)
|
|
113
|
+
end
|
|
114
|
+
elsif call? arg and arg.method == :url_for
|
|
115
|
+
return check_url_for(arg)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def explicit_host? arg
|
|
122
|
+
return unless sexp? arg
|
|
123
|
+
|
|
124
|
+
if hash? arg
|
|
125
|
+
if value = hash_access(arg, :host)
|
|
126
|
+
return !has_immediate_user_input?(value)
|
|
127
|
+
end
|
|
128
|
+
elsif call? arg
|
|
129
|
+
target = arg.target
|
|
130
|
+
|
|
131
|
+
if hash? target and value = hash_access(target, :host)
|
|
132
|
+
return !has_immediate_user_input?(value)
|
|
133
|
+
elsif call? arg
|
|
134
|
+
return explicit_host? target
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
#+url_for+ is only_path => true by default. This checks to see if it is
|
|
142
|
+
#set to false for some reason.
|
|
143
|
+
def check_url_for call
|
|
144
|
+
arg = call.first_arg
|
|
145
|
+
|
|
146
|
+
if hash? arg
|
|
147
|
+
if value = hash_access(arg, :only_path)
|
|
148
|
+
return false if false?(value)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
#Returns true if exp is (probably) a model instance
|
|
156
|
+
def model_instance? exp
|
|
157
|
+
if node_type? exp, :or
|
|
158
|
+
model_instance? exp.lhs or model_instance? exp.rhs
|
|
159
|
+
elsif call? exp
|
|
160
|
+
if model_target? exp and
|
|
161
|
+
(@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))
|
|
162
|
+
true
|
|
163
|
+
else
|
|
164
|
+
association?(exp.target, exp.method)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def model_target? exp
|
|
170
|
+
return false unless call? exp
|
|
171
|
+
model_name? exp.target or
|
|
172
|
+
friendly_model? exp.target or
|
|
173
|
+
model_target? exp.target
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
#Returns true if exp is (probably) a friendly model instance
|
|
177
|
+
#using the FriendlyId gem
|
|
178
|
+
def friendly_model? exp
|
|
179
|
+
call? exp and model_name? exp.target and exp.method == :friendly
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
#Returns true if exp is (probably) a decorated model instance
|
|
183
|
+
#using the Draper gem
|
|
184
|
+
def decorated_model? exp
|
|
185
|
+
if node_type? exp, :or
|
|
186
|
+
decorated_model? exp.lhs or decorated_model? exp.rhs
|
|
187
|
+
else
|
|
188
|
+
tracker.config.has_gem? :draper and
|
|
189
|
+
call? exp and
|
|
190
|
+
node_type?(exp.target, :const) and
|
|
191
|
+
exp.target.value.to_s.match(/Decorator$/) and
|
|
192
|
+
exp.method == :decorate
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
#Check if method is actually an association in a Model
|
|
197
|
+
def association? model_name, meth
|
|
198
|
+
if call? model_name
|
|
199
|
+
return association? model_name.target, meth
|
|
200
|
+
elsif model_name? model_name
|
|
201
|
+
model = tracker.models[class_name(model_name)]
|
|
202
|
+
else
|
|
203
|
+
return false
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
return false unless model
|
|
207
|
+
|
|
208
|
+
model.association? meth
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def slice_call? exp
|
|
212
|
+
return unless call? exp
|
|
213
|
+
exp.method == :slice
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#This check looks for regexes that include user input.
|
|
4
|
+
class Brakeman::CheckRegexDoS < Brakeman::BaseCheck
|
|
5
|
+
Brakeman::Checks.add self
|
|
6
|
+
|
|
7
|
+
ESCAPES = {
|
|
8
|
+
s(:const, :Regexp) => [
|
|
9
|
+
:escape,
|
|
10
|
+
:quote
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@description = "Searches regexes including user input"
|
|
15
|
+
|
|
16
|
+
#Process calls
|
|
17
|
+
def run_check
|
|
18
|
+
Brakeman.debug "Finding dynamic regexes"
|
|
19
|
+
calls = tracker.find_call :method => [:brakeman_regex_interp]
|
|
20
|
+
|
|
21
|
+
Brakeman.debug "Processing dynamic regexes"
|
|
22
|
+
calls.each do |call|
|
|
23
|
+
process_result call
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#Warns if regex includes user input
|
|
28
|
+
def process_result result
|
|
29
|
+
return if duplicate? result or result[:call].original_line
|
|
30
|
+
add_result result
|
|
31
|
+
|
|
32
|
+
call = result[:call]
|
|
33
|
+
components = call[1..-1]
|
|
34
|
+
|
|
35
|
+
components.any? do |component|
|
|
36
|
+
next unless sexp? component
|
|
37
|
+
|
|
38
|
+
if match = has_immediate_user_input?(component)
|
|
39
|
+
confidence = CONFIDENCE[:high]
|
|
40
|
+
elsif match = has_immediate_model?(component)
|
|
41
|
+
match = Match.new(:model, match)
|
|
42
|
+
confidence = CONFIDENCE[:med]
|
|
43
|
+
elsif match = include_user_input?(component)
|
|
44
|
+
confidence = CONFIDENCE[:low]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if match
|
|
48
|
+
message = "#{friendly_type_of(match).capitalize} used in regex"
|
|
49
|
+
|
|
50
|
+
warn :result => result,
|
|
51
|
+
:warning_type => "Denial of Service",
|
|
52
|
+
:warning_code => :regex_dos,
|
|
53
|
+
:message => message,
|
|
54
|
+
:confidence => confidence,
|
|
55
|
+
:user_input => match
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def process_call(exp)
|
|
61
|
+
if escape_methods = ESCAPES[exp.target]
|
|
62
|
+
if escape_methods.include? exp.method
|
|
63
|
+
return exp
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
super
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check calls to +render()+ for dangerous values
|
|
4
|
+
class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
5
|
+
Brakeman::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Finds calls to render that might allow file access or code execution"
|
|
8
|
+
|
|
9
|
+
def run_check
|
|
10
|
+
tracker.find_call(:target => nil, :method => :render).each do |result|
|
|
11
|
+
process_render_result result
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def process_render_result result
|
|
16
|
+
return unless node_type? result[:call], :render
|
|
17
|
+
|
|
18
|
+
case result[:call].render_type
|
|
19
|
+
when :partial, :template, :action, :file
|
|
20
|
+
check_for_rce(result) or
|
|
21
|
+
check_for_dynamic_path(result)
|
|
22
|
+
when :inline
|
|
23
|
+
when :js
|
|
24
|
+
when :json
|
|
25
|
+
when :text
|
|
26
|
+
when :update
|
|
27
|
+
when :xml
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#Check if path to action or file is determined dynamically
|
|
32
|
+
def check_for_dynamic_path result
|
|
33
|
+
view = result[:call][2]
|
|
34
|
+
|
|
35
|
+
if sexp? view and not duplicate? result
|
|
36
|
+
add_result result
|
|
37
|
+
|
|
38
|
+
if input = has_immediate_user_input?(view)
|
|
39
|
+
if string_interp? view
|
|
40
|
+
confidence = CONFIDENCE[:med]
|
|
41
|
+
else
|
|
42
|
+
confidence = CONFIDENCE[:high]
|
|
43
|
+
end
|
|
44
|
+
elsif input = include_user_input?(view)
|
|
45
|
+
confidence = CONFIDENCE[:low]
|
|
46
|
+
else
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return if input.type == :model #skip models
|
|
51
|
+
return if safe_param? input.match
|
|
52
|
+
|
|
53
|
+
message = "Render path contains #{friendly_type_of input}"
|
|
54
|
+
|
|
55
|
+
warn :result => result,
|
|
56
|
+
:warning_type => "Dynamic Render Path",
|
|
57
|
+
:warning_code => :dynamic_render_path,
|
|
58
|
+
:message => message,
|
|
59
|
+
:user_input => input,
|
|
60
|
+
:confidence => confidence
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def check_for_rce result
|
|
65
|
+
return unless version_between? "0.0.0", "3.2.22" or
|
|
66
|
+
version_between? "4.0.0", "4.1.14" or
|
|
67
|
+
version_between? "4.2.0", "4.2.5"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
view = result[:call][2]
|
|
71
|
+
if sexp? view and not duplicate? result
|
|
72
|
+
if params? view
|
|
73
|
+
add_result result
|
|
74
|
+
return if safe_param? view
|
|
75
|
+
|
|
76
|
+
warn :result => result,
|
|
77
|
+
:warning_type => "Remote Code Execution",
|
|
78
|
+
:warning_code => :dynamic_render_path_rce,
|
|
79
|
+
:message => "Passing query parameters to render() is vulnerable in Rails #{rails_version} (CVE-2016-0752)",
|
|
80
|
+
:user_input => view,
|
|
81
|
+
:confidence => CONFIDENCE[:high]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def safe_param? exp
|
|
87
|
+
if params? exp and call? exp and exp.method == :[]
|
|
88
|
+
arg = exp.first_arg
|
|
89
|
+
symbol? arg and [:controller, :action].include? arg.value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckRenderDoS < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Warn about denial of service with render :text (CVE-2014-0082)"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
if version_between? "3.0.0", "3.0.20" or
|
|
10
|
+
version_between? "3.1.0", "3.1.12" or
|
|
11
|
+
version_between? "3.2.0", "3.2.16"
|
|
12
|
+
|
|
13
|
+
tracker.find_call(:target => nil, :method => :render).each do |result|
|
|
14
|
+
if text_render? result
|
|
15
|
+
warn_about_text_render
|
|
16
|
+
break
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def text_render? result
|
|
23
|
+
node_type? result[:call], :render and
|
|
24
|
+
result[:call].render_type == :text
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def warn_about_text_render
|
|
28
|
+
message = "Rails #{rails_version} has a denial of service vulnerability (CVE-2014-0082). Upgrade to Rails version 3.2.17"
|
|
29
|
+
|
|
30
|
+
warn :warning_type => "Denial of Service",
|
|
31
|
+
:warning_code => :CVE_2014_0082,
|
|
32
|
+
:message => message,
|
|
33
|
+
:confidence => CONFIDENCE[:high],
|
|
34
|
+
:link_path => "https://groups.google.com/d/msg/rubyonrails-security/LMxO_3_eCuc/ozGBEhKaJbIJ",
|
|
35
|
+
:gem_info => gemfile_or_environment
|
|
36
|
+
end
|
|
37
|
+
end
|