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,86 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks if default routes are allowed in routes.rb
|
|
4
|
+
class Brakeman::CheckDefaultRoutes < Brakeman::BaseCheck
|
|
5
|
+
Brakeman::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Checks for default routes"
|
|
8
|
+
|
|
9
|
+
#Checks for :allow_all_actions globally and for individual routes
|
|
10
|
+
#if it is not enabled globally.
|
|
11
|
+
def run_check
|
|
12
|
+
check_for_default_routes
|
|
13
|
+
check_for_action_globs
|
|
14
|
+
check_for_cve_2014_0130
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check_for_default_routes
|
|
18
|
+
if allow_all_actions?
|
|
19
|
+
#Default routes are enabled globally
|
|
20
|
+
warn :warning_type => "Default Routes",
|
|
21
|
+
:warning_code => :all_default_routes,
|
|
22
|
+
:message => "All public methods in controllers are available as actions in routes.rb",
|
|
23
|
+
:line => tracker.routes[:allow_all_actions].line,
|
|
24
|
+
:confidence => CONFIDENCE[:high],
|
|
25
|
+
:file => "#{tracker.app_path}/config/routes.rb"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def check_for_action_globs
|
|
30
|
+
return if allow_all_actions?
|
|
31
|
+
Brakeman.debug "Checking each controller for default routes"
|
|
32
|
+
|
|
33
|
+
tracker.routes.each do |name, actions|
|
|
34
|
+
if actions.is_a? Array and actions[0] == :allow_all_actions
|
|
35
|
+
@actions_allowed_on_controller = true
|
|
36
|
+
if actions[1].is_a? Hash and actions[1][:allow_verb]
|
|
37
|
+
verb = actions[1][:allow_verb]
|
|
38
|
+
else
|
|
39
|
+
verb = "any"
|
|
40
|
+
end
|
|
41
|
+
warn :controller => name,
|
|
42
|
+
:warning_type => "Default Routes",
|
|
43
|
+
:warning_code => :controller_default_routes,
|
|
44
|
+
:message => "Any public method in #{name} can be used as an action for #{verb} requests.",
|
|
45
|
+
:line => actions[2],
|
|
46
|
+
:confidence => CONFIDENCE[:med],
|
|
47
|
+
:file => "#{tracker.app_path}/config/routes.rb"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def check_for_cve_2014_0130
|
|
53
|
+
case
|
|
54
|
+
when lts_version?("2.3.18.9")
|
|
55
|
+
#TODO: Should support LTS 3.0.20 too
|
|
56
|
+
return
|
|
57
|
+
when version_between?("2.0.0", "2.3.18")
|
|
58
|
+
upgrade = "3.2.18"
|
|
59
|
+
when version_between?("3.0.0", "3.2.17")
|
|
60
|
+
upgrade = "3.2.18"
|
|
61
|
+
when version_between?("4.0.0", "4.0.4")
|
|
62
|
+
upgrade = "4.0.5"
|
|
63
|
+
when version_between?("4.1.0", "4.1.0")
|
|
64
|
+
upgrade = "4.1.1"
|
|
65
|
+
else
|
|
66
|
+
return
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if allow_all_actions? or @actions_allowed_on_controller
|
|
70
|
+
confidence = CONFIDENCE[:high]
|
|
71
|
+
else
|
|
72
|
+
confidence = CONFIDENCE[:med]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
warn :warning_type => "Remote Code Execution",
|
|
76
|
+
:warning_code => :CVE_2014_0130,
|
|
77
|
+
:message => "Rails #{rails_version} with globbing routes is vulnerable to directory traversal and remote code execution. Patch or upgrade to #{upgrade}",
|
|
78
|
+
:confidence => confidence,
|
|
79
|
+
:file => "#{tracker.app_path}/config/routes.rb",
|
|
80
|
+
:link => "http://matasano.com/research/AnatomyOfRailsVuln-CVE-2014-0130.pdf"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def allow_all_actions?
|
|
84
|
+
tracker.routes[:allow_all_actions]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckDeserialize < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for unsafe deserialization of objects"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
check_yaml
|
|
10
|
+
check_csv
|
|
11
|
+
check_marshal
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def check_yaml
|
|
15
|
+
check_methods :YAML, :load, :load_documents, :load_stream, :parse_documents, :parse_stream
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def check_csv
|
|
19
|
+
check_methods :CSV, :load
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def check_marshal
|
|
23
|
+
check_methods :Marshal, :load, :restore
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def check_methods target, *methods
|
|
27
|
+
tracker.find_call(:target => target, :methods => methods ).each do |result|
|
|
28
|
+
check_deserialize result, target
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def check_deserialize result, target, arg = nil
|
|
33
|
+
return if duplicate? result
|
|
34
|
+
add_result result
|
|
35
|
+
|
|
36
|
+
arg ||= result[:call].first_arg
|
|
37
|
+
method = result[:call].method
|
|
38
|
+
|
|
39
|
+
if input = has_immediate_user_input?(arg)
|
|
40
|
+
confidence = CONFIDENCE[:high]
|
|
41
|
+
elsif input = include_user_input?(arg)
|
|
42
|
+
confidence = CONFIDENCE[:med]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if confidence
|
|
46
|
+
message = "#{target}.#{method} called with #{friendly_type_of input}"
|
|
47
|
+
|
|
48
|
+
warn :result => result,
|
|
49
|
+
:warning_type => "Remote Code Execution",
|
|
50
|
+
:warning_code => :unsafe_deserialize,
|
|
51
|
+
:message => message,
|
|
52
|
+
:user_input => input,
|
|
53
|
+
:confidence => confidence,
|
|
54
|
+
:link_path => "unsafe_deserialization"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
# Check for detailed exceptions enabled for production
|
|
4
|
+
class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
|
|
5
|
+
Brakeman::Checks.add self
|
|
6
|
+
|
|
7
|
+
LOCAL_REQUEST = s(:call, s(:call, nil, :request), :local?)
|
|
8
|
+
|
|
9
|
+
@description = "Checks for information disclosure displayed via detailed exceptions"
|
|
10
|
+
|
|
11
|
+
def run_check
|
|
12
|
+
check_local_request_config
|
|
13
|
+
check_detailed_exceptions
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check_local_request_config
|
|
17
|
+
if true? tracker.config.rails[:consider_all_requests_local]
|
|
18
|
+
warn :warning_type => "Information Disclosure",
|
|
19
|
+
:warning_code => :local_request_config,
|
|
20
|
+
:message => "Detailed exceptions are enabled in production",
|
|
21
|
+
:confidence => CONFIDENCE[:high],
|
|
22
|
+
:file => "config/environments/production.rb"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def check_detailed_exceptions
|
|
27
|
+
tracker.controllers.each do |name, controller|
|
|
28
|
+
controller.methods_public.each do |method_name, definition|
|
|
29
|
+
src = definition[:src]
|
|
30
|
+
body = src.body.last
|
|
31
|
+
next unless body
|
|
32
|
+
|
|
33
|
+
if method_name == :show_detailed_exceptions? and not safe? body
|
|
34
|
+
if true? body
|
|
35
|
+
confidence = CONFIDENCE[:high]
|
|
36
|
+
else
|
|
37
|
+
confidence = CONFIDENCE[:med]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
warn :warning_type => "Information Disclosure",
|
|
41
|
+
:warning_code => :detailed_exceptions,
|
|
42
|
+
:message => "Detailed exceptions may be enabled in 'show_detailed_exceptions?'",
|
|
43
|
+
:confidence => confidence,
|
|
44
|
+
:code => src,
|
|
45
|
+
:file => definition[:file]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def safe? body
|
|
52
|
+
false? body or
|
|
53
|
+
body == LOCAL_REQUEST
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckDigestDoS < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for digest authentication DoS vulnerability"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
message = "Vulnerability in digest authentication (CVE-2012-3424). Upgrade to Rails version "
|
|
10
|
+
|
|
11
|
+
if version_between? "3.0.0", "3.0.15"
|
|
12
|
+
message << "3.0.16"
|
|
13
|
+
elsif version_between? "3.1.0", "3.1.6"
|
|
14
|
+
message << "3.1.7"
|
|
15
|
+
elsif version_between? "3.2.0", "3.2.5"
|
|
16
|
+
message << "3.2.7"
|
|
17
|
+
else
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if with_http_digest?
|
|
22
|
+
confidence = CONFIDENCE[:high]
|
|
23
|
+
else
|
|
24
|
+
confidence = CONFIDENCE[:low]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
warn :warning_type => "Denial of Service",
|
|
28
|
+
:warning_code => :CVE_2012_3424,
|
|
29
|
+
:message => message,
|
|
30
|
+
:confidence => confidence,
|
|
31
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/vxJjrc15qYM/discussion",
|
|
32
|
+
:gem_info => gemfile_or_environment
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def with_http_digest?
|
|
36
|
+
not tracker.find_call(:target => false, :method => [:authenticate_or_request_with_http_digest, :authenticate_with_http_digest]).empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#This check looks for regexes that include user input.
|
|
4
|
+
class Brakeman::CheckDynamicFinders < Brakeman::BaseCheck
|
|
5
|
+
Brakeman::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Check unsafe usage of find_by_*"
|
|
8
|
+
|
|
9
|
+
def run_check
|
|
10
|
+
if tracker.config.has_gem? :mysql and version_between? '2.0.0', '4.1.99'
|
|
11
|
+
tracker.find_call(:method => /^find_by_/).each do |result|
|
|
12
|
+
process_result result
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process_result result
|
|
18
|
+
return if duplicate? result or result[:call].original_line
|
|
19
|
+
add_result result
|
|
20
|
+
|
|
21
|
+
call = result[:call]
|
|
22
|
+
|
|
23
|
+
if potentially_dangerous? call.method
|
|
24
|
+
call.each_arg do |arg|
|
|
25
|
+
if params? arg and not safe_call? arg
|
|
26
|
+
warn :result => result,
|
|
27
|
+
:warning_type => "SQL Injection",
|
|
28
|
+
:warning_code => :sql_injection_dynamic_finder,
|
|
29
|
+
:message => "MySQL integer conversion may cause 0 to match any string",
|
|
30
|
+
:confidence => CONFIDENCE[:med],
|
|
31
|
+
:user_input => arg
|
|
32
|
+
|
|
33
|
+
break
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def safe_call? arg
|
|
40
|
+
return false unless call? arg
|
|
41
|
+
|
|
42
|
+
meth = arg.method
|
|
43
|
+
meth == :to_s or meth == :to_i
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def potentially_dangerous? method_name
|
|
47
|
+
method_name.match /^find_by_.*(token|guid|password|api_key|activation|code|private|reset)/
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check for versions with vulnerable html escape method
|
|
4
|
+
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/56bffb5923ab1195
|
|
5
|
+
class Brakeman::CheckEscapeFunction < Brakeman::BaseCheck
|
|
6
|
+
Brakeman::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Checks for versions before 2.3.14 which have a vulnerable escape method"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
if version_between?('2.0.0', '2.3.13') and RUBY_VERSION < '1.9.0'
|
|
12
|
+
|
|
13
|
+
warn :warning_type => 'Cross Site Scripting',
|
|
14
|
+
:warning_code => :CVE_2011_2932,
|
|
15
|
+
:message => 'Versions before 2.3.14 have a vulnerability in escape method when used with Ruby 1.8: CVE-2011-2932',
|
|
16
|
+
:confidence => CONFIDENCE[:high],
|
|
17
|
+
:gem_info => gemfile_or_environment,
|
|
18
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/Vr_7WSOrEZU/discussion"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#This check looks for calls to +eval+, +instance_eval+, etc. which include
|
|
4
|
+
#user input.
|
|
5
|
+
class Brakeman::CheckEvaluation < Brakeman::BaseCheck
|
|
6
|
+
Brakeman::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Searches for evaluation of user input"
|
|
9
|
+
|
|
10
|
+
#Process calls
|
|
11
|
+
def run_check
|
|
12
|
+
Brakeman.debug "Finding eval-like calls"
|
|
13
|
+
calls = tracker.find_call :method => [:eval, :instance_eval, :class_eval, :module_eval]
|
|
14
|
+
|
|
15
|
+
Brakeman.debug "Processing eval-like calls"
|
|
16
|
+
calls.each do |call|
|
|
17
|
+
process_result call
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#Warns if eval includes user input
|
|
22
|
+
def process_result result
|
|
23
|
+
return if duplicate? result or result[:call].original_line
|
|
24
|
+
add_result result
|
|
25
|
+
|
|
26
|
+
if input = include_user_input?(result[:call].arglist)
|
|
27
|
+
warn :result => result,
|
|
28
|
+
:warning_type => "Dangerous Eval",
|
|
29
|
+
:warning_code => :code_eval,
|
|
30
|
+
:message => "User input in eval",
|
|
31
|
+
:code => result[:call],
|
|
32
|
+
:user_input => input,
|
|
33
|
+
:confidence => CONFIDENCE[:high]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks for string interpolation and parameters in calls to
|
|
4
|
+
#Kernel#system, Kernel#exec, Kernel#syscall, and inside backticks.
|
|
5
|
+
#
|
|
6
|
+
#Examples of command injection vulnerabilities:
|
|
7
|
+
#
|
|
8
|
+
# system("rf -rf #{params[:file]}")
|
|
9
|
+
# exec(params[:command])
|
|
10
|
+
# `unlink #{params[:something}`
|
|
11
|
+
class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
12
|
+
Brakeman::Checks.add self
|
|
13
|
+
|
|
14
|
+
@description = "Finds instances of possible command injection"
|
|
15
|
+
|
|
16
|
+
SAFE_VALUES = [s(:const, :RAILS_ROOT),
|
|
17
|
+
s(:call, s(:const, :Rails), :root),
|
|
18
|
+
s(:call, s(:const, :Rails), :env)]
|
|
19
|
+
|
|
20
|
+
#Check models, controllers, and views for command injection.
|
|
21
|
+
def run_check
|
|
22
|
+
Brakeman.debug "Finding system calls using ``"
|
|
23
|
+
check_for_backticks tracker
|
|
24
|
+
|
|
25
|
+
check_open_calls
|
|
26
|
+
|
|
27
|
+
Brakeman.debug "Finding other system calls"
|
|
28
|
+
calls = tracker.find_call :targets => [:IO, :Open3, :Kernel, :'POSIX::Spawn', :Process, nil],
|
|
29
|
+
:methods => [:capture2, :capture2e, :capture3, :exec, :pipeline, :pipeline_r,
|
|
30
|
+
:pipeline_rw, :pipeline_start, :pipeline_w, :popen, :popen2, :popen2e,
|
|
31
|
+
:popen3, :spawn, :syscall, :system]
|
|
32
|
+
|
|
33
|
+
Brakeman.debug "Processing system calls"
|
|
34
|
+
calls.each do |result|
|
|
35
|
+
process_result result
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#Processes results from Tracker#find_call.
|
|
40
|
+
def process_result result
|
|
41
|
+
call = result[:call]
|
|
42
|
+
args = call.arglist
|
|
43
|
+
first_arg = call.first_arg
|
|
44
|
+
|
|
45
|
+
case call.method
|
|
46
|
+
when :popen
|
|
47
|
+
unless array? first_arg
|
|
48
|
+
failure = include_user_input?(args) || dangerous_interp?(args)
|
|
49
|
+
end
|
|
50
|
+
when :system, :exec
|
|
51
|
+
failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
|
|
52
|
+
else
|
|
53
|
+
failure = include_user_input?(args) || dangerous_interp?(args)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if failure and not duplicate? result
|
|
57
|
+
add_result result
|
|
58
|
+
|
|
59
|
+
if failure.type == :interp #Not from user input
|
|
60
|
+
confidence = CONFIDENCE[:med]
|
|
61
|
+
else
|
|
62
|
+
confidence = CONFIDENCE[:high]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
warn :result => result,
|
|
66
|
+
:warning_type => "Command Injection",
|
|
67
|
+
:warning_code => :command_injection,
|
|
68
|
+
:message => "Possible command injection",
|
|
69
|
+
:code => call,
|
|
70
|
+
:user_input => failure,
|
|
71
|
+
:confidence => confidence
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def check_open_calls
|
|
76
|
+
tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|
|
|
77
|
+
if match = dangerous_open_arg?(result[:call].first_arg)
|
|
78
|
+
warn :result => result,
|
|
79
|
+
:warning_type => "Command Injection",
|
|
80
|
+
:warning_code => :command_injection,
|
|
81
|
+
:message => "Possible command injection in open()",
|
|
82
|
+
:user_input => match,
|
|
83
|
+
:confidence => CONFIDENCE[:high]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def dangerous_open_arg? exp
|
|
89
|
+
if string_interp? exp
|
|
90
|
+
# Check for input at start of string
|
|
91
|
+
exp[1] == "" and
|
|
92
|
+
node_type? exp[2], :evstr and
|
|
93
|
+
has_immediate_user_input? exp[2]
|
|
94
|
+
else
|
|
95
|
+
has_immediate_user_input? exp
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#Looks for calls using backticks such as
|
|
100
|
+
#
|
|
101
|
+
# `rm -rf #{params[:file]}`
|
|
102
|
+
def check_for_backticks tracker
|
|
103
|
+
tracker.find_call(:target => nil, :method => :`).each do |result|
|
|
104
|
+
process_backticks result
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#Processes backticks.
|
|
109
|
+
def process_backticks result
|
|
110
|
+
return if duplicate? result
|
|
111
|
+
|
|
112
|
+
add_result result
|
|
113
|
+
|
|
114
|
+
exp = result[:call]
|
|
115
|
+
|
|
116
|
+
if input = include_user_input?(exp)
|
|
117
|
+
confidence = CONFIDENCE[:high]
|
|
118
|
+
elsif input = dangerous?(exp)
|
|
119
|
+
confidence = CONFIDENCE[:med]
|
|
120
|
+
else
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
warn :result => result,
|
|
125
|
+
:warning_type => "Command Injection",
|
|
126
|
+
:warning_code => :command_injection,
|
|
127
|
+
:message => "Possible command injection",
|
|
128
|
+
:code => exp,
|
|
129
|
+
:user_input => input,
|
|
130
|
+
:confidence => confidence
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def dangerous? exp
|
|
134
|
+
exp.each_sexp do |e|
|
|
135
|
+
next if node_type? e, :lit, :str
|
|
136
|
+
next if SAFE_VALUES.include? e
|
|
137
|
+
|
|
138
|
+
if call? e and e.method == :to_s
|
|
139
|
+
e = e.target
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
if node_type? e, :or, :evstr, :dstr
|
|
143
|
+
if res = dangerous?(e)
|
|
144
|
+
return res
|
|
145
|
+
end
|
|
146
|
+
else
|
|
147
|
+
return e
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
false
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def dangerous_interp? exp
|
|
155
|
+
match = include_interp? exp
|
|
156
|
+
return unless match
|
|
157
|
+
interp = match.match
|
|
158
|
+
|
|
159
|
+
interp.each_sexp do |e|
|
|
160
|
+
if res = dangerous?(e)
|
|
161
|
+
return Match.new(:interp, res)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
false
|
|
166
|
+
end
|
|
167
|
+
end
|