contrast-agent 3.13.2 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/contrast_service +1 -7
- data/ext/cs__assess_active_record_named/cs__active_record_named.c +8 -7
- data/ext/cs__assess_array/cs__assess_array.c +6 -5
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +5 -5
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +2 -1
- data/ext/cs__assess_hash/cs__assess_hash.c +18 -17
- data/ext/cs__assess_hash/cs__assess_hash.h +2 -1
- data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -8
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +18 -16
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +1 -0
- data/ext/cs__assess_module/cs__assess_module.c +6 -6
- data/ext/cs__assess_regexp/cs__assess_regexp.c +4 -4
- data/ext/cs__assess_string/cs__assess_string.c +31 -16
- data/ext/cs__assess_string/cs__assess_string.h +6 -1
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +4 -2
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +2 -2
- data/ext/cs__common/cs__common.c +48 -39
- data/ext/cs__common/cs__common.h +16 -21
- data/ext/cs__contrast_patch/cs__contrast_patch.c +27 -25
- data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -7
- data/ext/cs__protect_kernel/cs__protect_kernel.c +11 -12
- data/ext/cs__protect_kernel/cs__protect_kernel.h +2 -2
- data/lib/contrast-agent.rb +1 -1
- data/lib/contrast.rb +13 -23
- data/lib/contrast/agent.rb +39 -47
- data/lib/contrast/agent/assess.rb +12 -12
- data/lib/contrast/agent/assess/contrast_event.rb +151 -85
- data/lib/contrast/agent/assess/events/event_factory.rb +2 -2
- data/lib/contrast/agent/assess/events/source_event.rb +3 -3
- data/lib/contrast/agent/assess/finalizers/freeze.rb +15 -0
- data/lib/contrast/agent/assess/finalizers/hash.rb +97 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +11 -4
- data/lib/contrast/agent/assess/policy/patcher.rb +6 -6
- data/lib/contrast/agent/assess/policy/policy.rb +9 -11
- data/lib/contrast/agent/assess/policy/policy_node.rb +17 -12
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +21 -5
- data/lib/contrast/agent/assess/policy/preshift.rb +13 -7
- data/lib/contrast/agent/assess/policy/propagation_method.rb +64 -44
- data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator.rb +18 -18
- data/lib/contrast/agent/assess/policy/propagator/append.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/center.rb +9 -5
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +6 -4
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +7 -4
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +4 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +7 -9
- data/lib/contrast/agent/assess/policy/propagator/next.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +13 -5
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +8 -4
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -2
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/select.rb +13 -7
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +10 -9
- data/lib/contrast/agent/assess/policy/propagator/split.rb +27 -22
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +52 -35
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +11 -5
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +5 -5
- data/lib/contrast/agent/assess/policy/source_method.rb +90 -72
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +1 -1
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -12
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +2 -2
- data/lib/contrast/agent/assess/policy/trigger_method.rb +81 -33
- data/lib/contrast/agent/assess/policy/trigger_node.rb +41 -46
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -1
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +2 -2
- data/lib/contrast/agent/assess/properties.rb +15 -5
- data/lib/contrast/agent/assess/property/evented.rb +7 -20
- data/lib/contrast/agent/assess/property/tagged.rb +13 -7
- data/lib/contrast/agent/assess/property/updated.rb +131 -0
- data/lib/contrast/agent/assess/rule.rb +2 -2
- data/lib/contrast/agent/assess/rule/base.rb +3 -4
- data/lib/contrast/agent/assess/rule/provider.rb +3 -3
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +24 -9
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +85 -16
- data/lib/contrast/agent/assess/tag.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +66 -0
- data/lib/contrast/agent/at_exit_hook.rb +6 -6
- data/lib/contrast/agent/class_reopener.rb +14 -11
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +1 -1
- data/lib/contrast/agent/deadzone/policy/policy.rb +2 -2
- data/lib/contrast/agent/disable_reaction.rb +1 -1
- data/lib/contrast/agent/exclusion_matcher.rb +1 -1
- data/lib/contrast/agent/inventory.rb +15 -0
- data/lib/contrast/agent/inventory/dependencies.rb +50 -0
- data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
- data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
- data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
- data/lib/contrast/agent/inventory/policy/policy.rb +3 -3
- data/lib/contrast/agent/inventory/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/middleware.rb +33 -34
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +9 -9
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +23 -22
- data/lib/contrast/agent/patching/policy/module_policy.rb +11 -11
- data/lib/contrast/agent/patching/policy/patch.rb +15 -15
- data/lib/contrast/agent/patching/policy/patcher.rb +43 -44
- data/lib/contrast/agent/patching/policy/policy.rb +23 -12
- data/lib/contrast/agent/patching/policy/policy_node.rb +1 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +6 -8
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +7 -6
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +4 -4
- data/lib/contrast/agent/protect/policy/policy.rb +8 -8
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -1
- data/lib/contrast/agent/protect/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/protect/rule.rb +18 -18
- data/lib/contrast/agent/protect/rule/base.rb +4 -3
- data/lib/contrast/agent/protect/rule/base_service.rb +1 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -5
- data/lib/contrast/agent/protect/rule/deserialization.rb +1 -1
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +1 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -1
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
- data/lib/contrast/agent/protect/rule/path_traversal.rb +4 -5
- data/lib/contrast/agent/protect/rule/sqli.rb +2 -2
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +1 -1
- data/lib/contrast/agent/protect/rule/xss.rb +1 -1
- data/lib/contrast/agent/protect/rule/xxe.rb +3 -5
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +2 -2
- data/lib/contrast/agent/railtie.rb +1 -1
- data/lib/contrast/agent/reaction_processor.rb +2 -2
- data/lib/contrast/agent/request.rb +45 -43
- data/lib/contrast/agent/request_context.rb +10 -6
- data/lib/contrast/agent/request_handler.rb +1 -1
- data/lib/contrast/agent/response.rb +23 -12
- data/lib/contrast/agent/rewriter.rb +6 -9
- data/lib/contrast/agent/service_heartbeat.rb +2 -2
- data/lib/contrast/agent/static_analysis.rb +9 -9
- data/lib/contrast/agent/thread.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +2 -2
- data/lib/contrast/agent/tracepoint_hook.rb +2 -2
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +4 -4
- data/lib/contrast/api/communication.rb +9 -9
- data/lib/contrast/api/communication/messaging_queue.rb +3 -6
- data/lib/contrast/api/communication/response_processor.rb +1 -1
- data/lib/contrast/api/communication/socket_client.rb +41 -6
- data/lib/contrast/api/communication/speedracer.rb +1 -1
- data/lib/contrast/api/communication/tcp_socket.rb +1 -1
- data/lib/contrast/api/communication/unix_socket.rb +1 -1
- data/lib/contrast/api/decorators.rb +17 -14
- data/lib/contrast/api/decorators/address.rb +20 -20
- data/lib/contrast/api/decorators/application_settings.rb +3 -2
- data/lib/contrast/api/decorators/application_update.rb +7 -8
- data/lib/contrast/api/decorators/http_request.rb +13 -12
- data/lib/contrast/api/decorators/input_analysis.rb +3 -2
- data/lib/contrast/api/decorators/library.rb +53 -0
- data/lib/contrast/api/decorators/library_usage_update.rb +30 -0
- data/lib/contrast/api/decorators/message.rb +4 -2
- data/lib/contrast/api/decorators/rasp_rule_sample.rb +2 -1
- data/lib/contrast/api/decorators/route_coverage.rb +3 -2
- data/lib/contrast/api/decorators/server_features.rb +3 -2
- data/lib/contrast/api/decorators/trace_event.rb +28 -25
- data/lib/contrast/api/decorators/trace_event_object.rb +6 -5
- data/lib/contrast/api/decorators/trace_event_signature.rb +5 -4
- data/lib/contrast/api/decorators/trace_taint_range.rb +4 -3
- data/lib/contrast/api/decorators/user_input.rb +4 -4
- data/lib/contrast/common_agent_configuration.rb +2 -2
- data/lib/contrast/components/agent.rb +8 -7
- data/lib/contrast/components/app_context.rb +50 -39
- data/lib/contrast/components/config.rb +32 -50
- data/lib/contrast/components/contrast_service.rb +10 -10
- data/lib/contrast/components/interface.rb +39 -17
- data/lib/contrast/components/inventory.rb +6 -1
- data/lib/contrast/components/logger.rb +1 -1
- data/lib/contrast/components/scope.rb +3 -3
- data/lib/contrast/components/settings.rb +20 -23
- data/lib/contrast/config.rb +18 -18
- data/lib/contrast/config/application_configuration.rb +5 -2
- data/lib/contrast/config/base_configuration.rb +2 -2
- data/lib/contrast/config/inventory_configuration.rb +2 -2
- data/lib/contrast/config/protect_rule_configuration.rb +1 -1
- data/lib/contrast/config/service_configuration.rb +8 -0
- data/lib/contrast/configuration.rb +93 -52
- data/lib/contrast/extension/assess.rb +21 -22
- data/lib/contrast/extension/assess/array.rb +18 -11
- data/lib/contrast/extension/assess/erb.rb +11 -3
- data/lib/contrast/extension/assess/eval_trigger.rb +7 -7
- data/lib/contrast/extension/assess/exec_trigger.rb +2 -2
- data/lib/contrast/extension/assess/fiber.rb +14 -14
- data/lib/contrast/extension/assess/hash.rb +7 -6
- data/lib/contrast/extension/assess/kernel.rb +34 -28
- data/lib/contrast/extension/assess/marshal.rb +67 -0
- data/lib/contrast/extension/assess/regexp.rb +10 -9
- data/lib/contrast/extension/assess/string.rb +23 -23
- data/lib/contrast/extension/inventory.rb +4 -4
- data/lib/contrast/extension/kernel.rb +1 -1
- data/lib/contrast/extension/module.rb +1 -1
- data/lib/contrast/extension/protect.rb +3 -3
- data/lib/contrast/extension/protect/kernel.rb +4 -4
- data/lib/contrast/extension/protect/psych.rb +2 -2
- data/lib/contrast/framework/base_support.rb +1 -1
- data/lib/contrast/framework/manager.rb +10 -11
- data/lib/contrast/framework/rack/patch/session_cookie.rb +23 -29
- data/lib/contrast/framework/rack/patch/support.rb +1 -1
- data/lib/contrast/framework/rack/support.rb +2 -2
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +13 -13
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +7 -13
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +11 -11
- data/lib/contrast/framework/rails/patch/support.rb +4 -4
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +11 -11
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +12 -12
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +12 -12
- data/lib/contrast/framework/rails/support.rb +67 -14
- data/lib/contrast/framework/sinatra/patch/base.rb +12 -12
- data/lib/contrast/framework/sinatra/patch/support.rb +1 -1
- data/lib/contrast/framework/sinatra/support.rb +6 -6
- data/lib/contrast/funchook/funchook.rb +1 -1
- data/lib/contrast/logger/application.rb +13 -5
- data/lib/contrast/logger/format.rb +22 -9
- data/lib/contrast/logger/log.rb +17 -10
- data/lib/contrast/logger/request.rb +30 -0
- data/lib/contrast/tasks/config.rb +1 -1
- data/lib/contrast/tasks/service.rb +2 -2
- data/lib/contrast/utils/assess/sampling_util.rb +2 -2
- data/lib/contrast/utils/assess/tracking_util.rb +49 -4
- data/lib/contrast/utils/class_util.rb +2 -2
- data/lib/contrast/utils/duck_utils.rb +0 -10
- data/lib/contrast/utils/env_configuration_item.rb +2 -1
- data/lib/contrast/utils/hash_digest.rb +2 -1
- data/lib/contrast/utils/heap_dump_util.rb +2 -2
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -22
- data/lib/contrast/utils/inventory_util.rb +3 -10
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/os.rb +1 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
- data/lib/contrast/utils/sha256_builder.rb +2 -14
- data/lib/contrast/utils/stack_trace_utils.rb +2 -2
- data/lib/contrast/utils/string_utils.rb +11 -6
- data/resources/assess/policy.json +31 -22
- data/ruby-agent.gemspec +21 -19
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +73 -30
- data/lib/contrast/agent/assess/insulator.rb +0 -49
- data/lib/contrast/agent/require_state.rb +0 -61
- data/lib/contrast/extension/assess/assess_extension.rb +0 -147
- data/lib/contrast/utils/boolean_util.rb +0 -30
- data/lib/contrast/utils/freeze_util.rb +0 -32
- data/lib/contrast/utils/gemfile_reader.rb +0 -193
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require 'contrast/agent/assess/policy/source_method'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Agent
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require 'contrast/agent/assess/policy/source_validation/cross_site_validator'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Agent
|
@@ -25,24 +25,26 @@ module Contrast
|
|
25
25
|
TEMPLATE_PROPAGATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
|
26
26
|
|
27
27
|
def xss_tilt_trigger context, trigger_node, _source, object, ret, *args
|
28
|
-
|
28
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
29
|
+
return unless properties
|
29
30
|
|
31
|
+
scope = args[0]
|
30
32
|
erb_template_prerender = object.instance_variable_get(:@data)
|
31
33
|
interpolated_inputs = []
|
32
34
|
handle_binding_variables(scope, erb_template_prerender, ret, interpolated_inputs)
|
33
|
-
|
34
35
|
handle_local_variables(args, erb_template_prerender, ret, interpolated_inputs)
|
35
|
-
|
36
|
+
properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
|
36
37
|
unless interpolated_inputs.empty?
|
38
|
+
current_event = properties.event
|
37
39
|
interpolated_inputs.each do |input|
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
input_properties = Contrast::Agent::Assess::Tracker.properties(input)
|
41
|
+
next unless input_properties&.event
|
42
|
+
|
43
|
+
current_event.parent_events << input_properties.event
|
41
44
|
end
|
42
|
-
ret.cs__properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
|
43
45
|
end
|
44
46
|
|
45
|
-
if
|
47
|
+
if Contrast::Agent::Assess::Tracker.tracked?(ret)
|
46
48
|
Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(context, trigger_node, ret, erb_template_prerender, ret, interpolated_inputs)
|
47
49
|
end
|
48
50
|
|
@@ -52,32 +54,34 @@ module Contrast
|
|
52
54
|
private
|
53
55
|
|
54
56
|
def handle_binding_variables scope, erb_template_prerender, ret, interpolated_inputs
|
57
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
55
58
|
binding_variables = scope.instance_variables
|
56
59
|
|
57
60
|
binding_variables.each do |bound_variable_sym|
|
58
61
|
bound_variable_value = scope.instance_variable_get(bound_variable_sym)
|
59
62
|
|
60
|
-
next unless
|
63
|
+
next unless Contrast::Agent::Assess::Tracker.tracked?(bound_variable_value)
|
61
64
|
next unless erb_template_prerender.include?(bound_variable_sym.to_s)
|
62
65
|
|
63
66
|
start_index = ret.index(bound_variable_value)
|
64
67
|
next if start_index.nil?
|
65
68
|
|
66
|
-
|
69
|
+
properties.copy_from(bound_variable_value, ret, start_index)
|
67
70
|
interpolated_inputs << bound_variable_sym
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
74
|
def handle_local_variables args, erb_template_prerender, ret, interpolated_inputs
|
75
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
72
76
|
locals = args[1]
|
73
77
|
locals.each do |local_name, local_value|
|
74
|
-
next unless
|
78
|
+
next unless Contrast::Agent::Assess::Tracker.tracked?(local_value)
|
75
79
|
next unless erb_template_prerender.include?(local_name.to_s)
|
76
80
|
|
77
81
|
start_index = ret.index(local_value)
|
78
82
|
next if start_index.nil?
|
79
83
|
|
80
|
-
|
84
|
+
properties.copy_from(local_value, ret, start_index)
|
81
85
|
interpolated_inputs << local_name
|
82
86
|
end
|
83
87
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require 'contrast/components/interface'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Agent
|
@@ -39,7 +39,7 @@ module Contrast
|
|
39
39
|
def process context, trigger_node, object, ret, *args
|
40
40
|
args.each do |arg|
|
41
41
|
next unless arg.cs__is_a?(String) || arg.cs__is_a?(Symbol)
|
42
|
-
next unless
|
42
|
+
next unless Contrast::Agent::Assess::Tracker.tracked?(arg)
|
43
43
|
next unless trigger_node.violated?(arg)
|
44
44
|
|
45
45
|
Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
require 'contrast/agent/assess/events/event_factory'
|
5
|
+
require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
|
6
|
+
require 'contrast/components/interface'
|
7
|
+
require 'contrast/utils/object_share'
|
8
|
+
require 'contrast/utils/sha256_builder'
|
9
9
|
|
10
10
|
module Contrast
|
11
11
|
module Agent
|
@@ -20,10 +20,31 @@ module Contrast
|
|
20
20
|
include Contrast::Components::Interface
|
21
21
|
access_component :analysis, :logging
|
22
22
|
|
23
|
+
# The level of TeamServer compliance our traces meet when in the
|
24
|
+
# abnormal condition of being dataflow rules without routes
|
25
|
+
MINIMUM_FINDING_VERSION = 3
|
23
26
|
# The level of TeamServer compliance our traces meet
|
24
|
-
CURRENT_FINDING_VERSION =
|
27
|
+
CURRENT_FINDING_VERSION = 4
|
25
28
|
|
26
29
|
class << self
|
30
|
+
# Append the given finding to the given context to be reported when
|
31
|
+
# the Context's activity is sent to the Service or, in the absence
|
32
|
+
# of that Context, generate an Activity and queue it manually
|
33
|
+
# @param finding [Contrast::Api::Dtm::Finding]
|
34
|
+
def report_finding finding
|
35
|
+
context = Contrast::Agent::REQUEST_TRACKER.current
|
36
|
+
if context
|
37
|
+
context.activity.findings << finding
|
38
|
+
else
|
39
|
+
activity = Contrast::Api::Dtm::Activity.new
|
40
|
+
activity.findings << finding
|
41
|
+
|
42
|
+
Contrast::Agent.messaging_queue.send_event_eventually(activity)
|
43
|
+
end
|
44
|
+
logger.debug('Finding reported',
|
45
|
+
rule: finding.rule_id)
|
46
|
+
end
|
47
|
+
|
27
48
|
# This is called from within our woven proc. It will be called as if it
|
28
49
|
# were inline in the Rack application.
|
29
50
|
#
|
@@ -66,8 +87,8 @@ module Contrast
|
|
66
87
|
# This converts the source of the finding, and the events leading
|
67
88
|
# up to it into a Finding
|
68
89
|
#
|
69
|
-
# @param context [Contrast::
|
70
|
-
# context
|
90
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
91
|
+
# request context
|
71
92
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
72
93
|
# the node to direct applying this trigger event
|
73
94
|
# @param source [Object] the source of the Trigger Event
|
@@ -89,18 +110,17 @@ module Contrast
|
|
89
110
|
|
90
111
|
finding = Contrast::Api::Dtm::Finding.new
|
91
112
|
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
|
92
|
-
finding.version = CURRENT_FINDING_VERSION
|
93
|
-
|
94
113
|
build_from_source(finding, source)
|
95
114
|
trigger_event = Contrast::Agent::Assess::Events::EventFactory.build(trigger_node, source, object, ret, args).to_dtm_event
|
96
115
|
finding.events << trigger_event
|
97
116
|
build_hash(finding, source)
|
98
117
|
finding.routes << context.route if context.route
|
99
|
-
|
118
|
+
finding.version = determine_compliance_version(finding)
|
100
119
|
logger.trace('Finding created',
|
101
120
|
node_id: trigger_node.id,
|
102
121
|
source_id: source.__id__,
|
103
122
|
rule: trigger_node.rule_id)
|
123
|
+
report_finding(finding)
|
104
124
|
rescue StandardError => e
|
105
125
|
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
106
126
|
end
|
@@ -110,8 +130,8 @@ module Contrast
|
|
110
130
|
# This is our method that actually checks the taint on the object
|
111
131
|
# our trigger_node targets.
|
112
132
|
#
|
113
|
-
# @param context [Contrast::
|
114
|
-
# context
|
133
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
134
|
+
# request context
|
115
135
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
116
136
|
# the node to direct applying this trigger event
|
117
137
|
# @param source [Object] the source of the Trigger Event
|
@@ -179,8 +199,8 @@ module Contrast
|
|
179
199
|
# This is our method that actually checks the taint on the object
|
180
200
|
# our trigger_node targets for our Regexp based rules.
|
181
201
|
#
|
182
|
-
# @param context [Contrast::
|
183
|
-
# context
|
202
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
203
|
+
# request context
|
184
204
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
185
205
|
# the node to direct applying this trigger event
|
186
206
|
# @param source [Object] the source of the Trigger Event
|
@@ -199,8 +219,8 @@ module Contrast
|
|
199
219
|
# This is our method that actually checks the taint on the object
|
200
220
|
# our trigger_node targets for our Dataflow based rules.
|
201
221
|
#
|
202
|
-
# @param context [Contrast::
|
203
|
-
# context
|
222
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
223
|
+
# request context
|
204
224
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
205
225
|
# the node to direct applying this trigger event
|
206
226
|
# @param source [Object] the source of the Trigger Event
|
@@ -211,8 +231,8 @@ module Contrast
|
|
211
231
|
def apply_dataflow_rule context, trigger_node, source, object, ret, *args
|
212
232
|
return unless source
|
213
233
|
|
214
|
-
if Contrast::
|
215
|
-
return unless
|
234
|
+
if Contrast::Agent::Assess::Tracker.trackable?(source)
|
235
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
216
236
|
return unless trigger_node.violated?(source)
|
217
237
|
|
218
238
|
build_finding(context, trigger_node, source, object, ret, *args)
|
@@ -226,30 +246,27 @@ module Contrast
|
|
226
246
|
apply_dataflow_rule(context, trigger_node, value, object, ret, *args)
|
227
247
|
end
|
228
248
|
else
|
229
|
-
logger.
|
230
|
-
|
231
|
-
|
232
|
-
|
249
|
+
logger.debug('Trigger source is untrackable. Unable to inspect.',
|
250
|
+
node_id: trigger_node.id,
|
251
|
+
source_id: source.__id__,
|
252
|
+
source_type: source.cs__class.to_s,
|
253
|
+
frozen: source.cs__frozen?)
|
233
254
|
logger.trace(source.to_s[0..99])
|
234
255
|
end
|
235
256
|
end
|
236
257
|
|
237
258
|
def build_from_source finding, source
|
238
259
|
return unless source
|
239
|
-
return unless Contrast::
|
240
|
-
source,
|
241
|
-
:cs__properties)
|
242
|
-
return unless source.cs__properties
|
260
|
+
return unless Contrast::Agent::Assess::Tracker.trackable?(source)
|
243
261
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
end
|
262
|
+
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
263
|
+
return unless properties
|
264
|
+
|
265
|
+
build_events finding, properties.event if properties.event
|
249
266
|
|
250
267
|
# Google::Protobuf::Map doesn't support merge!, so we have to do this
|
251
268
|
# long form
|
252
|
-
source_props =
|
269
|
+
source_props = properties.properties
|
253
270
|
return unless source_props
|
254
271
|
|
255
272
|
source_props.each_pair do |key, value|
|
@@ -258,11 +275,42 @@ module Contrast
|
|
258
275
|
end
|
259
276
|
end
|
260
277
|
|
278
|
+
def build_events finding, event
|
279
|
+
return unless event
|
280
|
+
|
281
|
+
event.parent_events&.each do |parent_event|
|
282
|
+
build_events(finding, parent_event)
|
283
|
+
end
|
284
|
+
# events could technically be nil, but we would have failed
|
285
|
+
# the rule check before getting here. not worth the nil check
|
286
|
+
finding.events << event.to_dtm_event
|
287
|
+
end
|
288
|
+
|
261
289
|
def build_hash finding, source
|
262
290
|
hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
|
263
291
|
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
|
264
292
|
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
265
293
|
end
|
294
|
+
|
295
|
+
# Because our APIs are not versioned, TeamServer relies on a field,
|
296
|
+
# version, to tell it what, if any, validation it can preform on
|
297
|
+
# the findings we send it. Examine the finding and determine the
|
298
|
+
# level to which it conforms.
|
299
|
+
#
|
300
|
+
# @param finding [Contrast::Api::Dtm::Finding]
|
301
|
+
# @return [int] the version of compliance
|
302
|
+
def determine_compliance_version finding
|
303
|
+
return MINIMUM_FINDING_VERSION unless finding
|
304
|
+
# as routes are the only variable between findings, in the case
|
305
|
+
# where we couldn't determine one, any finding with a route is at
|
306
|
+
# maximum compliance
|
307
|
+
return CURRENT_FINDING_VERSION if finding.routes.any?
|
308
|
+
# any finding without events is not of a dataflow type and
|
309
|
+
# therefore at maximum compliance
|
310
|
+
return CURRENT_FINDING_VERSION unless finding.events.any?
|
311
|
+
|
312
|
+
MINIMUM_FINDING_VERSION
|
313
|
+
end
|
266
314
|
end
|
267
315
|
end
|
268
316
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
require 'contrast/agent/assess/policy/trigger/reflected_xss'
|
5
|
+
require 'contrast/agent/assess/policy/trigger/xpath'
|
6
|
+
require 'contrast/api/decorators/trace_taint_range_tags'
|
7
7
|
|
8
8
|
module Contrast
|
9
9
|
module Agent
|
@@ -100,16 +100,17 @@ module Contrast
|
|
100
100
|
# if the source isn't tracked, there can't be a violation
|
101
101
|
# this condition may not hold true forever, but for now it's
|
102
102
|
# a nice optimization
|
103
|
-
return false unless
|
103
|
+
return false unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
104
104
|
|
105
|
+
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
105
106
|
# find the ranges that violate the rule (untrusted, etc)
|
106
|
-
vulnerable_ranges =
|
107
|
+
vulnerable_ranges = ranges_with_all_tags(Contrast::Utils::StringUtils.ret_length(source), properties, required_tags)
|
107
108
|
# if there aren't any vulnerable ranges, nope out
|
108
109
|
return false if vulnerable_ranges.empty?
|
109
110
|
|
110
111
|
# find the ranges that are exempt from the rule
|
111
112
|
# (validated, sanitized, etc)
|
112
|
-
secure_ranges =
|
113
|
+
secure_ranges = ranges_with_any_tag(properties, disallowed_tags)
|
113
114
|
# if there are vulnerable ranges and no secure, report
|
114
115
|
return true if secure_ranges.empty?
|
115
116
|
|
@@ -178,68 +179,62 @@ module Contrast
|
|
178
179
|
# @param length [Integer] the length of the object which may have the
|
179
180
|
# given tags -- used as the maximum index to search for all of the
|
180
181
|
# tags.
|
181
|
-
# @param
|
182
|
+
# @param properties [Contrast::Agent::Assess::Properties] the
|
182
183
|
# properties to check for the tags
|
183
|
-
# @param
|
184
|
+
# @param required_tags [Set<String>] the list of tags on which to match
|
184
185
|
# @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
|
185
186
|
# by the given conditions
|
186
|
-
def
|
187
|
-
# if there
|
188
|
-
|
189
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless
|
190
|
-
|
191
|
-
|
192
|
-
return find_ranges_by_any_tag(cs__properties, tags) if tags.length == 1
|
187
|
+
def ranges_with_all_tags length, properties, required_tags
|
188
|
+
# if there are no tags, not required tags, or the tags don't have
|
189
|
+
# all the required tags, we can just return here.
|
190
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
|
191
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags&.any?
|
192
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags.all? { |tag| properties.tag_keys.include?(tag) }
|
193
193
|
|
194
194
|
ranges = []
|
195
|
-
|
196
|
-
# tags.each { |tag| applicable << cs__properties.fetch_tag(tag) }
|
197
|
-
# return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless applicable.length == tags.length
|
198
|
-
# ...
|
195
|
+
chunking = false
|
199
196
|
# find all the indicies on the source that have all the given tags
|
200
197
|
(0..length).each do |idx|
|
201
|
-
tags_at =
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
198
|
+
tags_at = properties.tags_at(idx).to_a
|
199
|
+
# only those that have all the required tags in the tags_at
|
200
|
+
# satisfy the requirement
|
201
|
+
satisfied = tags_at.any? && required_tags.all? { |tag| tags_at.any? { |found| found.label == tag } }
|
202
|
+
# if this range matches all the required tags and we're already
|
203
|
+
# chunking, meaning the previous range matched, do nothing
|
204
|
+
next if satisfied && chunking
|
205
|
+
|
206
|
+
# if we are satisfied and we were not chunking, this represents
|
207
|
+
# the start of the next range, so create a new entry.
|
208
|
+
if satisfied
|
209
|
+
ranges << Contrast::Agent::Assess::Tag.new('required', 0, idx)
|
210
|
+
chunking = true
|
211
|
+
# if we are chunking and not satisfied, this represents the end
|
212
|
+
# of the range, meaning the last index is what satisfied the
|
213
|
+
# range. Because the range is exclusive end, we can just use this
|
214
|
+
# index.
|
215
|
+
elsif chunking
|
216
|
+
ranges[-1]&.update_end(idx)
|
217
|
+
chunking = false
|
209
218
|
end
|
210
219
|
end
|
211
|
-
|
212
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if ranges.empty?
|
213
|
-
|
214
|
-
# chunk all the adjacent ranges
|
215
|
-
chunked = ranges.chunk_while { |i, j| i + 1 == j }
|
216
|
-
tag_ranges = []
|
217
|
-
# and convert them into Tags
|
218
|
-
chunked.each do |join|
|
219
|
-
start = join[0]
|
220
|
-
stop = join[-1]
|
221
|
-
# add the 1 to account for end index being exclusive
|
222
|
-
tag_length = stop - start + 1
|
223
|
-
tag_ranges = Contrast::Utils::TagUtil.ordered_merge(tag_ranges, Tag.new(tag_length, start))
|
224
|
-
end
|
225
|
-
tag_ranges
|
220
|
+
ranges
|
226
221
|
end
|
227
222
|
|
228
223
|
# Find the ranges that satisfy any of the given tags.
|
229
224
|
#
|
230
|
-
# @param
|
225
|
+
# @param properties [Contrast::Agent::Assess::Properties] the
|
231
226
|
# properties to check for the tags
|
232
227
|
# @param tags [Set<String>] the list of tags on which to match
|
233
228
|
# @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
|
234
229
|
# by the given conditions
|
235
|
-
def
|
230
|
+
def ranges_with_any_tag properties, tags
|
236
231
|
# if there aren't any all_tags or tags, break early
|
237
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless
|
232
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
|
238
233
|
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
|
239
234
|
|
240
235
|
ranges = []
|
241
236
|
tags.each do |desired|
|
242
|
-
found =
|
237
|
+
found = properties.fetch_tag(desired)
|
243
238
|
next unless found
|
244
239
|
|
245
240
|
# we need to dup here so that we don't change the tags if target is
|