contrast-agent 6.6.3 → 6.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.gitmodules +0 -3
- data/ext/cs__scope/cs__scope.c +1 -1
- data/lib/contrast/agent/assess/contrast_event.rb +2 -24
- data/lib/contrast/agent/assess/events/source_event.rb +7 -61
- data/lib/contrast/agent/assess/finalizers/hash.rb +11 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +0 -55
- data/lib/contrast/agent/assess/policy/policy_node.rb +3 -3
- data/lib/contrast/agent/assess/policy/policy_node_utils.rb +0 -1
- data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
- data/lib/contrast/agent/assess/policy/source_method.rb +24 -1
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -5
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +6 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +38 -119
- data/lib/contrast/agent/assess/policy/trigger_node.rb +3 -3
- data/lib/contrast/agent/assess/property/evented.rb +2 -12
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +42 -82
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +11 -27
- data/lib/contrast/agent/assess/rule/response/body_rule.rb +1 -3
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +77 -62
- data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +6 -1
- data/lib/contrast/agent/assess/rule/response/header_rule.rb +5 -5
- data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +1 -7
- data/lib/contrast/agent/at_exit_hook.rb +1 -7
- data/lib/contrast/agent/excluder.rb +206 -0
- data/lib/contrast/agent/exclusion_matcher.rb +6 -0
- data/lib/contrast/agent/inventory/database_config.rb +18 -23
- data/lib/contrast/agent/middleware.rb +0 -1
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +4 -0
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -0
- data/lib/contrast/agent/protect/rule/base.rb +64 -24
- data/lib/contrast/agent/protect/rule/base_service.rb +1 -0
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +18 -104
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +129 -0
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +169 -0
- data/lib/contrast/agent/protect/rule/deserialization.rb +7 -5
- data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
- data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +51 -0
- data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +67 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +6 -31
- data/lib/contrast/agent/protect/rule/xxe.rb +11 -6
- data/lib/contrast/agent/protect/rule.rb +3 -1
- data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +91 -36
- data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
- data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
- data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
- data/lib/contrast/agent/reporting/details/details.rb +18 -0
- data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
- data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
- data/lib/contrast/agent/reporting/details/sqli_dangerous_functions.rb +22 -0
- data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
- data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
- data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
- data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
- data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
- data/lib/contrast/agent/reporting/reporter.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +81 -15
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
- data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +7 -21
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +19 -49
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +12 -9
- data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +23 -21
- data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +5 -18
- data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +1 -0
- data/lib/contrast/{api/decorators/trace_taint_range_tags.rb → agent/reporting/reporting_events/finding_event_taint_range_tags.rb} +7 -6
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +10 -14
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +11 -0
- data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +3 -1
- data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +11 -23
- data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +8 -26
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -7
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +3 -3
- data/lib/contrast/agent/request.rb +4 -2
- data/lib/contrast/agent/request_context.rb +12 -15
- data/lib/contrast/agent/request_context_extend.rb +67 -69
- data/lib/contrast/agent/request_handler.rb +1 -11
- data/lib/contrast/agent/response.rb +0 -18
- data/lib/contrast/agent/service_heartbeat.rb +1 -1
- data/lib/contrast/agent/telemetry/events/event.rb +1 -1
- data/lib/contrast/agent/telemetry/events/metric_event.rb +1 -1
- data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +3 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +2 -3
- data/lib/contrast/api/communication/socket_client.rb +4 -4
- data/lib/contrast/api/communication/speedracer.rb +4 -8
- data/lib/contrast/api/decorators/agent_startup.rb +5 -6
- data/lib/contrast/api/decorators/application_settings.rb +2 -1
- data/lib/contrast/api/decorators/application_startup.rb +6 -6
- data/lib/contrast/api/decorators/message.rb +0 -4
- data/lib/contrast/api/decorators/rasp_rule_sample.rb +0 -6
- data/lib/contrast/api/decorators.rb +0 -6
- data/lib/contrast/api/dtm.pb.rb +0 -489
- data/lib/contrast/components/agent.rb +16 -12
- data/lib/contrast/components/api.rb +10 -10
- data/lib/contrast/components/app_context.rb +9 -9
- data/lib/contrast/components/app_context_extend.rb +1 -1
- data/lib/contrast/components/assess.rb +92 -38
- data/lib/contrast/components/assess_rules.rb +36 -0
- data/lib/contrast/components/config.rb +54 -12
- data/lib/contrast/components/contrast_service.rb +8 -8
- data/lib/contrast/components/heap_dump.rb +1 -1
- data/lib/contrast/components/protect.rb +5 -5
- data/lib/contrast/components/ruby_component.rb +81 -0
- data/lib/contrast/components/sampling.rb +1 -1
- data/lib/contrast/components/security_logger.rb +23 -0
- data/lib/contrast/components/service.rb +55 -0
- data/lib/contrast/components/settings.rb +12 -4
- data/lib/contrast/config/base_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +17 -3
- data/lib/contrast/config/server_configuration.rb +1 -1
- data/lib/contrast/config.rb +0 -6
- data/lib/contrast/configuration.rb +81 -17
- data/lib/contrast/extension/assess/exec_trigger.rb +3 -1
- data/lib/contrast/extension/assess/marshal.rb +3 -2
- data/lib/contrast/extension/assess/string.rb +0 -1
- data/lib/contrast/extension/extension.rb +1 -1
- data/lib/contrast/framework/base_support.rb +0 -5
- data/lib/contrast/framework/grape/support.rb +1 -23
- data/lib/contrast/framework/manager.rb +0 -10
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
- data/lib/contrast/framework/rails/support.rb +5 -58
- data/lib/contrast/framework/sinatra/support.rb +2 -21
- data/lib/contrast/logger/cef_log.rb +21 -3
- data/lib/contrast/logger/log.rb +1 -11
- data/lib/contrast/tasks/config.rb +4 -2
- data/lib/contrast/utils/assess/event_limit_utils.rb +28 -12
- data/lib/contrast/utils/assess/trigger_method_utils.rb +10 -18
- data/lib/contrast/utils/findings.rb +6 -5
- data/lib/contrast/utils/hash_digest.rb +9 -24
- data/lib/contrast/utils/hash_digest_extend.rb +6 -6
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -58
- data/lib/contrast/utils/log_utils.rb +47 -17
- data/lib/contrast/utils/net_http_base.rb +7 -8
- data/lib/contrast/utils/patching/policy/patch_utils.rb +3 -2
- data/lib/contrast/utils/stack_trace_utils.rb +0 -25
- data/lib/contrast/utils/string_utils.rb +9 -0
- data/lib/contrast/utils/telemetry_client.rb +13 -7
- data/lib/contrast.rb +5 -10
- metadata +39 -28
- data/lib/contrast/agent/reporting/reporting_events/trace_event_source.rb +0 -30
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -43
- data/lib/contrast/api/decorators/activity.rb +0 -33
- data/lib/contrast/api/decorators/architecture_component.rb +0 -36
- data/lib/contrast/api/decorators/finding.rb +0 -29
- data/lib/contrast/api/decorators/route_coverage.rb +0 -91
- data/lib/contrast/api/decorators/trace_event.rb +0 -120
- data/lib/contrast/api/decorators/trace_event_object.rb +0 -63
- data/lib/contrast/api/decorators/trace_event_signature.rb +0 -69
- data/lib/contrast/api/decorators/trace_taint_range.rb +0 -52
- data/lib/contrast/config/assess_configuration.rb +0 -93
- data/lib/contrast/config/assess_rules_configuration.rb +0 -32
- data/lib/contrast/config/root_configuration.rb +0 -90
- data/lib/contrast/config/ruby_configuration.rb +0 -81
- data/lib/contrast/config/service_configuration.rb +0 -49
- data/lib/contrast/utils/preflight_util.rb +0 -13
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
|
+
require 'contrast/agent/reporting/details/untrusted_deserialization_details'
|
5
6
|
require 'contrast/components/logger'
|
6
7
|
|
7
8
|
module Contrast
|
@@ -68,9 +69,10 @@ module Contrast
|
|
68
69
|
# Per the spec, this rule applies regardless of input. Only the mode
|
69
70
|
# of the rule and code exclusions apply at this point.
|
70
71
|
# @return [Boolean] should the rule apply to this call.
|
71
|
-
def infilter?
|
72
|
+
def infilter? context
|
72
73
|
return false unless enabled?
|
73
74
|
return false if protect_excluded_by_code?
|
75
|
+
return false if protect_excluded_by_url?(context)
|
74
76
|
|
75
77
|
true
|
76
78
|
end
|
@@ -116,7 +118,7 @@ module Contrast
|
|
116
118
|
ia_result = build_evaluation(gadget_command)
|
117
119
|
result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
|
118
120
|
append_to_activity(context, result)
|
119
|
-
cef_logging(result, :successful_attack, gadget_command)
|
121
|
+
cef_logging(result, :successful_attack, value: gadget_command)
|
120
122
|
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
121
123
|
end
|
122
124
|
|
@@ -135,13 +137,13 @@ module Contrast
|
|
135
137
|
# to render this attack event in TeamServer.
|
136
138
|
def build_sample context, input_analysis_result, _candidate_string, **kwargs
|
137
139
|
sample = build_base_sample(context, input_analysis_result)
|
138
|
-
sample.
|
140
|
+
sample.details = Contrast::Agent::Reporting::Details::UntrustedDeserializationDetails.new
|
139
141
|
|
140
142
|
deserializer = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:GADGET_TYPE])
|
141
|
-
sample.
|
143
|
+
sample.details.deserializer = deserializer
|
142
144
|
|
143
145
|
command = !!kwargs[:COMMAND_SCOPE]
|
144
|
-
sample.
|
146
|
+
sample.details.cmd = command
|
145
147
|
|
146
148
|
sample
|
147
149
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base_service'
|
5
5
|
require 'contrast/utils/stack_trace_utils'
|
6
|
+
require 'contrast/agent/reporting/details/path_traversal_details'
|
7
|
+
require 'contrast/agent/reporting/details/path_traversal_semantic_analysis_details'
|
6
8
|
|
7
9
|
module Contrast
|
8
10
|
module Agent
|
@@ -48,7 +50,7 @@ module Contrast
|
|
48
50
|
append_to_activity(context, result)
|
49
51
|
return unless blocked?
|
50
52
|
|
51
|
-
cef_logging(result, :successful_attack
|
53
|
+
cef_logging(result, :successful_attack)
|
52
54
|
raise(Contrast::SecurityException.new(self,
|
53
55
|
"Path Traversal rule triggered. Call to File.#{ method } blocked."))
|
54
56
|
end
|
@@ -65,9 +67,9 @@ module Contrast
|
|
65
67
|
# evaluation
|
66
68
|
def build_sample context, input_analysis_result, path, **_kwargs
|
67
69
|
sample = build_base_sample(context, input_analysis_result)
|
68
|
-
sample.
|
70
|
+
sample.details = Contrast::Agent::Reporting::Details::PathTraversalDetails.new
|
69
71
|
path ||= input_analysis_result.value
|
70
|
-
sample.
|
72
|
+
sample.details.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
71
73
|
sample
|
72
74
|
end
|
73
75
|
|
@@ -76,17 +78,17 @@ module Contrast
|
|
76
78
|
# Build a subclass of the RaspRuleSample if the sample matches
|
77
79
|
def build_rep_sample context, path
|
78
80
|
sample = build_base_sample(context, nil)
|
79
|
-
sample.
|
81
|
+
sample.details = Contrast::Agent::Reporting::Details::PathTraversalSemanticAnalysisDetails.new
|
80
82
|
path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
81
|
-
sample.
|
83
|
+
sample.details.path = path
|
82
84
|
|
83
85
|
if custom_code_access_sysfile_enabled? && custom_code_accessing_system_file?(path)
|
84
|
-
sample.
|
86
|
+
sample.details.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
|
85
87
|
return sample
|
86
88
|
end
|
87
89
|
|
88
90
|
if common_file_exploits_enabled? && contains_known_attack_signatures?(path)
|
89
|
-
sample.
|
91
|
+
sample.details.findings << :COMMON_FILE_EXPLOITS
|
90
92
|
return sample
|
91
93
|
end
|
92
94
|
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
5
|
require 'contrast/agent/protect/rule/base_service'
|
6
|
+
require 'contrast/agent/reporting/details/sqli_details'
|
7
|
+
require 'contrast/agent/reporting/details/no_sqli_details'
|
6
8
|
|
7
9
|
module Contrast
|
8
10
|
module Agent
|
@@ -18,16 +20,16 @@ module Contrast
|
|
18
20
|
# @candidate_string [String] the value of the input which may be an attack
|
19
21
|
# @kwargs [Hash] key - value pairs of context individual rules need to build out details
|
20
22
|
# to send to the Service to tell the story of the attack
|
21
|
-
# @return [Contrast::
|
23
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
|
22
24
|
module SqliSample
|
23
25
|
def build_sample context, input_analysis_result, candidate_string, **kwargs
|
24
26
|
sqli_sample = build_base_sample(context, input_analysis_result)
|
25
|
-
sqli_sample.
|
26
|
-
sqli_sample.
|
27
|
-
sqli_sample.
|
28
|
-
sqli_sample.
|
29
|
-
sqli_sample.
|
30
|
-
sqli_sample.
|
27
|
+
sqli_sample.details = Contrast::Agent::Reporting::Details::SqliDetails.new
|
28
|
+
sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
29
|
+
sqli_sample.details.start_idx = kwargs[:start_idx]
|
30
|
+
sqli_sample.details.end_idx = kwargs[:end_idx]
|
31
|
+
sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
|
32
|
+
sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
|
31
33
|
sqli_sample
|
32
34
|
end
|
33
35
|
end
|
@@ -41,16 +43,16 @@ module Contrast
|
|
41
43
|
# @candidate_string [String] the value of the input which may be an attack
|
42
44
|
# @kwargs [Hash] key - value pairs of context individual rules need to build out details
|
43
45
|
# to send to the Service to tell the story of the attack
|
44
|
-
# @return [Contrast::
|
46
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
|
45
47
|
module NoSqliSample
|
46
48
|
def build_sample context, input_analysis_result, candidate_string, **kwargs
|
47
49
|
no_sqli_sample = build_base_sample(context, input_analysis_result)
|
48
|
-
no_sqli_sample.
|
49
|
-
no_sqli_sample.
|
50
|
-
no_sqli_sample.
|
51
|
-
no_sqli_sample.
|
52
|
-
no_sqli_sample.
|
53
|
-
no_sqli_sample.
|
50
|
+
no_sqli_sample.details = Contrast::Agent::Reporting::Details::NoSqliDetails.new
|
51
|
+
no_sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
52
|
+
no_sqli_sample.details.start_idx = kwargs[:start_idx].to_i
|
53
|
+
no_sqli_sample.details.end_idx = kwargs[:end_idx].to_i
|
54
|
+
no_sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
|
55
|
+
no_sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
|
54
56
|
no_sqli_sample
|
55
57
|
end
|
56
58
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Contrast
|
5
|
+
module Agent
|
6
|
+
module Protect
|
7
|
+
module Rule
|
8
|
+
# This is the Base Rule
|
9
|
+
class SqliBaseRule < Contrast::Agent::Protect::Rule::BaseService
|
10
|
+
include Contrast::Components::Logger::InstanceMethods
|
11
|
+
include Contrast::Agent::Reporting::InputType
|
12
|
+
|
13
|
+
BLOCK_MESSAGE = 'SQLi rule triggered. Response blocked.'
|
14
|
+
|
15
|
+
APPLICABLE_USER_INPUTS = [
|
16
|
+
BODY, COOKIE_NAME, COOKIE_VALUE, HEADER,
|
17
|
+
PARAMETER_NAME, PARAMETER_VALUE, JSON_VALUE,
|
18
|
+
MULTIPART_VALUE, MULTIPART_FIELD_NAME,
|
19
|
+
XML_VALUE, DWR_VALUE
|
20
|
+
].cs__freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
|
24
|
+
# @return [Hash] the details for this specific rule
|
25
|
+
def extract_details attack_sample
|
26
|
+
{
|
27
|
+
start: attack_sample.sqli.start_idx,
|
28
|
+
end: attack_sample.sqli.end_idx,
|
29
|
+
boundaryOverrunIndex: attack_sample.sqli.boundary_overrun_idx,
|
30
|
+
inputBoundaryIndex: attack_sample.sqli.input_boundary_idx,
|
31
|
+
query: attack_sample.sqli.query
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def infilter context, database, query_string
|
37
|
+
return unless infilter?(context)
|
38
|
+
|
39
|
+
result = find_attacker(context, query_string, database: database)
|
40
|
+
return unless result
|
41
|
+
|
42
|
+
append_to_activity(context, result)
|
43
|
+
|
44
|
+
cef_logging(result, :successful_attack)
|
45
|
+
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/agent/reporting/details/sqli_dangerous_functions'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Protect
|
9
|
+
module Rule
|
10
|
+
# This class will include the check for SQL Injection Semantic Dangerous Functions
|
11
|
+
class SqliDangerousFunctions < Contrast::Agent::Protect::Rule::SqliBaseRule
|
12
|
+
NAME = 'sql-injection-semantic-dangerous-functions'
|
13
|
+
BLOCK_MESSAGE = 'SQLi Semantic Dangerous Functions rule triggered. Response blocked.'
|
14
|
+
|
15
|
+
SQL_DANGEROUS_FUNCTIONS = %w[unhex waitfor xp_cmdshell exec].cs__freeze
|
16
|
+
|
17
|
+
def rule_name
|
18
|
+
NAME
|
19
|
+
end
|
20
|
+
|
21
|
+
def infilter context, query_string
|
22
|
+
return unless infilter?(context)
|
23
|
+
return unless violated?(query_string)
|
24
|
+
|
25
|
+
result = build_violation(context, query_string)
|
26
|
+
return unless result
|
27
|
+
|
28
|
+
append_to_activity(context, result)
|
29
|
+
|
30
|
+
cef_logging(result, :successful_attack)
|
31
|
+
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def violated? attack_string
|
37
|
+
SQL_DANGEROUS_FUNCTIONS.any? { |dang_func| attack_string.downcase.include?(dang_func) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_violation context, potential_attack_string
|
41
|
+
result = build_attack_result(context)
|
42
|
+
update_successful_attack_response(context, nil, result, potential_attack_string)
|
43
|
+
append_sample(context, nil, result, potential_attack_string)
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# Override if rule can make use of the candidate string or kwargs to
|
48
|
+
# build rasp rule sample.
|
49
|
+
def build_sample context, ia_result, candidate_string, **_kwargs
|
50
|
+
sample = build_base_sample(context, nil)
|
51
|
+
sample.details = Contrast::Agent::Reporting::Details::SqliDangerousFunctions.new
|
52
|
+
sample.details.query = candidate_string
|
53
|
+
|
54
|
+
if ia_result.nil?
|
55
|
+
ui = Contrast::Agent::Reporting::UserInput.new
|
56
|
+
ui.input_type = :UNKNOWN
|
57
|
+
ui.value = candidate_string
|
58
|
+
sample.user_input = ui
|
59
|
+
end
|
60
|
+
|
61
|
+
sample
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -5,13 +5,15 @@ require 'contrast/agent/protect/rule/base_service'
|
|
5
5
|
require 'contrast/agent/protect/policy/applies_sqli_rule'
|
6
6
|
require 'contrast/agent/protect/rule/sql_sample_builder'
|
7
7
|
require 'contrast/agent/reporting/input_analysis/input_type'
|
8
|
+
require 'contrast/agent/protect/rule/sqli/sqli_base_rule'
|
9
|
+
require 'contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions'
|
8
10
|
|
9
11
|
module Contrast
|
10
12
|
module Agent
|
11
13
|
module Protect
|
12
14
|
module Rule
|
13
15
|
# The Ruby implementation of the Protect SQL Injection rule.
|
14
|
-
class Sqli < Contrast::Agent::Protect::Rule::
|
16
|
+
class Sqli < Contrast::Agent::Protect::Rule::SqliBaseRule
|
15
17
|
# Generate a sample for the SQLI injection detection rule, allowing for reporting to and rendering
|
16
18
|
# by TeamServer
|
17
19
|
include SqlSampleBuilder::SqliSample
|
@@ -22,28 +24,9 @@ module Contrast
|
|
22
24
|
include Contrast::Agent::Reporting::InputType
|
23
25
|
end
|
24
26
|
|
25
|
-
APPLICABLE_USER_INPUTS = [
|
26
|
-
BODY, COOKIE_NAME, COOKIE_VALUE, HEADER,
|
27
|
-
PARAMETER_NAME, PARAMETER_VALUE, JSON_VALUE,
|
28
|
-
MULTIPART_VALUE, MULTIPART_FIELD_NAME,
|
29
|
-
XML_VALUE, DWR_VALUE
|
30
|
-
].cs__freeze
|
31
27
|
NAME = 'sql-injection'
|
32
|
-
BLOCK_MESSAGE = 'SQLi rule triggered. Response blocked.'
|
33
28
|
|
34
|
-
|
35
|
-
# @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
|
36
|
-
# @return [Hash] the details for this specific rule
|
37
|
-
def extract_details attack_sample
|
38
|
-
{
|
39
|
-
start: attack_sample.sqli.start_idx,
|
40
|
-
end: attack_sample.sqli.end_idx,
|
41
|
-
boundaryOverrunIndex: attack_sample.sqli.boundary_overrun_idx,
|
42
|
-
inputBoundaryIndex: attack_sample.sqli.input_boundary_idx,
|
43
|
-
query: attack_sample.sqli.query
|
44
|
-
}
|
45
|
-
end
|
46
|
-
end
|
29
|
+
SUB_RULES = [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
|
47
30
|
|
48
31
|
def rule_name
|
49
32
|
NAME
|
@@ -53,16 +36,8 @@ module Contrast
|
|
53
36
|
BLOCK_MESSAGE
|
54
37
|
end
|
55
38
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
result = find_attacker(context, query_string, database: database)
|
60
|
-
return unless result
|
61
|
-
|
62
|
-
append_to_activity(context, result)
|
63
|
-
|
64
|
-
cef_logging(result, :successful_attack, query_string)
|
65
|
-
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
39
|
+
def sub_rules
|
40
|
+
SUB_RULES
|
66
41
|
end
|
67
42
|
end
|
68
43
|
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
|
+
require 'contrast/agent/reporting/details/xxe_details'
|
6
|
+
require 'contrast/agent/reporting/details/xxe_match'
|
7
|
+
require 'contrast/agent/reporting/details/xxe_wrapper'
|
5
8
|
require 'contrast/utils/timer'
|
6
9
|
require 'contrast/components/logger'
|
7
10
|
|
@@ -56,13 +59,15 @@ module Contrast
|
|
56
59
|
# @raise [Contrast::SecurityException] Security exception if an XXE
|
57
60
|
# attack is found and the rule is in block mode.
|
58
61
|
def infilter context, framework, xml
|
62
|
+
return if protect_excluded_by_url?(context)
|
63
|
+
|
59
64
|
result = find_attacker(context, xml, framework: framework)
|
60
65
|
return unless result
|
61
66
|
|
62
67
|
append_to_activity(context, result)
|
63
68
|
return unless blocked?
|
64
69
|
|
65
|
-
cef_logging(result, :successful_attack, xml)
|
70
|
+
cef_logging(result, :successful_attack, value: xml)
|
66
71
|
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE))
|
67
72
|
end
|
68
73
|
|
@@ -105,7 +110,7 @@ module Contrast
|
|
105
110
|
entity_wrapper = Contrast::Agent::Protect::Rule::Xxe::EntityWrapper.new(ss.matched)
|
106
111
|
next unless entity_wrapper.external_entity?
|
107
112
|
|
108
|
-
xxe_details ||= Contrast::
|
113
|
+
xxe_details ||= Contrast::Agent::Reporting::Details::XxeDetails.new
|
109
114
|
xxe_details.declared_entities << build_match(ss)
|
110
115
|
xxe_details.entities_resolved << build_wrapper(entity_wrapper)
|
111
116
|
end
|
@@ -119,12 +124,12 @@ module Contrast
|
|
119
124
|
def build_sample context, ia_result, _url, **kwargs
|
120
125
|
sample = build_base_sample(context, ia_result)
|
121
126
|
sample.user_input = build_user_input(ia_result)
|
122
|
-
sample.
|
127
|
+
sample.details = kwargs[:details]
|
123
128
|
sample
|
124
129
|
end
|
125
130
|
|
126
131
|
def build_user_input ia_result
|
127
|
-
input = Contrast::
|
132
|
+
input = Contrast::Agent::Reporting::UserInput.new
|
128
133
|
input.key = INPUT_NAME
|
129
134
|
input.input_type = :UNKNOWN
|
130
135
|
input.document_type = :XML
|
@@ -146,14 +151,14 @@ module Contrast
|
|
146
151
|
end
|
147
152
|
|
148
153
|
def build_match string_scanner
|
149
|
-
match = Contrast::
|
154
|
+
match = Contrast::Agent::Reporting::Details::XxeMatch.new
|
150
155
|
match.end_idx = string_scanner.pos.to_i
|
151
156
|
match.start_idx = match.end_idx - string_scanner.matched_size
|
152
157
|
match
|
153
158
|
end
|
154
159
|
|
155
160
|
def build_wrapper entity_wrapper
|
156
|
-
wrapper = Contrast::
|
161
|
+
wrapper = Contrast::Agent::Reporting::Details::XxeWrapper.new
|
157
162
|
wrapper.system_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.system_id)
|
158
163
|
wrapper.public_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.public_id)
|
159
164
|
wrapper
|
@@ -28,12 +28,14 @@ require 'contrast/agent/protect/rule/sqli/default_sql_scanner'
|
|
28
28
|
require 'contrast/agent/protect/rule/sqli/mysql_sql_scanner'
|
29
29
|
require 'contrast/agent/protect/rule/sqli/postgres_sql_scanner'
|
30
30
|
require 'contrast/agent/protect/rule/sqli/sqlite_sql_scanner'
|
31
|
+
require 'contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions'
|
31
32
|
|
32
33
|
# The classes required for Path Traversal
|
33
34
|
require 'contrast/agent/protect/rule/path_traversal'
|
34
35
|
|
35
|
-
# The classes required for Command Injection
|
36
|
+
# The classes required for Command Injection and sub-rules
|
36
37
|
require 'contrast/agent/protect/rule/cmd_injection'
|
38
|
+
require 'contrast/agent/protect/rule/cmdi/cmdi_backdoors'
|
37
39
|
|
38
40
|
# The classes required for XXE
|
39
41
|
require 'contrast/agent/protect/rule/xxe'
|
@@ -57,6 +57,14 @@ module Contrast
|
|
57
57
|
def tags= tags
|
58
58
|
@_tags = tags if tags.is_a?(String)
|
59
59
|
end
|
60
|
+
|
61
|
+
def details
|
62
|
+
@_details ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def details= protect_details
|
66
|
+
@_details = protect_details if protect_details.is_a?(Contrast::Agent::Reporting::Details::ProtectRuleDetails)
|
67
|
+
end
|
60
68
|
end
|
61
69
|
end
|
62
70
|
end
|
@@ -4,48 +4,103 @@
|
|
4
4
|
require 'contrast/utils/object_share'
|
5
5
|
require 'contrast/utils/timer'
|
6
6
|
require 'contrast/agent/reporting/attack_result/user_input'
|
7
|
+
require 'contrast/agent/reporting/input_analysis/input_type'
|
8
|
+
require 'contrast/agent/reporting/details/protect_rule_details'
|
7
9
|
|
8
10
|
module Contrast
|
9
11
|
module Agent
|
10
12
|
module Reporting
|
11
13
|
# This class will hold the new RaspRuleSample.
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
14
|
+
# It is mainly used to build samples for each
|
15
|
+
# protect rule, and translate data from SP IA.
|
16
|
+
# It is not a reporting event.
|
17
|
+
class RaspRuleSample
|
18
|
+
# Any rules specific details
|
17
19
|
#
|
18
|
-
#
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
20
|
+
# @return [Contrast::Agent::Reporting::Details::ProtectRuleDetails, nil]
|
21
|
+
attr_accessor :details
|
22
|
+
# @return [Contrast::Agent::Reporting::Details::IpDenylistDetails, nil]
|
23
|
+
attr_accessor :ip_denylist
|
24
|
+
# @return [Contrast::Agent::Reporting::Details::VirtualPatchDetails, nil]
|
25
|
+
attr_accessor :virtual_patch
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# @param context [Contrast::Agent::RequestContext]
|
29
|
+
# @param ia_result [Contrast::Api::Settings::InputAnalysisResult] the analysis of the input that was
|
30
|
+
# determined to be an attack
|
31
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample]
|
32
|
+
def build context, ia_result
|
33
|
+
sample = new
|
34
|
+
sample.time_stamp = context&.timer&.start_ms
|
35
|
+
sample.user_input = build_user_input_from_ia(ia_result)
|
36
|
+
sample.user_input.document_type = if context&.request
|
37
|
+
Contrast::Utils::StringUtils.force_utf8(context.request.document_type)
|
38
|
+
end
|
39
|
+
sample
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param ia_result [Contrast::Api::Settings::InputAnalysisResult] the analysis of the input that was
|
43
|
+
# determined to be an attack
|
44
|
+
def build_user_input_from_ia ia_result
|
45
|
+
# TODO: RUBY-99999 remove once only using Agent IA
|
46
|
+
result = if ia_result.cs__is_a?(Contrast::Api::Settings::InputAnalysisResult)
|
47
|
+
transform_ia_result(ia_result)
|
48
|
+
else
|
49
|
+
# Use Agent ia_result
|
50
|
+
ia_result
|
51
|
+
end
|
52
|
+
user_input = Contrast::Agent::Reporting::UserInput.new
|
53
|
+
return user_input unless result
|
54
|
+
|
55
|
+
user_input.input_type = result.input_type
|
56
|
+
user_input.matcher_ids = result.ids
|
57
|
+
user_input.path = result.path
|
58
|
+
user_input.key = result.key if result.key
|
59
|
+
user_input.value = result.value if result.value
|
60
|
+
user_input
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Contrast::Api::Settings::InputAnalysisResult]
|
64
|
+
# @return [Contrast::Agent::Reporting::InputAnalysisResult]
|
65
|
+
def transform_ia_result dtm_ia_result
|
66
|
+
ia_result = Contrast::Agent::Reporting::InputAnalysisResult.new
|
67
|
+
ia_result.input_type = Contrast::Agent::Reporting::InputType.to_a.find do |value|
|
68
|
+
value == dtm_ia_result.input_type.name # rubocop:disable Security/Module/Name
|
69
|
+
end
|
70
|
+
ia_result.score_level = dtm_ia_result.score_level.name # rubocop:disable Security/Module/Name
|
71
|
+
ia_result.value = dtm_ia_result.value
|
72
|
+
ia_result.key = dtm_ia_result.key
|
73
|
+
ia_result.path = dtm_ia_result.path
|
74
|
+
ia_result.rule_id = dtm_ia_result.rule_id
|
75
|
+
ia_result.attack_count = dtm_ia_result.attack_count
|
76
|
+
ia_result.ids = dtm_ia_result.ids
|
77
|
+
ia_result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def time_stamp
|
82
|
+
@_time_stamp ||= Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def time_stamp= timestamp_ms
|
86
|
+
@_time_stamp = timestamp_ms
|
87
|
+
end
|
88
|
+
|
89
|
+
def user_input
|
90
|
+
@_user_input ||= Contrast::Agent::Reporting::UserInput.new
|
91
|
+
end
|
92
|
+
|
93
|
+
def user_input= input
|
94
|
+
@_user_input = input if input.is_a?(Contrast::Agent::Reporting::UserInput)
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_controlled_hash
|
98
|
+
{
|
99
|
+
timeStamp: Time.at(time_stamp).iso8601,
|
100
|
+
userInput: user_input.to_controlled_hash,
|
101
|
+
details: details&.to_controlled_hash
|
102
|
+
}
|
103
|
+
end
|
49
104
|
end
|
50
105
|
end
|
51
106
|
end
|
@@ -81,6 +81,17 @@ module Contrast
|
|
81
81
|
def matcher_ids= ids
|
82
82
|
@_matcher_ids = ids if ids.is_a?(Array) && ids.any?(String)
|
83
83
|
end
|
84
|
+
|
85
|
+
def to_controlled_hash
|
86
|
+
{
|
87
|
+
path: path,
|
88
|
+
key: key,
|
89
|
+
value: value,
|
90
|
+
inputType: input_type.to_s,
|
91
|
+
documentType: document_type.to_s,
|
92
|
+
matcherIds: matcher_ids&.map(&:to_s)
|
93
|
+
}
|
94
|
+
end
|
84
95
|
end
|
85
96
|
end
|
86
97
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/agent/reporting/details/protect_rule_details'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Reporting
|
9
|
+
module Details
|
10
|
+
# Bot blocker IA result details info.
|
11
|
+
class BotBlockerDetails < ProtectRuleDetails
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :bot
|
14
|
+
# User agent header value
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
attr_accessor :user_agent
|
18
|
+
|
19
|
+
def to_controlled_hash
|
20
|
+
{
|
21
|
+
bot: bot,
|
22
|
+
userAgent: user_agent
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/agent/reporting/details/protect_rule_details'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Reporting
|
9
|
+
module Details
|
10
|
+
# CMDI IA result details info.
|
11
|
+
class CmdInjectionDetails < ProtectRuleDetails
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :cmd
|
14
|
+
# @return [Integer]
|
15
|
+
attr_accessor :start_idx
|
16
|
+
# @return [Integer]
|
17
|
+
attr_accessor :end_idx
|
18
|
+
|
19
|
+
def to_controlled_hash
|
20
|
+
{
|
21
|
+
command: cmd,
|
22
|
+
startIndex: start_idx,
|
23
|
+
endIndex: end_idx
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|