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,132 @@
|
|
|
1
|
+
require 'brakeman/checks/check_cross_site_scripting'
|
|
2
|
+
|
|
3
|
+
#Checks for calls to link_to in versions of Ruby where link_to did not
|
|
4
|
+
#escape the first argument.
|
|
5
|
+
#
|
|
6
|
+
#See https://rails.lighthouseapp.com/projects/8994/tickets/3518-link_to-doesnt-escape-its-input
|
|
7
|
+
class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
|
|
8
|
+
Brakeman::Checks.add self
|
|
9
|
+
|
|
10
|
+
@description = "Checks for XSS in link_to in versions before 3.0"
|
|
11
|
+
|
|
12
|
+
def run_check
|
|
13
|
+
return unless version_between?("2.0.0", "2.9.9") and not tracker.config.escape_html?
|
|
14
|
+
|
|
15
|
+
@ignore_methods = Set[:button_to, :check_box, :escapeHTML, :escape_once,
|
|
16
|
+
:field_field, :fields_for, :h, :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, :u, :url_for,
|
|
21
|
+
:will_paginate].merge tracker.options[:safe_methods]
|
|
22
|
+
|
|
23
|
+
@known_dangerous = []
|
|
24
|
+
#Ideally, I think this should also check to see if people are setting
|
|
25
|
+
#:escape => false
|
|
26
|
+
@models = tracker.models.keys
|
|
27
|
+
@inspect_arguments = tracker.options[:check_arguments]
|
|
28
|
+
|
|
29
|
+
tracker.find_call(:target => false, :method => :link_to).each {|call| process_result call}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_result result
|
|
33
|
+
return if duplicate? result
|
|
34
|
+
|
|
35
|
+
#Have to make a copy of this, otherwise it will be changed to
|
|
36
|
+
#an ignored method call by the code above.
|
|
37
|
+
call = result[:call] = result[:call].dup
|
|
38
|
+
|
|
39
|
+
first_arg = call.first_arg
|
|
40
|
+
second_arg = call.second_arg
|
|
41
|
+
|
|
42
|
+
@matched = false
|
|
43
|
+
|
|
44
|
+
#Skip if no arguments(?) or first argument is a hash
|
|
45
|
+
return if first_arg.nil? or hash? first_arg
|
|
46
|
+
|
|
47
|
+
if version_between? "2.0.0", "2.2.99"
|
|
48
|
+
check_argument result, first_arg
|
|
49
|
+
|
|
50
|
+
if second_arg and not hash? second_arg
|
|
51
|
+
check_argument result, second_arg
|
|
52
|
+
end
|
|
53
|
+
elsif second_arg
|
|
54
|
+
#Only check first argument if there is a second argument
|
|
55
|
+
#in Rails 2.3.x
|
|
56
|
+
check_argument result, first_arg
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Check the argument for possible xss exploits
|
|
61
|
+
def check_argument result, exp
|
|
62
|
+
argument = process(exp)
|
|
63
|
+
!check_user_input(result, argument) && !check_method(result, argument) && !check_matched(result, @matched)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Check we should warn about the user input
|
|
67
|
+
def check_user_input(result, argument)
|
|
68
|
+
input = has_immediate_user_input?(argument)
|
|
69
|
+
return false unless input
|
|
70
|
+
|
|
71
|
+
message = "Unescaped #{friendly_type_of input} in link_to"
|
|
72
|
+
|
|
73
|
+
warn_xss(result, message, input, CONFIDENCE[:high])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Check if we should warn about the specified method
|
|
77
|
+
def check_method(result, argument)
|
|
78
|
+
return false if tracker.options[:ignore_model_output]
|
|
79
|
+
match = has_immediate_model?(argument)
|
|
80
|
+
return false unless match
|
|
81
|
+
method = match.method
|
|
82
|
+
return false if IGNORE_MODEL_METHODS.include? method
|
|
83
|
+
|
|
84
|
+
confidence = CONFIDENCE[:med]
|
|
85
|
+
confidence = CONFIDENCE[:high] if likely_model_attribute? match
|
|
86
|
+
warn_xss(result, "Unescaped model attribute in link_to", match, confidence)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Check if we should warn about the matched result
|
|
90
|
+
def check_matched(result, matched = nil)
|
|
91
|
+
return false unless matched
|
|
92
|
+
return false if matched.type == :model and tracker.options[:ignore_model_output]
|
|
93
|
+
|
|
94
|
+
message = "Unescaped #{friendly_type_of matched} in link_to"
|
|
95
|
+
|
|
96
|
+
warn_xss(result, message, @matched, CONFIDENCE[:med])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create a warn for this xss
|
|
100
|
+
def warn_xss(result, message, user_input, confidence)
|
|
101
|
+
add_result(result)
|
|
102
|
+
warn :result => result,
|
|
103
|
+
:warning_type => "Cross Site Scripting",
|
|
104
|
+
:warning_code => :xss_link_to,
|
|
105
|
+
:message => message,
|
|
106
|
+
:user_input => user_input,
|
|
107
|
+
:confidence => confidence,
|
|
108
|
+
:link_path => "link_to"
|
|
109
|
+
|
|
110
|
+
true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def process_call exp
|
|
114
|
+
@mark = true
|
|
115
|
+
actually_process_call exp
|
|
116
|
+
exp
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def actually_process_call exp
|
|
120
|
+
return if @matched
|
|
121
|
+
|
|
122
|
+
target = exp.target
|
|
123
|
+
target = process target.dup if sexp? target
|
|
124
|
+
|
|
125
|
+
#Bare records create links to the model resource,
|
|
126
|
+
#not a string that could have injection
|
|
127
|
+
#TODO: Needs test? I think this is broken?
|
|
128
|
+
return exp if model_name? target and context == [:call, :arglist]
|
|
129
|
+
|
|
130
|
+
super
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
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[:button_to, :check_box,
|
|
16
|
+
:field_field, :fields_for, :hidden_field,
|
|
17
|
+
:hidden_field, :hidden_field_tag, :image_tag, :label,
|
|
18
|
+
:mail_to, :polymorphic_url, :radio_button, :select, :slice,
|
|
19
|
+
:submit_tag, :text_area, :text_field,
|
|
20
|
+
:text_field_tag, :url_encode, :u, :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.second_arg
|
|
38
|
+
|
|
39
|
+
#Ignore situations where the href is an interpolated string
|
|
40
|
+
#with something before the user input
|
|
41
|
+
return if string_interp?(url_arg) && !url_arg[1].chomp.empty?
|
|
42
|
+
|
|
43
|
+
return if call? url_arg and ignore_call? url_arg.target, url_arg.method
|
|
44
|
+
|
|
45
|
+
if input = has_immediate_user_input?(url_arg)
|
|
46
|
+
message = "Unsafe #{friendly_type_of input} in link_to href"
|
|
47
|
+
|
|
48
|
+
unless duplicate? result
|
|
49
|
+
add_result result
|
|
50
|
+
warn :result => result,
|
|
51
|
+
:warning_type => "Cross Site Scripting",
|
|
52
|
+
:warning_code => :xss_link_to_href,
|
|
53
|
+
:message => message,
|
|
54
|
+
:user_input => input,
|
|
55
|
+
:confidence => CONFIDENCE[:high],
|
|
56
|
+
:link_path => "link_to_href"
|
|
57
|
+
end
|
|
58
|
+
elsif has_immediate_model? url_arg or model_find_call? url_arg
|
|
59
|
+
|
|
60
|
+
# Decided NOT warn on models. polymorphic_path is called it a model is
|
|
61
|
+
# passed to link_to (which passes it to url_for)
|
|
62
|
+
|
|
63
|
+
elsif array? url_arg
|
|
64
|
+
# Just like models, polymorphic path/url is called if the argument is
|
|
65
|
+
# an array
|
|
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.type == :model and not tracker.options[:ignore_model_output]
|
|
76
|
+
message = "Unsafe model attribute in link_to href"
|
|
77
|
+
elsif @matched.type == :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
|
+
:warning_code => :xss_link_to_href,
|
|
86
|
+
:message => message,
|
|
87
|
+
:user_input => @matched,
|
|
88
|
+
:confidence => CONFIDENCE[:med],
|
|
89
|
+
:link_path => "link_to_href"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def ignore_call? target, method
|
|
95
|
+
decorated_model? method or super
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def decorated_model? method
|
|
99
|
+
tracker.config.has_gem? :draper and
|
|
100
|
+
method == :decorate
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def ignored_method? target, method
|
|
104
|
+
@ignore_methods.include? method or
|
|
105
|
+
method.to_s =~ /_path$/ or
|
|
106
|
+
(target.nil? and method.to_s =~ /_url$/)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def model_find_call? exp
|
|
110
|
+
return unless call? exp
|
|
111
|
+
|
|
112
|
+
MODEL_METHODS.include? exp.method or
|
|
113
|
+
exp.method.to_s =~ /^find_by_/
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check for cross site scripting vulnerability in mail_to :encode => :javascript
|
|
4
|
+
#with certain versions of Rails (< 2.3.11 or < 3.0.4).
|
|
5
|
+
#
|
|
6
|
+
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f02a48ede8315f81
|
|
7
|
+
class Brakeman::CheckMailTo < Brakeman::BaseCheck
|
|
8
|
+
Brakeman::Checks.add self
|
|
9
|
+
|
|
10
|
+
@description = "Checks for mail_to XSS vulnerability in certain versions"
|
|
11
|
+
|
|
12
|
+
def run_check
|
|
13
|
+
if (version_between? "2.3.0", "2.3.10" or version_between? "3.0.0", "3.0.3") and result = mail_to_javascript?
|
|
14
|
+
message = "Vulnerability in mail_to using javascript encoding (CVE-2011-0446). Upgrade to Rails version "
|
|
15
|
+
|
|
16
|
+
if version_between? "2.3.0", "2.3.10"
|
|
17
|
+
message << "2.3.11"
|
|
18
|
+
else
|
|
19
|
+
message << "3.0.4"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
warn :result => result,
|
|
23
|
+
:warning_type => "Mail Link",
|
|
24
|
+
:warning_code => :CVE_2011_0446,
|
|
25
|
+
:message => message,
|
|
26
|
+
:confidence => CONFIDENCE[:high],
|
|
27
|
+
:gem_info => gemfile_or_environment,
|
|
28
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/8CpI7egxX4E/discussion"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#Check for javascript encoding of mail_to address
|
|
33
|
+
# mail_to email, name, :encode => :javascript
|
|
34
|
+
def mail_to_javascript?
|
|
35
|
+
Brakeman.debug "Checking calls to mail_to for javascript encoding"
|
|
36
|
+
|
|
37
|
+
tracker.find_call(:target => false, :method => :mail_to).each do |result|
|
|
38
|
+
result[:call].each_arg do |arg|
|
|
39
|
+
if hash? arg
|
|
40
|
+
if option = hash_access(arg, :encode)
|
|
41
|
+
return result if symbol? option and option.value == :javascript
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
#Checks for mass assignments to models.
|
|
5
|
+
#
|
|
6
|
+
#See http://guides.rubyonrails.org/security.html#mass-assignment for details
|
|
7
|
+
class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
|
|
8
|
+
Brakeman::Checks.add self
|
|
9
|
+
|
|
10
|
+
@description = "Finds instances of mass assignment"
|
|
11
|
+
|
|
12
|
+
def initialize(*)
|
|
13
|
+
super
|
|
14
|
+
@mass_assign_calls = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run_check
|
|
18
|
+
check_mass_assignment
|
|
19
|
+
check_permit!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find_mass_assign_calls
|
|
23
|
+
return @mass_assign_calls if @mass_assign_calls
|
|
24
|
+
|
|
25
|
+
models = []
|
|
26
|
+
tracker.models.each do |name, m|
|
|
27
|
+
if m.is_a? Hash
|
|
28
|
+
p m
|
|
29
|
+
end
|
|
30
|
+
if m.unprotected_model?
|
|
31
|
+
models << name
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return [] if models.empty?
|
|
36
|
+
|
|
37
|
+
Brakeman.debug "Finding possible mass assignment calls on #{models.length} models"
|
|
38
|
+
@mass_assign_calls = tracker.find_call :chained => true, :targets => models, :methods => [:new,
|
|
39
|
+
:attributes=,
|
|
40
|
+
:update_attributes,
|
|
41
|
+
:update_attributes!,
|
|
42
|
+
:create,
|
|
43
|
+
:create!,
|
|
44
|
+
:build,
|
|
45
|
+
:first_or_create,
|
|
46
|
+
:first_or_create!,
|
|
47
|
+
:first_or_initialize!,
|
|
48
|
+
:assign_attributes,
|
|
49
|
+
:update
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_mass_assignment
|
|
54
|
+
return if mass_assign_disabled?
|
|
55
|
+
|
|
56
|
+
Brakeman.debug "Processing possible mass assignment calls"
|
|
57
|
+
find_mass_assign_calls.each do |result|
|
|
58
|
+
process_result result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#All results should be Model.new(...) or Model.attributes=() calls
|
|
63
|
+
def process_result res
|
|
64
|
+
call = res[:call]
|
|
65
|
+
|
|
66
|
+
check = check_call call
|
|
67
|
+
|
|
68
|
+
if check and not call.original_line and not duplicate? res
|
|
69
|
+
add_result res
|
|
70
|
+
|
|
71
|
+
model = tracker.models[res[:chain].first]
|
|
72
|
+
|
|
73
|
+
attr_protected = (model and model.attr_protected)
|
|
74
|
+
|
|
75
|
+
if attr_protected and tracker.options[:ignore_attr_protected]
|
|
76
|
+
return
|
|
77
|
+
elsif input = include_user_input?(call.arglist)
|
|
78
|
+
first_arg = call.first_arg
|
|
79
|
+
|
|
80
|
+
if call? first_arg and (first_arg.method == :slice or first_arg.method == :only)
|
|
81
|
+
return
|
|
82
|
+
elsif not node_type? first_arg, :hash
|
|
83
|
+
if attr_protected
|
|
84
|
+
confidence = CONFIDENCE[:med]
|
|
85
|
+
else
|
|
86
|
+
confidence = CONFIDENCE[:high]
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
confidence = CONFIDENCE[:low]
|
|
90
|
+
end
|
|
91
|
+
elsif node_type? call.first_arg, :lit, :str
|
|
92
|
+
return
|
|
93
|
+
else
|
|
94
|
+
confidence = CONFIDENCE[:low]
|
|
95
|
+
input = nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
warn :result => res,
|
|
99
|
+
:warning_type => "Mass Assignment",
|
|
100
|
+
:warning_code => :mass_assign_call,
|
|
101
|
+
:message => "Unprotected mass assignment",
|
|
102
|
+
:code => call,
|
|
103
|
+
:user_input => input,
|
|
104
|
+
:confidence => confidence
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
res
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#Want to ignore calls to Model.new that have no arguments
|
|
111
|
+
def check_call call
|
|
112
|
+
process_call_args call
|
|
113
|
+
|
|
114
|
+
if call.method == :update
|
|
115
|
+
arg = call.second_arg
|
|
116
|
+
else
|
|
117
|
+
arg = call.first_arg
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if arg.nil? #empty new()
|
|
121
|
+
false
|
|
122
|
+
elsif hash? arg and not include_user_input? arg
|
|
123
|
+
false
|
|
124
|
+
elsif all_literal_args? call
|
|
125
|
+
false
|
|
126
|
+
else
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
LITERALS = Set[:lit, :true, :false, :nil, :string]
|
|
132
|
+
|
|
133
|
+
def all_literal_args? exp
|
|
134
|
+
if call? exp
|
|
135
|
+
exp.each_arg do |arg|
|
|
136
|
+
return false unless literal? arg
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
true
|
|
140
|
+
else
|
|
141
|
+
exp.all? do |arg|
|
|
142
|
+
literal? arg
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def literal? exp
|
|
149
|
+
if sexp? exp
|
|
150
|
+
if exp.node_type == :hash
|
|
151
|
+
all_literal_args? exp
|
|
152
|
+
else
|
|
153
|
+
LITERALS.include? exp.node_type
|
|
154
|
+
end
|
|
155
|
+
else
|
|
156
|
+
true
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Look for and warn about uses of Parameters#permit! for mass assignment
|
|
161
|
+
def check_permit!
|
|
162
|
+
tracker.find_call(:method => :permit!).each do |result|
|
|
163
|
+
if params? result[:call].target and not result[:chain].include? :slice
|
|
164
|
+
warn_on_permit! result
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Look for actual use of params in mass assignment to avoid
|
|
170
|
+
# warning about uses of Parameters#permit! without any mass assignment
|
|
171
|
+
# or when mass assignment is restricted by model instead.
|
|
172
|
+
def subsequent_mass_assignment? result
|
|
173
|
+
location = result[:location]
|
|
174
|
+
line = result[:call].line
|
|
175
|
+
find_mass_assign_calls.any? do |call|
|
|
176
|
+
call[:location] == location and
|
|
177
|
+
params? call[:call].first_arg and
|
|
178
|
+
call[:call].line >= line
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def warn_on_permit! result
|
|
183
|
+
return if duplicate? result or result[:call].original_line
|
|
184
|
+
add_result result
|
|
185
|
+
|
|
186
|
+
confidence = if subsequent_mass_assignment? result
|
|
187
|
+
CONFIDENCE[:high]
|
|
188
|
+
else
|
|
189
|
+
CONFIDENCE[:med]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
warn :result => result,
|
|
193
|
+
:warning_type => "Mass Assignment",
|
|
194
|
+
:warning_code => :mass_assign_permit!,
|
|
195
|
+
:message => "Parameters should be whitelisted for mass assignment",
|
|
196
|
+
:confidence => confidence
|
|
197
|
+
end
|
|
198
|
+
end
|