contrast-agent 3.8.5 → 3.9.0
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 +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +0 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
- data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
- data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +6 -6
- data/ext/cs__common/cs__common.h +3 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
- data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
- data/funchook/autom4te.cache/requests +48 -48
- data/funchook/config.log +2 -2
- data/lib/contrast/agent.rb +15 -5
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/assess/contrast_event.rb +9 -8
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
- data/lib/contrast/agent/assess/policy/policy.rb +0 -14
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
- data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
- data/lib/contrast/agent/assess/properties.rb +29 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +98 -55
- data/lib/contrast/agent/feature_state.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/logger_manager.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
- data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
- data/lib/contrast/agent/patching/policy/patch.rb +54 -23
- data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
- data/lib/contrast/agent/patching/policy/policy.rb +4 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/protect/policy/policy.rb +6 -6
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/railtie.rb +11 -5
- data/lib/contrast/agent/request.rb +1 -19
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/scope.rb +116 -19
- data/lib/contrast/agent/service_heartbeat.rb +5 -2
- data/lib/contrast/agent/settings_state.rb +12 -8
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +1 -0
- data/lib/contrast/api/speedracer.rb +2 -2
- data/lib/contrast/components/agent.rb +26 -7
- data/lib/contrast/components/app_context.rb +8 -45
- data/lib/contrast/components/contrast_service.rb +3 -4
- data/lib/contrast/components/interface.rb +1 -1
- data/lib/contrast/components/scope.rb +56 -26
- data/lib/contrast/config/ruby_configuration.rb +8 -3
- data/lib/contrast/delegators.rb +9 -0
- data/lib/contrast/delegators/application_update.rb +32 -0
- data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
- data/lib/contrast/extensions/framework/rack/request.rb +24 -0
- data/lib/contrast/extensions/framework/rack/response.rb +23 -0
- data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
- data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
- data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
- data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
- data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
- data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
- data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
- data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
- data/lib/contrast/extensions/ruby_core/module.rb +17 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
- data/lib/contrast/framework/base_support.rb +63 -0
- data/lib/contrast/framework/manager.rb +109 -0
- data/lib/contrast/framework/platform_version.rb +21 -0
- data/lib/contrast/framework/rails_support.rb +88 -0
- data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
- data/lib/contrast/framework/sinatra_support.rb +94 -0
- data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +92 -37
- data/lib/contrast/utils/duck_utils.rb +59 -39
- data/lib/contrast/utils/environment_util.rb +5 -75
- data/lib/contrast/utils/freeze_util.rb +3 -7
- data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
- data/lib/contrast/utils/job_servers_running.rb +39 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
- data/lib/contrast/utils/service_response_util.rb +0 -6
- data/lib/contrast/utils/sinatra_helper.rb +6 -0
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/resources/assess/policy.json +74 -23
- data/resources/inventory/policy.json +1 -1
- data/resources/protect/policy.json +11 -9
- data/resources/rubocops/object/frozen_cop.rb +1 -1
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +94 -57
- data/ext/cs__scope/cs__scope.c +0 -96
- data/ext/cs__scope/cs__scope.h +0 -33
- data/lib/contrast/agent/assess/class_reverter.rb +0 -82
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
- data/lib/contrast/core_extensions/module.rb +0 -42
- data/lib/contrast/core_extensions/object.rb +0 -27
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
- data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
- data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
- data/lib/contrast/rails_extensions/buffer.rb +0 -30
- data/lib/contrast/rails_extensions/rack.rb +0 -45
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
- data/lib/contrast/utils/operating_environment.rb +0 -38
- data/lib/contrast/utils/path_util.rb +0 -151
- data/lib/contrast/utils/scope_util.rb +0 -99
|
@@ -126,6 +126,10 @@ module Contrast
|
|
|
126
126
|
triggers.select { |trigger| trigger.rule_id == rule_id }
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
def find_source_node class_name, method_name, instance_method
|
|
130
|
+
sources.find { |source| source.class_name == class_name && source.method_name == method_name && source.instance_method == instance_method }
|
|
131
|
+
end
|
|
132
|
+
|
|
129
133
|
def find_node rule_id, class_name, method_name, instance_method
|
|
130
134
|
find_triggers_by_rule(rule_id).find do |node|
|
|
131
135
|
node.class_name == class_name && node.method_name == method_name && node.instance_method == instance_method
|
|
@@ -1,6 +1,8 @@
|
|
|
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
|
+
cs__scoped_require 'contrast/components/interface'
|
|
5
|
+
|
|
4
6
|
module Contrast
|
|
5
7
|
module Agent
|
|
6
8
|
module Patching
|
|
@@ -10,7 +12,10 @@ module Contrast
|
|
|
10
12
|
#
|
|
11
13
|
# @abstract
|
|
12
14
|
class PolicyNode
|
|
13
|
-
|
|
15
|
+
include Contrast::Components::Interface
|
|
16
|
+
access_component :scope
|
|
17
|
+
|
|
18
|
+
attr_accessor :class_name, :instance_method, :method_name, :method_scope, :method_visibility
|
|
14
19
|
attr_reader :properties
|
|
15
20
|
|
|
16
21
|
def node_class
|
|
@@ -25,6 +30,7 @@ module Contrast
|
|
|
25
30
|
@class_name = policy_hash[JSON_CLASS_NAME]
|
|
26
31
|
@instance_method = policy_hash[JSON_INSTANCE_METHOD]
|
|
27
32
|
@method_name = policy_hash[JSON_METHOD_NAME]
|
|
33
|
+
@method_scope = policy_hash[JSON_METHOD_SCOPE]
|
|
28
34
|
@method_visibility = policy_hash[JSON_METHOD_VISIBILITY]
|
|
29
35
|
@properties = policy_hash[JSON_PROPERTIES]
|
|
30
36
|
symbolize
|
|
@@ -42,6 +48,11 @@ module Contrast
|
|
|
42
48
|
raise(ArgumentError, "#{ node_class } #{ id } did not have a proper method name. Unable to create.") unless method_name
|
|
43
49
|
raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_name value. Unable to create.") unless method_name.is_a?(Symbol)
|
|
44
50
|
raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_visibility value. Unable to create.") unless method_visibility.is_a?(Symbol)
|
|
51
|
+
unless method_scope.nil? || Contrast::Agent::Scope.valid_scope?(method_scope)
|
|
52
|
+
raise(ArgumentError, "#{ node_class } #{ id } requires an undefined scope. Unable to create.")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
nil
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
# just turns this into a ruby-ism
|
|
@@ -64,6 +75,7 @@ module Contrast
|
|
|
64
75
|
def symbolize
|
|
65
76
|
@method_name = @method_name.to_sym if @method_name
|
|
66
77
|
@method_visibility = @method_visibility.to_sym if @method_visibility
|
|
78
|
+
@method_scope = @method_scope.to_sym if @method_scope
|
|
67
79
|
end
|
|
68
80
|
|
|
69
81
|
# The keys used to read from policy.json to create the individual
|
|
@@ -71,6 +83,7 @@ module Contrast
|
|
|
71
83
|
JSON_CLASS_NAME = 'class_name'
|
|
72
84
|
JSON_INSTANCE_METHOD = 'instance_method'
|
|
73
85
|
JSON_METHOD_NAME = 'method_name'
|
|
86
|
+
JSON_METHOD_SCOPE = 'scope'
|
|
74
87
|
JSON_METHOD_VISIBILITY = 'method_visibility'
|
|
75
88
|
JSON_PROPERTIES = 'properties'
|
|
76
89
|
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
|
-
cs__scoped_require 'contrast/
|
|
4
|
+
cs__scoped_require 'contrast/extensions/ruby_core/module'
|
|
5
5
|
cs__scoped_require 'contrast/agent/patching/policy/policy_node'
|
|
6
6
|
|
|
7
7
|
module Contrast
|
|
@@ -20,6 +20,7 @@ module Contrast
|
|
|
20
20
|
JSON_APPLICATOR_METHOD = 'applicator_method'
|
|
21
21
|
JSON_REQUIRED_PROPS = 'required_properties'
|
|
22
22
|
JSON_OPTIONAL_PROPS = 'optional_properties'
|
|
23
|
+
JSON_SCOPE = 'scope'
|
|
23
24
|
JSON_ON_EXCEPTION = 'on_exception'
|
|
24
25
|
|
|
25
26
|
attr_reader :rule_id
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
cs__scoped_require 'contrast/agent/patching/policy/policy'
|
|
5
5
|
|
|
6
6
|
# classes required by patches in the policy
|
|
7
|
-
cs__scoped_require 'contrast/
|
|
8
|
-
cs__scoped_require 'contrast/
|
|
9
|
-
cs__scoped_require 'contrast/
|
|
10
|
-
cs__scoped_require 'contrast/
|
|
11
|
-
cs__scoped_require 'contrast/
|
|
12
|
-
cs__scoped_require 'contrast/
|
|
7
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_command_injection_rule'
|
|
8
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_deserialization_rule'
|
|
9
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_no_sqli_rule'
|
|
10
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_path_traversal_rule'
|
|
11
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_sqli_rule'
|
|
12
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_xxe_rule'
|
|
13
13
|
cs__scoped_require 'contrast/agent/protect/policy/trigger_node'
|
|
14
14
|
|
|
15
15
|
module Contrast
|
|
@@ -14,7 +14,7 @@ module Contrast
|
|
|
14
14
|
class Base
|
|
15
15
|
include Contrast::Components::Interface
|
|
16
16
|
|
|
17
|
-
access_component :logging, :analysis, :settings
|
|
17
|
+
access_component :logging, :analysis, :settings, :scope
|
|
18
18
|
|
|
19
19
|
UNKNOWN_USER_INPUT = Contrast::Api::Dtm::UserInput.new.tap do |user_input|
|
|
20
20
|
user_input.input_type = :UNKNOWN
|
|
@@ -90,7 +90,9 @@ module Contrast
|
|
|
90
90
|
# @raise [Contrast::SecurityException] if attack detected while in
|
|
91
91
|
# block mode.
|
|
92
92
|
def check_command_scope gadget_command
|
|
93
|
-
|
|
93
|
+
# If we're within 'deserialization scope', then we've got a
|
|
94
|
+
# deserialization method in our call stack.
|
|
95
|
+
return unless in_deserialization_scope?
|
|
94
96
|
|
|
95
97
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
|
96
98
|
kwargs = { COMMAND_SCOPE: true }
|
|
@@ -100,30 +102,6 @@ module Contrast
|
|
|
100
102
|
raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
|
|
101
103
|
end
|
|
102
104
|
|
|
103
|
-
# I don't know a better way to do this without introducing another
|
|
104
|
-
# scope.
|
|
105
|
-
# The policy files are designed around using the Module names, not
|
|
106
|
-
# the file names, but stack is built using filenames. These are the
|
|
107
|
-
# files and methods we patch for this rule.
|
|
108
|
-
#
|
|
109
|
-
# There's not real test for this since I can't figure out how to get
|
|
110
|
-
# a command to execute from within the .load methods
|
|
111
|
-
# Assuming someone else does, this should just work (tested with
|
|
112
|
-
# Screener and the combination "%w[erb.rb `result']")
|
|
113
|
-
DESERIALIZER_STACK = [
|
|
114
|
-
%w[/psych.rb: `load'],
|
|
115
|
-
%w[/marshal.rb: `load']
|
|
116
|
-
].cs__freeze
|
|
117
|
-
# We're considered deserializing if the call stack includes a
|
|
118
|
-
# reference to the file in which a serializer is defined and
|
|
119
|
-
# the method of that serializer responsible for deserializing.
|
|
120
|
-
# @return [Boolean] if the caller indicates deserialization.
|
|
121
|
-
def deserializing?
|
|
122
|
-
Kernel.caller.product(DESERIALIZER_STACK).find do |(frame, (class_signature_form, method_signature_form))|
|
|
123
|
-
frame.end_with?(method_signature_form) && frame.include?(class_signature_form)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
105
|
protected
|
|
128
106
|
|
|
129
107
|
# Build the RaspRuleSample for the detected Deserialization attack.
|
|
@@ -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
|
-
cs__scoped_require 'contrast/
|
|
4
|
+
cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_sqli_rule'
|
|
5
5
|
|
|
6
6
|
module Contrast
|
|
7
7
|
module Agent
|
|
@@ -1,24 +1,30 @@
|
|
|
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
|
-
cs__scoped_require 'contrast/utils/
|
|
4
|
+
cs__scoped_require 'contrast/utils/job_servers_running'
|
|
5
5
|
|
|
6
6
|
module Contrast
|
|
7
7
|
module Agent
|
|
8
8
|
# A Railtie to allow for the automatic hooking of the Agent into a Rails
|
|
9
9
|
# application.
|
|
10
10
|
class Railtie < Rails::Railtie
|
|
11
|
+
include Contrast::Components::Interface
|
|
12
|
+
access_component :agent, :app_context, :logging
|
|
13
|
+
|
|
11
14
|
initializer 'Contrast Ruby Agent Initializer' do |app|
|
|
12
15
|
if defined?(Rails) && defined?(Rails.logger)
|
|
13
16
|
Rails.logger.debug('In railtie ::')
|
|
14
17
|
Rails.logger.debug(app.middleware.inspect)
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
# TODO: RUBY-564 This logic is not specific to Rails and should be used more broadly
|
|
21
|
+
# with all web frameworks. Move this check to be a part of our new initialization
|
|
22
|
+
# routine.
|
|
23
|
+
if APP_CONTEXT.instrument_middleware_stack?
|
|
24
|
+
AGENT.insert_middleware(app)
|
|
19
25
|
else
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
Rails.logger.debug('Detected a running job server, skipping Contrast middleware insertion.')
|
|
27
|
+
logger.debug(nil, "Disabling Contrast for process #{ Process.pid }")
|
|
22
28
|
end
|
|
23
29
|
end
|
|
24
30
|
|
|
@@ -265,17 +265,7 @@ module Contrast
|
|
|
265
265
|
http_request.version = '1.1' # currently not in rack request; hard-coding
|
|
266
266
|
http_request.method = Contrast::Utils::StringUtils.force_utf8(request_method)
|
|
267
267
|
http_request.raw = Contrast::Utils::StringUtils.force_utf8(@rack_request.path_info)
|
|
268
|
-
http_request.parsed_connection =
|
|
269
|
-
http_request.uri = Contrast::Utils::StringUtils.force_utf8(path)
|
|
270
|
-
http_request.normalized_uri = Contrast::Utils::StringUtils.force_utf8(normalized_uri)
|
|
271
|
-
http_context = if http_request.uri == Contrast::Utils::ObjectShare::SLASH
|
|
272
|
-
Contrast::Utils::ObjectShare::SLASH
|
|
273
|
-
else
|
|
274
|
-
http_request.uri.split(Contrast::Utils::ObjectShare::SLASH).first
|
|
275
|
-
end
|
|
276
|
-
http_request.context = Contrast::Utils::StringUtils.force_utf8(http_context)
|
|
277
|
-
http_request.path = Contrast::Utils::StringUtils.force_utf8(http_request.uri)
|
|
278
|
-
http_request.query_string = Contrast::Utils::StringUtils.force_utf8(query_string)
|
|
268
|
+
http_request.parsed_connection = false
|
|
279
269
|
end
|
|
280
270
|
|
|
281
271
|
def append_params http_request
|
|
@@ -395,10 +385,6 @@ module Contrast
|
|
|
395
385
|
@_content_type ||= @rack_request.content_type
|
|
396
386
|
end
|
|
397
387
|
|
|
398
|
-
def path
|
|
399
|
-
@_path ||= @rack_request.path
|
|
400
|
-
end
|
|
401
|
-
|
|
402
388
|
def request_cookies
|
|
403
389
|
@_request_cookies ||= @rack_request.cookies
|
|
404
390
|
end
|
|
@@ -407,10 +393,6 @@ module Contrast
|
|
|
407
393
|
@_parameters ||= with_contrast_scope { normalize_params(@rack_request.params) }
|
|
408
394
|
end
|
|
409
395
|
|
|
410
|
-
def base_url
|
|
411
|
-
@_base_url ||= @rack_request.base_url
|
|
412
|
-
end
|
|
413
|
-
|
|
414
396
|
# End of Rack Request memoized values
|
|
415
397
|
|
|
416
398
|
def file_names
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
-
# frozen_string_literal:
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
# intentional -- we're using a << operator here
|
|
5
5
|
|
|
@@ -18,8 +18,8 @@ module Contrast
|
|
|
18
18
|
include Contrast::Components::Interface
|
|
19
19
|
access_component :logging, :scope
|
|
20
20
|
|
|
21
|
-
SELF_DEFINITION = 'def self.'
|
|
22
|
-
DEFINITION = 'def '
|
|
21
|
+
SELF_DEFINITION = 'def self.'
|
|
22
|
+
DEFINITION = 'def '
|
|
23
23
|
|
|
24
24
|
class << self
|
|
25
25
|
def rewrite_class module_data, redo_rewrite = false
|
|
@@ -95,6 +95,7 @@ module Contrast
|
|
|
95
95
|
return nil if location.empty? || location[0].empty? || location[0].include?('eval')
|
|
96
96
|
return nil if opener.written_from_location?(location)
|
|
97
97
|
|
|
98
|
+
opener.written_from_location!(location)
|
|
98
99
|
opener.source_code(location, method_name)
|
|
99
100
|
rescue SyntaxError
|
|
100
101
|
logger.debug(nil, "SyntaxError: Can't parse method source from #{ clazz }##{ method_name }")
|
data/lib/contrast/agent/scope.rb
CHANGED
|
@@ -1,28 +1,125 @@
|
|
|
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
|
-
# Scope lets us disable Contrast for certain code calls. We need to do this so
|
|
5
|
-
# that we don't propagate through our own code.
|
|
6
|
-
#
|
|
7
|
-
# Think logging: If you have
|
|
8
|
-
# something like "The source was '" + source + "'", and source is tracked,
|
|
9
|
-
# you'll trigger propagation with the + method. This in turn would cause
|
|
10
|
-
# propagation if you log there "The target ''" + target + "' was propagated'"
|
|
11
|
-
# Which would then cause another propagation with the '+' method, forever.
|
|
12
|
-
#
|
|
13
|
-
# Instead, we should say "If I'm already doing Contrast things, don't track
|
|
14
|
-
# this"
|
|
15
|
-
#
|
|
16
|
-
# NOTE: DO NOT DO ANYTHING IN THIS CLASS THAT COULD RESULT IN A MONKEY PATCHED
|
|
17
|
-
# ACTION. WE CAN'T ENTER SCOPE BEFORE WE HAVE A SCOPE! YOU HAVE BEEN WARNED!!!
|
|
18
4
|
module Contrast
|
|
19
5
|
module Agent
|
|
6
|
+
# Scope lets us disable Contrast for certain code calls. We need to do this so
|
|
7
|
+
# that we don't propagate through our own code.
|
|
8
|
+
#
|
|
9
|
+
# Think logging: If you have
|
|
10
|
+
# something like "The source was '" + source + "'", and source is tracked,
|
|
11
|
+
# you'll trigger propagation with the + method. This in turn would cause
|
|
12
|
+
# propagation if you log there "The target ''" + target + "' was propagated'"
|
|
13
|
+
# Which would then cause another propagation with the '+' method, forever.
|
|
14
|
+
#
|
|
15
|
+
# Instead, we should say "If I'm already doing Contrast things, don't track
|
|
16
|
+
# this"
|
|
20
17
|
class Scope
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
18
|
+
# The following %i[] list is the authoritative list
|
|
19
|
+
# of scopes. If you define a new symbol here, you'll
|
|
20
|
+
# get scope methods:
|
|
21
|
+
# %i[monkey] ->
|
|
22
|
+
# enter_monkey_scope!
|
|
23
|
+
# exit_monkey_scope!
|
|
24
|
+
# in_monkey_scope?
|
|
25
|
+
# with_monkey_scope { special_monkey_function }
|
|
26
|
+
SCOPE_LIST = %i[contrast deserialization].cs__freeze
|
|
27
|
+
|
|
28
|
+
iv_list = SCOPE_LIST.map { |name| :"@#{ name }_scope" }
|
|
29
|
+
define_method 'initialize' do
|
|
30
|
+
iv_list.each do |iv_sym|
|
|
31
|
+
instance_variable_set(iv_sym, 0)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
SCOPE_LIST.each do |name|
|
|
36
|
+
iv_sym = :"@#{ name }_scope"
|
|
37
|
+
|
|
38
|
+
define_method "in_#{ name }_scope?" do
|
|
39
|
+
instance_variable_get(iv_sym).positive?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
enter_method_sym = :"enter_#{ name }_scope!"
|
|
43
|
+
define_method enter_method_sym do
|
|
44
|
+
level = instance_variable_get(iv_sym)
|
|
45
|
+
instance_variable_set(iv_sym, level + 1)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
exit_method_sym = :"exit_#{ name }_scope!"
|
|
49
|
+
define_method exit_method_sym do
|
|
50
|
+
# by design, can go below zero.
|
|
51
|
+
# every exit/enter pair (regardless of series)
|
|
52
|
+
# should cancel each other out.
|
|
53
|
+
#
|
|
54
|
+
# so we prefer this sequence:
|
|
55
|
+
# scope = 0
|
|
56
|
+
# exit = -1
|
|
57
|
+
# enter = 0
|
|
58
|
+
# enter = 1
|
|
59
|
+
# exit = 0
|
|
60
|
+
# scope = 0
|
|
61
|
+
#
|
|
62
|
+
# over this sequence:
|
|
63
|
+
# scope = 0
|
|
64
|
+
# exit = 0
|
|
65
|
+
# enter = 1
|
|
66
|
+
# enter = 2
|
|
67
|
+
# exit = 1
|
|
68
|
+
# scope = 1
|
|
69
|
+
level = instance_variable_get(iv_sym)
|
|
70
|
+
instance_variable_set(iv_sym, level - 1)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
define_method "with_#{ name }_scope" do |*_args, &block|
|
|
74
|
+
begin
|
|
75
|
+
send enter_method_sym
|
|
76
|
+
block.call
|
|
77
|
+
ensure
|
|
78
|
+
send exit_method_sym
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Dynamic versions of the above.
|
|
84
|
+
# These are equivalent, but they're slower and riskier.
|
|
85
|
+
# Prefer the static methods if you know what scope you need at the call site.
|
|
86
|
+
def in_scope? name
|
|
87
|
+
cs__class.ensure_valid_scope! name
|
|
88
|
+
call = with_contrast_scope { :"in_#{ name }_scope?" }
|
|
89
|
+
send(call)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def enter_scope! name
|
|
93
|
+
cs__class.ensure_valid_scope! name
|
|
94
|
+
call = with_contrast_scope { :"enter_#{ name }_scope!" }
|
|
95
|
+
send(call)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def exit_scope! name
|
|
99
|
+
cs__class.ensure_valid_scope! name
|
|
100
|
+
call = with_contrast_scope { :"exit_#{ name }_scope!" }
|
|
101
|
+
send(call)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def with_scope name, &block
|
|
105
|
+
cs__class.ensure_valid_scope! name
|
|
106
|
+
call = with_contrast_scope { :"with_#{ name }_scope" }
|
|
107
|
+
send(call, &block)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class << self
|
|
111
|
+
def valid_scope? scope_sym
|
|
112
|
+
Contrast::Agent::Scope::SCOPE_LIST.include? scope_sym
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def ensure_valid_scope! scope_sym
|
|
116
|
+
unless valid_scope? scope_sym # rubocop:disable Style/GuardClause
|
|
117
|
+
with_contrast_scope do
|
|
118
|
+
raise NotImplementedError, "Scope '#{ scope_sym.inspect }' is not registered as a scope."
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
25
123
|
end
|
|
26
124
|
end
|
|
27
125
|
end
|
|
28
|
-
cs__scoped_require 'cs__scope/cs__scope'
|
|
@@ -20,8 +20,7 @@ module Contrast
|
|
|
20
20
|
loop do
|
|
21
21
|
begin
|
|
22
22
|
logger.debug(nil, 'Sending Heartbeat...')
|
|
23
|
-
|
|
24
|
-
CONTRAST_SERVICE.queue_message(Contrast::Api::Dtm::Noop.new)
|
|
23
|
+
CONTRAST_SERVICE.queue_message(poll_message)
|
|
25
24
|
end
|
|
26
25
|
sleep REFRESH_INTERVAL_SEC
|
|
27
26
|
end
|
|
@@ -29,6 +28,10 @@ module Contrast
|
|
|
29
28
|
end
|
|
30
29
|
alias_method :start, :updater_thread
|
|
31
30
|
|
|
31
|
+
def poll_message
|
|
32
|
+
@_poll_message ||= Contrast::Api::Dtm::Poll.new
|
|
33
|
+
end
|
|
34
|
+
|
|
32
35
|
def stop
|
|
33
36
|
Thread.kill(@_updater_thread) if @_updater_thread&.alive?
|
|
34
37
|
end
|
|
@@ -120,16 +120,20 @@ module Contrast
|
|
|
120
120
|
def send_inventory_message
|
|
121
121
|
return unless INVENTORY.enabled?
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
msg.platform = Contrast::Api::Dtm::Platform.new
|
|
125
|
-
msg.platform.major, msg.platform.minor, msg.platform.build = *Contrast::Utils::EnvironmentUtil.platform
|
|
123
|
+
app_update_msg = Contrast::Api::Dtm::ApplicationUpdate.new
|
|
126
124
|
|
|
127
|
-
|
|
128
|
-
Contrast::Utils::EnvironmentUtil.
|
|
129
|
-
Contrast::Utils::EnvironmentUtil.scan_routes(msg)
|
|
130
|
-
Contrast::Utils::InventoryUtil.append_db_config(msg)
|
|
125
|
+
# TODO: RUBY-770
|
|
126
|
+
Contrast::Utils::EnvironmentUtil.add_library_to_app_update(app_update_msg, protobuf_format(CONFIG.root.inventory.tags))
|
|
131
127
|
|
|
132
|
-
|
|
128
|
+
Contrast::Delegators::ApplicationUpdate.new(app_update_msg).instance_eval do
|
|
129
|
+
append_view_technology_descriptor_data(Contrast::Agent.framework_manager.find_applicable_view_technologies) if INVENTORY.enabled?
|
|
130
|
+
append_route_coverage_data(Contrast::Agent.framework_manager.find_route_discovery_data) if INVENTORY.enabled?
|
|
131
|
+
append_platform_version(Contrast::Agent.framework_manager.platform_version)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
Contrast::Utils::InventoryUtil.append_db_config(app_update_msg)
|
|
135
|
+
|
|
136
|
+
CONTRAST_SERVICE.queue_message app_update_msg
|
|
133
137
|
end
|
|
134
138
|
|
|
135
139
|
def present? str
|