brakeman-lib 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,116 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
#Reports any calls to +validates_format_of+ which do not use +\A+ and +\z+
|
4
|
+
#as anchors in the given regular expression.
|
5
|
+
#
|
6
|
+
#For example:
|
7
|
+
#
|
8
|
+
# #Allows anything after new line
|
9
|
+
# validates_format_of :user_name, :with => /^\w+$/
|
10
|
+
class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
|
11
|
+
Brakeman::Checks.add self
|
12
|
+
|
13
|
+
@description = "Report uses of validates_format_of with improper anchors"
|
14
|
+
|
15
|
+
WITH = Sexp.new(:lit, :with)
|
16
|
+
FORMAT = Sexp.new(:lit, :format)
|
17
|
+
|
18
|
+
def run_check
|
19
|
+
active_record_models.each do |name, model|
|
20
|
+
@current_model = name
|
21
|
+
format_validations = model.options[:validates_format_of]
|
22
|
+
|
23
|
+
if format_validations
|
24
|
+
format_validations.each do |v|
|
25
|
+
process_validates_format_of v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
validates = model.options[:validates]
|
30
|
+
|
31
|
+
if validates
|
32
|
+
validates.each do |v|
|
33
|
+
process_validates v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#Check validates_format_of
|
40
|
+
def process_validates_format_of validator
|
41
|
+
if value = hash_access(validator.last, WITH)
|
42
|
+
check_regex value, validator
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#Check validates ..., :format => ...
|
47
|
+
def process_validates validator
|
48
|
+
hash_arg = validator.last
|
49
|
+
return unless hash? hash_arg
|
50
|
+
|
51
|
+
value = hash_access(hash_arg, FORMAT)
|
52
|
+
|
53
|
+
if hash? value
|
54
|
+
value = hash_access(value, WITH)
|
55
|
+
end
|
56
|
+
|
57
|
+
if value
|
58
|
+
check_regex value, validator
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Match secure regexp without extended option
|
63
|
+
SECURE_REGEXP_PATTERN = %r{
|
64
|
+
\A
|
65
|
+
\\A
|
66
|
+
.*
|
67
|
+
\\[zZ]
|
68
|
+
\z
|
69
|
+
}x
|
70
|
+
|
71
|
+
# Match secure of regexp with extended option
|
72
|
+
EXTENDED_SECURE_REGEXP_PATTERN = %r{
|
73
|
+
\A
|
74
|
+
\s*
|
75
|
+
\\A
|
76
|
+
.*
|
77
|
+
\\[zZ]
|
78
|
+
\s*
|
79
|
+
\z
|
80
|
+
}mx
|
81
|
+
|
82
|
+
#Issue warning if the regular expression does not use
|
83
|
+
#+\A+ and +\z+
|
84
|
+
def check_regex value, validator
|
85
|
+
return unless regexp? value
|
86
|
+
|
87
|
+
regex = value.value
|
88
|
+
unless secure_regex?(regex)
|
89
|
+
warn :model => @current_model,
|
90
|
+
:warning_type => "Format Validation",
|
91
|
+
:warning_code => :validation_regex,
|
92
|
+
:message => "Insufficient validation for '#{get_name validator}' using #{regex.inspect}. Use \\A and \\z as anchors",
|
93
|
+
:line => value.line,
|
94
|
+
:confidence => CONFIDENCE[:high]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#Get the name of the attribute being validated.
|
99
|
+
def get_name validator
|
100
|
+
name = validator[1]
|
101
|
+
|
102
|
+
if sexp? name
|
103
|
+
name.value
|
104
|
+
else
|
105
|
+
name
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def secure_regex?(regex)
|
112
|
+
extended_regex = Regexp::EXTENDED == regex.options & Regexp::EXTENDED
|
113
|
+
regex_pattern = extended_regex ? EXTENDED_SECURE_REGEXP_PATTERN : SECURE_REGEXP_PATTERN
|
114
|
+
regex_pattern =~ regex.source
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckWeakHash < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add_optional self
|
5
|
+
|
6
|
+
@description = "Checks for use of weak hashes like MD5"
|
7
|
+
|
8
|
+
DIGEST_CALLS = [:base64digest, :digest, :hexdigest, :new]
|
9
|
+
|
10
|
+
def run_check
|
11
|
+
tracker.find_call(:targets => [:'Digest::MD5', :'Digest::SHA1', :'OpenSSL::Digest::MD5', :'OpenSSL::Digest::SHA1'], :nested => true).each do |result|
|
12
|
+
process_hash_result result
|
13
|
+
end
|
14
|
+
|
15
|
+
tracker.find_call(:target => :'Digest::HMAC', :methods => [:new, :hexdigest], :nested => true).each do |result|
|
16
|
+
process_hmac_result result
|
17
|
+
end
|
18
|
+
|
19
|
+
tracker.find_call(:targets => [:'OpenSSL::Digest::Digest', :'OpenSSL::Digest'], :method => :new).each do |result|
|
20
|
+
process_openssl_result result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_hash_result result
|
25
|
+
return if duplicate? result
|
26
|
+
add_result result
|
27
|
+
|
28
|
+
input = nil
|
29
|
+
call = result[:call]
|
30
|
+
|
31
|
+
if DIGEST_CALLS.include? call.method
|
32
|
+
if input = user_input_as_arg?(call)
|
33
|
+
confidence = CONFIDENCE[:high]
|
34
|
+
elsif input = hashing_password?(call)
|
35
|
+
confidence = CONFIDENCE[:high]
|
36
|
+
else
|
37
|
+
confidence = CONFIDENCE[:med]
|
38
|
+
end
|
39
|
+
else
|
40
|
+
confidence = CONFIDENCE[:med]
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
alg = case call.target.last
|
45
|
+
when :MD5
|
46
|
+
" (MD5)"
|
47
|
+
when :SHA1
|
48
|
+
" (SHA1)"
|
49
|
+
else
|
50
|
+
""
|
51
|
+
end
|
52
|
+
|
53
|
+
warn :result => result,
|
54
|
+
:warning_type => "Weak Hash",
|
55
|
+
:warning_code => :weak_hash_digest,
|
56
|
+
:message => "Weak hashing algorithm#{alg} used",
|
57
|
+
:confidence => confidence,
|
58
|
+
:user_input => input
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_hmac_result result
|
62
|
+
return if duplicate? result
|
63
|
+
add_result result
|
64
|
+
|
65
|
+
call = result[:call]
|
66
|
+
|
67
|
+
alg = case call.third_arg.last
|
68
|
+
when :MD5
|
69
|
+
'MD5'
|
70
|
+
when :SHA1
|
71
|
+
'SHA1'
|
72
|
+
else
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
warn :result => result,
|
77
|
+
:warning_type => "Weak Hash",
|
78
|
+
:warning_code => :weak_hash_hmac,
|
79
|
+
:message => "Weak hashing algorithm (#{alg}) used in HMAC",
|
80
|
+
:confidence => CONFIDENCE[:med]
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_openssl_result result
|
84
|
+
return if duplicate? result
|
85
|
+
add_result result
|
86
|
+
|
87
|
+
arg = result[:call].first_arg
|
88
|
+
|
89
|
+
if string? arg
|
90
|
+
alg = arg.value.upcase
|
91
|
+
|
92
|
+
if alg == 'MD5' or alg == 'SHA1'
|
93
|
+
warn :result => result,
|
94
|
+
:warning_type => "Weak Hash",
|
95
|
+
:warning_code => :weak_hash_digest,
|
96
|
+
:message => "Weak hashing algorithm (#{alg}) used",
|
97
|
+
:confidence => CONFIDENCE[:med]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def user_input_as_arg? call
|
103
|
+
call.each_arg do |arg|
|
104
|
+
if input = include_user_input?(arg)
|
105
|
+
return input
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def hashing_password? call
|
113
|
+
call.each_arg do |arg|
|
114
|
+
@has_password = false
|
115
|
+
|
116
|
+
process arg
|
117
|
+
|
118
|
+
if @has_password
|
119
|
+
return @has_password
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
|
126
|
+
def process_call exp
|
127
|
+
if exp.method == :password
|
128
|
+
@has_password = exp
|
129
|
+
else
|
130
|
+
process_default exp
|
131
|
+
end
|
132
|
+
|
133
|
+
exp
|
134
|
+
end
|
135
|
+
|
136
|
+
def process_ivar exp
|
137
|
+
if exp.value == :@password
|
138
|
+
@has_password = exp
|
139
|
+
end
|
140
|
+
|
141
|
+
exp
|
142
|
+
end
|
143
|
+
|
144
|
+
def process_lvar exp
|
145
|
+
if exp.value == :password
|
146
|
+
@has_password = exp
|
147
|
+
end
|
148
|
+
|
149
|
+
exp
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
#Check for bypassing mass assignment protection
|
4
|
+
#with without_protection => true
|
5
|
+
#
|
6
|
+
#Only for Rails 3.1
|
7
|
+
class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
|
8
|
+
Brakeman::Checks.add self
|
9
|
+
|
10
|
+
@description = "Check for mass assignment using without_protection"
|
11
|
+
|
12
|
+
def run_check
|
13
|
+
if version_between? "0.0.0", "3.0.99"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
return if active_record_models.empty?
|
18
|
+
|
19
|
+
Brakeman.debug "Finding all mass assignments"
|
20
|
+
calls = tracker.find_call :targets => active_record_models.keys, :methods => [:new,
|
21
|
+
:attributes=,
|
22
|
+
:update_attributes,
|
23
|
+
:update_attributes!,
|
24
|
+
:create,
|
25
|
+
:create!]
|
26
|
+
|
27
|
+
Brakeman.debug "Processing all mass assignments"
|
28
|
+
calls.each do |result|
|
29
|
+
process_result result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#All results should be Model.new(...) or Model.attributes=() calls
|
34
|
+
def process_result res
|
35
|
+
call = res[:call]
|
36
|
+
last_arg = call.last_arg
|
37
|
+
|
38
|
+
if hash? last_arg and not call.original_line and not duplicate? res
|
39
|
+
|
40
|
+
if value = hash_access(last_arg, :without_protection)
|
41
|
+
if true? value
|
42
|
+
add_result res
|
43
|
+
|
44
|
+
if input = include_user_input?(call.arglist)
|
45
|
+
confidence = CONFIDENCE[:high]
|
46
|
+
elsif all_literals? call
|
47
|
+
return
|
48
|
+
else
|
49
|
+
confidence = CONFIDENCE[:med]
|
50
|
+
end
|
51
|
+
|
52
|
+
warn :result => res,
|
53
|
+
:warning_type => "Mass Assignment",
|
54
|
+
:warning_code => :mass_assign_without_protection,
|
55
|
+
:message => "Unprotected mass assignment",
|
56
|
+
:code => call,
|
57
|
+
:user_input => input,
|
58
|
+
:confidence => confidence
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def all_literals? call
|
66
|
+
call.each_arg do |arg|
|
67
|
+
if hash? arg
|
68
|
+
hash_iterate arg do |k, v|
|
69
|
+
unless node_type? k, :str, :lit, :false, :true and node_type? v, :str, :lit, :false, :true
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
else
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
true
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckXMLDoS < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Checks for XML denial of service (CVE-2015-3227)"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
version = rails_version
|
10
|
+
|
11
|
+
fix_version = case
|
12
|
+
when version_between?("2.0.0", "3.2.21")
|
13
|
+
"3.2.22"
|
14
|
+
when version_between?("4.1.0", "4.1.10")
|
15
|
+
"4.1.11"
|
16
|
+
when version_between?("4.2.0", "4.2.1")
|
17
|
+
"4.2.2"
|
18
|
+
when version_between?("4.0.0", "4.0.99")
|
19
|
+
"4.2.2"
|
20
|
+
when (version.nil? and tracker.options[:rails3])
|
21
|
+
version = "3.x"
|
22
|
+
"3.2.22"
|
23
|
+
when (version.nil? and tracker.options[:rails4])
|
24
|
+
version = "4.x"
|
25
|
+
"4.2.2"
|
26
|
+
else
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
return if has_workaround?
|
31
|
+
|
32
|
+
message = "Rails #{version} is vulnerable to denial of service via XML parsing (CVE-2015-3227). Upgrade to Rails version #{fix_version}"
|
33
|
+
|
34
|
+
warn :warning_type => "Denial of Service",
|
35
|
+
:warning_code => :CVE_2015_3227,
|
36
|
+
:message => message,
|
37
|
+
:confidence => CONFIDENCE[:med],
|
38
|
+
:gem_info => gemfile_or_environment,
|
39
|
+
:link_path => "https://groups.google.com/d/msg/rubyonrails-security/bahr2JLnxvk/x4EocXnHPp8J"
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_workaround?
|
43
|
+
tracker.check_initializers(:"ActiveSupport::XmlMini", :backend=).any? do |match|
|
44
|
+
arg = match.call.first_arg
|
45
|
+
if string? arg
|
46
|
+
value = arg.value
|
47
|
+
value == 'Nokogiri' or value == 'LibXML'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Checks for YAML parsing vulnerabilities (CVE-2013-0156)"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
return unless version_between? "0.0.0", "2.3.14" or
|
10
|
+
version_between? "3.0.0", "3.0.18" or
|
11
|
+
version_between? "3.1.0", "3.1.9" or
|
12
|
+
version_between? "3.2.0", "3.2.10"
|
13
|
+
|
14
|
+
unless disabled_xml_parser? or disabled_xml_dangerous_types?
|
15
|
+
new_version = if version_between? "0.0.0", "2.3.14"
|
16
|
+
"2.3.15"
|
17
|
+
elsif version_between? "3.0.0", "3.0.18"
|
18
|
+
"3.0.19"
|
19
|
+
elsif version_between? "3.1.0", "3.1.9"
|
20
|
+
"3.1.10"
|
21
|
+
elsif version_between? "3.2.0", "3.2.10"
|
22
|
+
"3.2.11"
|
23
|
+
end
|
24
|
+
|
25
|
+
message = "Rails #{rails_version} has a remote code execution vulnerability: upgrade to #{new_version} or disable XML parsing"
|
26
|
+
|
27
|
+
warn :warning_type => "Remote Code Execution",
|
28
|
+
:warning_code => :CVE_2013_0156,
|
29
|
+
:message => message,
|
30
|
+
:confidence => CONFIDENCE[:high],
|
31
|
+
:gem_info => gemfile_or_environment,
|
32
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion"
|
33
|
+
end
|
34
|
+
|
35
|
+
#Warn if app accepts YAML
|
36
|
+
if version_between?("0.0.0", "2.3.14") and enabled_yaml_parser?
|
37
|
+
message = "Parsing YAML request parameters enables remote code execution: disable YAML parser"
|
38
|
+
|
39
|
+
warn :warning_type => "Remote Code Execution",
|
40
|
+
:warning_code => :CVE_2013_0156,
|
41
|
+
:message => message,
|
42
|
+
:confidence => CONFIDENCE[:high],
|
43
|
+
:gem_info => gemfile_or_environment,
|
44
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def disabled_xml_parser?
|
49
|
+
if version_between? "0.0.0", "2.3.14"
|
50
|
+
#Look for ActionController::Base.param_parsers.delete(Mime::XML)
|
51
|
+
params_parser = s(:call,
|
52
|
+
s(:colon2, s(:const, :ActionController), :Base),
|
53
|
+
:param_parsers)
|
54
|
+
|
55
|
+
matches = tracker.check_initializers(params_parser, :delete)
|
56
|
+
else
|
57
|
+
#Look for ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)
|
58
|
+
matches = tracker.check_initializers(:"ActionDispatch::ParamsParser::DEFAULT_PARSERS", :delete)
|
59
|
+
end
|
60
|
+
|
61
|
+
unless matches.empty?
|
62
|
+
mime_xml = s(:colon2, s(:const, :Mime), :XML)
|
63
|
+
|
64
|
+
matches.each do |result|
|
65
|
+
if result.call.first_arg == mime_xml
|
66
|
+
return true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
#Look for ActionController::Base.param_parsers[Mime::YAML] = :yaml
|
75
|
+
#in Rails 2.x apps
|
76
|
+
def enabled_yaml_parser?
|
77
|
+
param_parsers = s(:call,
|
78
|
+
s(:colon2, s(:const, :ActionController), :Base),
|
79
|
+
:param_parsers)
|
80
|
+
|
81
|
+
matches = tracker.check_initializers(param_parsers, :[]=)
|
82
|
+
|
83
|
+
mime_yaml = s(:colon2, s(:const, :Mime), :YAML)
|
84
|
+
|
85
|
+
matches.each do |result|
|
86
|
+
if result.call.first_arg == mime_yaml and
|
87
|
+
symbol? result.call.second_arg and
|
88
|
+
result.call.second_arg.value == :yaml
|
89
|
+
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def disabled_xml_dangerous_types?
|
98
|
+
if version_between? "0.0.0", "2.3.14"
|
99
|
+
matches = tracker.check_initializers(:"ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING", :delete)
|
100
|
+
else
|
101
|
+
matches = tracker.check_initializers(:"ActiveSupport::XmlMini::PARSING", :delete)
|
102
|
+
end
|
103
|
+
|
104
|
+
symbols_off = false
|
105
|
+
yaml_off = false
|
106
|
+
|
107
|
+
matches.each do |result|
|
108
|
+
arg = result.call.first_arg
|
109
|
+
|
110
|
+
if string? arg
|
111
|
+
if arg.value == "yaml"
|
112
|
+
yaml_off = true
|
113
|
+
elsif arg.value == "symbol"
|
114
|
+
symbols_off = true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
symbols_off and yaml_off
|
120
|
+
end
|
121
|
+
end
|