contrast-agent 6.2.0 → 6.3.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_basic_object/cs__assess_basic_object.c +7 -5
- data/ext/cs__assess_kernel/cs__assess_kernel.c +14 -3
- data/ext/cs__assess_kernel/cs__assess_kernel.h +2 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +10 -3
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +2 -1
- data/ext/cs__assess_regexp/cs__assess_regexp.c +9 -7
- data/ext/{cs__assess_string_interpolation26/cs__assess_string_interpolation26.c → cs__assess_string_interpolation/cs__assess_string_interpolation.c} +14 -3
- data/ext/{cs__assess_string_interpolation26/cs__assess_string_interpolation26.h → cs__assess_string_interpolation/cs__assess_string_interpolation.h} +1 -1
- data/ext/{cs__assess_string_interpolation26 → cs__assess_string_interpolation}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +5 -4
- data/ext/cs__contrast_patch/cs__contrast_patch.c +3 -10
- data/lib/contrast/agent/assess/events/source_event.rb +16 -12
- data/lib/contrast/agent/assess/policy/policy_node.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -39
- data/lib/contrast/agent/assess/policy/propagation_node.rb +8 -0
- data/lib/contrast/agent/assess/policy/propagator/base.rb +2 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +2 -47
- data/lib/contrast/agent/assess/policy/source_node.rb +1 -0
- data/lib/contrast/agent/assess/policy/trigger_node.rb +8 -0
- data/lib/contrast/agent/assess/property/evented.rb +4 -18
- data/lib/contrast/agent/assess/tag.rb +19 -0
- data/lib/contrast/agent/at_exit_hook.rb +8 -8
- data/lib/contrast/agent/inventory/database_config.rb +6 -3
- data/lib/contrast/agent/inventory/dependency_analysis.rb +3 -2
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +10 -10
- data/lib/contrast/agent/middleware.rb +4 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +27 -2
- data/lib/contrast/agent/patching/policy/policy.rb +5 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +6 -0
- data/lib/contrast/agent/patching/policy/trigger_node.rb +3 -0
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +3 -4
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -0
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +2 -2
- data/lib/contrast/agent/protect/rule/base.rb +1 -0
- data/lib/contrast/agent/protect/rule/no_sqli.rb +2 -0
- data/lib/contrast/agent/reporting/reporter.rb +32 -7
- data/lib/contrast/agent/reporting/reporter_heartbeat.rb +21 -15
- data/lib/contrast/agent/reporting/reporting_events/application_update.rb +5 -24
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +8 -1
- data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +8 -1
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +7 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +10 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_object.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +12 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +11 -1
- data/lib/contrast/agent/reporting/reporting_events/library_discovery.rb +29 -32
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +13 -1
- data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +11 -8
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -5
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +8 -1
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +9 -1
- data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +10 -1
- data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +11 -4
- data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -8
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -4
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -22
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -3
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +1 -11
- data/lib/contrast/agent/request.rb +5 -7
- data/lib/contrast/agent/request_context.rb +8 -17
- data/lib/contrast/agent/request_context_extend.rb +8 -9
- data/lib/contrast/agent/request_handler.rb +9 -38
- data/lib/contrast/agent/rule_set.rb +4 -0
- data/lib/contrast/agent/service_heartbeat.rb +1 -1
- data/lib/contrast/agent/static_analysis.rb +6 -11
- data/lib/contrast/agent/telemetry/base.rb +35 -35
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +2 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message.rb +5 -2
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +3 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +3 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions.rb +0 -1
- data/lib/contrast/agent/thread_watcher.rb +1 -4
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/socket.rb +1 -0
- data/lib/contrast/api/decorators/message.rb +0 -6
- data/lib/contrast/api/decorators.rb +0 -2
- data/lib/contrast/components/assess.rb +0 -6
- data/lib/contrast/components/config.rb +18 -2
- data/lib/contrast/config/base_configuration.rb +0 -13
- data/lib/contrast/config/root_configuration.rb +1 -0
- data/lib/contrast/config/ruby_configuration.rb +2 -9
- data/lib/contrast/configuration.rb +0 -2
- data/lib/contrast/extension/assess/eval_trigger.rb +0 -4
- data/lib/contrast/extension/assess/hash.rb +3 -2
- data/lib/contrast/extension/assess/kernel.rb +22 -0
- data/lib/contrast/extension/assess/marshal.rb +16 -0
- data/lib/contrast/extension/assess/string.rb +21 -20
- data/lib/contrast/framework/base_support.rb +8 -0
- data/lib/contrast/framework/manager.rb +6 -20
- data/lib/contrast/framework/manager_extend.rb +0 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +11 -16
- data/lib/contrast/logger/aliased_logging.rb +2 -0
- data/lib/contrast/utils/assess/source_method_utils.rb +0 -9
- data/lib/contrast/utils/lru_cache.rb +3 -0
- data/lib/contrast/utils/middleware_utils.rb +2 -0
- data/lib/contrast/utils/telemetry_client.rb +7 -7
- data/resources/assess/policy.json +2 -11
- data/ruby-agent.gemspec +1 -1
- metadata +22 -20
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +0 -30
- data/lib/contrast/api/decorators/application_update.rb +0 -44
- data/lib/contrast/api/decorators/library.rb +0 -56
- data/lib/contrast/framework/platform_version.rb +0 -22
|
@@ -33,7 +33,7 @@ module Contrast
|
|
|
33
33
|
@messaging_queue = Contrast::Api::Communication::MessagingQueue.new
|
|
34
34
|
end
|
|
35
35
|
@reporter = Contrast::Agent::Reporter.new
|
|
36
|
-
@reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
|
|
36
|
+
@reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
|
|
37
37
|
@telemetry = Contrast::Agent::Telemetry::Base.new if Contrast::Agent::Telemetry::Base.enabled?
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -50,9 +50,6 @@ module Contrast
|
|
|
50
50
|
@pids[Process.pid] = @pids[Process.pid] && telemetry_status
|
|
51
51
|
end
|
|
52
52
|
reporter_status = init_thread(reporter)
|
|
53
|
-
|
|
54
|
-
return @pids unless Contrast::Agent::Reporter.enabled?
|
|
55
|
-
|
|
56
53
|
reporter_heartbeat_status = init_thread(reporter_heartbeat)
|
|
57
54
|
@pids[Process.pid] = @pids[Process.pid] && reporter_status && reporter_heartbeat_status
|
|
58
55
|
@pids
|
|
@@ -34,6 +34,7 @@ module Contrast
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Override this method to return a socket. Should be interface compatible with TCPSocket, UNIXSocket, etc.
|
|
37
|
+
# @raise[NoMethodError] abstract method, needs to be implemented
|
|
37
38
|
def new_socket
|
|
38
39
|
raise(NoMethodError, 'This is abstract, override it.')
|
|
39
40
|
end
|
|
@@ -19,14 +19,10 @@ module Contrast
|
|
|
19
19
|
|
|
20
20
|
def append_event event
|
|
21
21
|
case event
|
|
22
|
-
when Contrast::Api::Dtm::ServerActivity
|
|
23
|
-
self.server_activity = event
|
|
24
22
|
when Contrast::Api::Dtm::AgentStartup
|
|
25
23
|
self.agent_startup = event
|
|
26
24
|
when Contrast::Api::Dtm::ApplicationCreate
|
|
27
25
|
self.application_create = event
|
|
28
|
-
when Contrast::Api::Dtm::ApplicationUpdate
|
|
29
|
-
self.application_update = event
|
|
30
26
|
when Contrast::Api::Dtm::Activity
|
|
31
27
|
self.activity = event
|
|
32
28
|
when Contrast::Api::Dtm::HttpRequest
|
|
@@ -35,8 +31,6 @@ module Contrast
|
|
|
35
31
|
self.postfilter = event
|
|
36
32
|
when Contrast::Api::Dtm::Poll
|
|
37
33
|
self.poll = event
|
|
38
|
-
when Contrast::Api::Dtm::ObservedRoute
|
|
39
|
-
self.observed_route = event
|
|
40
34
|
else
|
|
41
35
|
logger.error('Unknown event type received. Unsure how to send.', event_type: event.cs__class.cs__name)
|
|
42
36
|
return
|
|
@@ -12,12 +12,10 @@ end
|
|
|
12
12
|
require 'contrast/api/decorators/message'
|
|
13
13
|
require 'contrast/api/decorators/agent_startup'
|
|
14
14
|
require 'contrast/api/decorators/application_startup'
|
|
15
|
-
require 'contrast/api/decorators/application_update'
|
|
16
15
|
require 'contrast/api/decorators/architecture_component'
|
|
17
16
|
require 'contrast/api/decorators/input_analysis'
|
|
18
17
|
require 'contrast/api/decorators/application_settings'
|
|
19
18
|
require 'contrast/api/decorators/server_features'
|
|
20
|
-
require 'contrast/api/decorators/library'
|
|
21
19
|
require 'contrast/api/decorators/route_coverage'
|
|
22
20
|
require 'contrast/api/decorators/trace_event_object'
|
|
23
21
|
require 'contrast/api/decorators/trace_event_signature'
|
|
@@ -76,12 +76,6 @@ module Contrast
|
|
|
76
76
|
@_scan_response
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
def track_frozen_sources?
|
|
80
|
-
@_track_frozen_sources = !false?(::Contrast::CONFIG.root.agent.ruby.track_frozen_sources) if
|
|
81
|
-
@_track_frozen_sources.nil?
|
|
82
|
-
@_track_frozen_sources
|
|
83
|
-
end
|
|
84
|
-
|
|
85
79
|
def require_scan?
|
|
86
80
|
@_require_scan = !false?(::Contrast::CONFIG.root.agent.ruby.require_scan) if @_require_scan.nil?
|
|
87
81
|
@_require_scan
|
|
@@ -27,7 +27,7 @@ module Contrast
|
|
|
27
27
|
CONTRAST_LOG = 'contrast_agent.log'
|
|
28
28
|
CONTRAST_NAME = 'Contrast Agent'
|
|
29
29
|
|
|
30
|
-
class Interface # :nodoc:
|
|
30
|
+
class Interface # :nodoc: # rubocop:disable Metrics/ClassLength
|
|
31
31
|
SESSION_VARIABLES = 'Invalid configuration. '\
|
|
32
32
|
"Setting both application.session_id and application.session_metadata is not allowed.\n"
|
|
33
33
|
API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
|
|
@@ -57,6 +57,8 @@ module Contrast
|
|
|
57
57
|
@config = Contrast::Configuration.new
|
|
58
58
|
env_overrides
|
|
59
59
|
validate
|
|
60
|
+
rescue ArgumentError => e
|
|
61
|
+
proto_logger.error('Configuration failed with error: ', e)
|
|
60
62
|
end
|
|
61
63
|
alias_method :rebuild, :build
|
|
62
64
|
|
|
@@ -136,7 +138,7 @@ module Contrast
|
|
|
136
138
|
next unless env_key.to_s.start_with?(CONTRAST_ENV_MARKER)
|
|
137
139
|
|
|
138
140
|
config_item = Contrast::Utils::EnvConfigurationItem.new(env_key, env_value)
|
|
139
|
-
|
|
141
|
+
assign_value_to_path_array(root, config_item.dot_path_array, config_item.value)
|
|
140
142
|
end
|
|
141
143
|
end
|
|
142
144
|
|
|
@@ -193,6 +195,20 @@ module Contrast
|
|
|
193
195
|
def logger_path
|
|
194
196
|
root.agent.logger.path
|
|
195
197
|
end
|
|
198
|
+
|
|
199
|
+
# Assign the value from an ENV variable to the Contrast::Config::RootConfiguration object, when
|
|
200
|
+
# appropriate.
|
|
201
|
+
#
|
|
202
|
+
# @return nil
|
|
203
|
+
def assign_value_to_path_array current_level, dot_path_array, value
|
|
204
|
+
dot_path_array[0...-1].each do |segment|
|
|
205
|
+
segment = segment.tr('-', '_')
|
|
206
|
+
current_level = current_level.send(segment) if current_level.cs__respond_to?(segment)
|
|
207
|
+
end
|
|
208
|
+
return unless current_level.nil? == false && current_level.cs__respond_to?(dot_path_array[-1])
|
|
209
|
+
|
|
210
|
+
current_level.send("#{ dot_path_array[-1] }=", value)
|
|
211
|
+
end
|
|
196
212
|
end
|
|
197
213
|
end
|
|
198
214
|
end
|
|
@@ -20,19 +20,6 @@ module Contrast
|
|
|
20
20
|
end
|
|
21
21
|
hsh
|
|
22
22
|
end
|
|
23
|
-
|
|
24
|
-
def assign_value_to_path_array dot_path_array, value
|
|
25
|
-
current_level = self
|
|
26
|
-
dot_path_array[0...-1].each do |segment|
|
|
27
|
-
segment = segment.tr('-', '_')
|
|
28
|
-
current_level = current_level.send(segment) if current_level.cs__respond_to?(segment)
|
|
29
|
-
end
|
|
30
|
-
last_entry = dot_path_array[-1]
|
|
31
|
-
if current_level.nil? == false && current_level.cs__respond_to?(last_entry)
|
|
32
|
-
current_level.send("#{ last_entry }=", value)
|
|
33
|
-
end
|
|
34
|
-
nil
|
|
35
|
-
end
|
|
36
23
|
end
|
|
37
24
|
end
|
|
38
25
|
end
|
|
@@ -21,7 +21,7 @@ module Contrast
|
|
|
21
21
|
DEFAULT_UNINSTRUMENTED_NAMESPACES = %w[FactoryGirl FactoryBot].cs__freeze
|
|
22
22
|
|
|
23
23
|
attr_writer :disabled_agent_rake_tasks, :exceptions, :interpolate, :propagate_yield, :require_scan,
|
|
24
|
-
:
|
|
24
|
+
:non_request_tracking, :uninstrument_namespace
|
|
25
25
|
|
|
26
26
|
def initialize hsh = {}
|
|
27
27
|
return unless hsh
|
|
@@ -31,7 +31,6 @@ module Contrast
|
|
|
31
31
|
@interpolate = hsh[:interpolate]
|
|
32
32
|
@propagate_yield = hsh[:propagate_yield]
|
|
33
33
|
@require_scan = hsh[:require_scan]
|
|
34
|
-
@track_frozen_sources = hsh[:track_frozen_sources]
|
|
35
34
|
@non_request_tracking = hsh[:non_request_tracking]
|
|
36
35
|
@uninstrument_namespace = hsh[:uninstrument_namespace]
|
|
37
36
|
end
|
|
@@ -67,16 +66,10 @@ module Contrast
|
|
|
67
66
|
@require_scan.nil? ? Contrast::Utils::ObjectShare::TRUE : @require_scan
|
|
68
67
|
end
|
|
69
68
|
|
|
70
|
-
# controls whether or not we track frozen Strings by replacing them
|
|
71
|
-
# @return [Boolean, Contrast::Utils::ObjectShare::TRUE]
|
|
72
|
-
def track_frozen_sources
|
|
73
|
-
@track_frozen_sources.nil? ? Contrast::Utils::ObjectShare::TRUE : @track_frozen_sources
|
|
74
|
-
end
|
|
75
|
-
|
|
76
69
|
# controls tracking outside of request
|
|
77
70
|
# @return [Boolean, Contrast::Utils::ObjectShare::FALSE]
|
|
78
71
|
def non_request_tracking
|
|
79
|
-
@non_request_tracking.nil? ?
|
|
72
|
+
@non_request_tracking.nil? ? Contrast::Utils::ObjectShare::FALSE : @non_request_tracking
|
|
80
73
|
end
|
|
81
74
|
|
|
82
75
|
# @return [Array, DEFAULT_UNINSTRUMENTED_NAMESPACES]
|
|
@@ -18,8 +18,6 @@ module Contrast
|
|
|
18
18
|
include Contrast::Components::Scope::InstanceMethods
|
|
19
19
|
extend Contrast::Components::Scope::InstanceMethods
|
|
20
20
|
|
|
21
|
-
def_delegator :root, :assign_value_to_path_array
|
|
22
|
-
|
|
23
21
|
attr_reader :default_name, :root
|
|
24
22
|
|
|
25
23
|
DEFAULT_YAML_PATH = 'contrast_security.yaml'
|
|
@@ -25,10 +25,6 @@ module Contrast
|
|
|
25
25
|
def apply_trigger obj, source, ret, clazz, method
|
|
26
26
|
return unless ::Contrast::ASSESS.non_request_tracking? || Contrast::Agent::REQUEST_TRACKER.current
|
|
27
27
|
|
|
28
|
-
# Since we know this is the source of the trigger, we can do some
|
|
29
|
-
# optimization here and return when it is not tracked
|
|
30
|
-
return unless Contrast::Utils::Assess::TrackingUtil.tracked?(source)
|
|
31
|
-
|
|
32
28
|
# source might not be all the args passed in, but it is the one we care
|
|
33
29
|
# about. we could pass in all the args in the last param here if it
|
|
34
30
|
# becomes an issue in rendering on TS
|
|
@@ -15,11 +15,12 @@ module Contrast
|
|
|
15
15
|
class << self
|
|
16
16
|
def cs__duplicate_and_freeze object
|
|
17
17
|
return object unless object.is_a?(String) && !object.cs__frozen?
|
|
18
|
-
return object unless Contrast::Agent::Assess::Tracker.tracked?(object)
|
|
19
18
|
|
|
20
19
|
# Copy the object, then freeze it, so that it looks the same
|
|
21
20
|
# externally, but will have our finalizer on it.
|
|
22
|
-
object.dup
|
|
21
|
+
copy = object.dup
|
|
22
|
+
Contrast::Agent::Assess::Tracker.pre_freeze(copy)
|
|
23
|
+
copy&.cs__freeze
|
|
23
24
|
rescue StandardError
|
|
24
25
|
# we'll rescue this error, but we can't log it here as that will
|
|
25
26
|
# result in a seg fault
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
require 'contrast/extension/assess/exec_trigger'
|
|
5
5
|
require 'contrast/components/logger'
|
|
6
6
|
require 'contrast/agent/assess/events/event_data'
|
|
7
|
+
require 'contrast/agent/patching/policy/patch'
|
|
7
8
|
|
|
8
9
|
module Contrast
|
|
9
10
|
module Extension
|
|
@@ -97,6 +98,27 @@ module Contrast
|
|
|
97
98
|
end
|
|
98
99
|
end
|
|
99
100
|
end
|
|
101
|
+
|
|
102
|
+
# Used for Kernel exec aliasing since we have no other way of accessing the
|
|
103
|
+
# Kernel#exec under C if we alias it there.
|
|
104
|
+
module ContrastKernel
|
|
105
|
+
# Method to replace the call to Kernel#exec when applying alias patch.
|
|
106
|
+
# It will invoke Contrast::Extension::Assess::KernelPropagator before
|
|
107
|
+
# calling the original method.
|
|
108
|
+
#
|
|
109
|
+
# @param source [String, Proc] Potentially untrusted shell command to execute.
|
|
110
|
+
# @return nil - This method will invoke the Kernel#exec which will
|
|
111
|
+
def cs__kernel_exec source
|
|
112
|
+
# Check if in contrast scope and we have source.
|
|
113
|
+
unless Contrast::Agent::Patching::Policy::Patch.in_contrast_scope? || source.nil?
|
|
114
|
+
Contrast::Agent::Patching::Policy::Patch.with_contrast_scope do
|
|
115
|
+
Contrast::Extension::Assess::KernelPropagator.apply_trigger(source)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
# Call this in the end else any code bellow this call won't be executed.
|
|
119
|
+
Kernel.exec(source)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
100
122
|
end
|
|
101
123
|
end
|
|
102
124
|
end
|
|
@@ -58,6 +58,22 @@ module Contrast
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
+
|
|
62
|
+
# Used for aliasing
|
|
63
|
+
module ContrastMarshal
|
|
64
|
+
def cs__marshal_load source
|
|
65
|
+
# Do the protect
|
|
66
|
+
Contrast::Extension::Assess::MarshalPropagator.cs__load_protect(source) if source
|
|
67
|
+
# call the original
|
|
68
|
+
result = Marshal.load(source) # rubocop:disable Security/MarshalLoad
|
|
69
|
+
# Do the assess
|
|
70
|
+
tracked = Contrast::Agent::Assess::Tracker::PROPERTIES_HASH.tracked?(source) if source
|
|
71
|
+
skip = Contrast::Agent::Patching::Policy::Patch.skip_assess_analysis? if tracked
|
|
72
|
+
Contrast::Extension::Assess::MarshalPropagator.cs__load_assess(source, result) if skip
|
|
73
|
+
# return original
|
|
74
|
+
result
|
|
75
|
+
end
|
|
76
|
+
end
|
|
61
77
|
end
|
|
62
78
|
end
|
|
63
79
|
end
|
|
@@ -31,31 +31,32 @@ module Contrast
|
|
|
31
31
|
INTERPOLATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
|
|
32
32
|
|
|
33
33
|
class << self
|
|
34
|
+
# We call this method from C, and the Scope check is happening there. If we are in
|
|
35
|
+
# Contrast Scope the method won't be invoked.
|
|
36
|
+
#
|
|
37
|
+
# @param inputs [Array<String>] Inputs for interpolation.
|
|
38
|
+
# @param result [String] The result from the interpolation.
|
|
34
39
|
def track_interpolation inputs, result
|
|
35
40
|
return unless ::Contrast::AGENT.interpolation_enabled?
|
|
36
|
-
return if in_contrast_scope?
|
|
37
41
|
return unless inputs.any? { |input| Contrast::Agent::Assess::Tracker.tracked?(input) }
|
|
42
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(result))
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
offset += input.length
|
|
48
|
-
parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event
|
|
49
|
-
parent_events << parent_event if parent_event
|
|
50
|
-
end
|
|
51
|
-
event_data = Contrast::Agent::Assess::Events::EventData.new(INTERPOLATION_NODE,
|
|
52
|
-
result,
|
|
53
|
-
inputs,
|
|
54
|
-
result,
|
|
55
|
-
inputs)
|
|
56
|
-
properties.build_event(event_data)
|
|
57
|
-
properties.event.instance_variable_set(:@_parent_events, parent_events)
|
|
44
|
+
parent_events = []
|
|
45
|
+
offset = 0
|
|
46
|
+
inputs.each do |input|
|
|
47
|
+
properties.copy_from(input, result, offset)
|
|
48
|
+
add_dynamic_sources_info(input, result)
|
|
49
|
+
offset += input.length
|
|
50
|
+
parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event
|
|
51
|
+
parent_events << parent_event if parent_event
|
|
58
52
|
end
|
|
53
|
+
event_data = Contrast::Agent::Assess::Events::EventData.new(INTERPOLATION_NODE,
|
|
54
|
+
result,
|
|
55
|
+
inputs,
|
|
56
|
+
result,
|
|
57
|
+
inputs)
|
|
58
|
+
properties.build_event(event_data)
|
|
59
|
+
properties.event.instance_variable_set(:@_parent_events, parent_events)
|
|
59
60
|
rescue StandardError => e
|
|
60
61
|
logger.error('Unable to track interpolation', e)
|
|
61
62
|
end
|
|
@@ -8,18 +8,22 @@ module Contrast
|
|
|
8
8
|
# The API for all subclasses to implement to correctly support a given framework
|
|
9
9
|
module BaseSupport
|
|
10
10
|
# The top level module name used by the framework
|
|
11
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
11
12
|
def detection_class
|
|
12
13
|
raise(NoMethodError('Subclasses of BaseSupport should implement this method'))
|
|
13
14
|
end
|
|
14
15
|
|
|
16
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
15
17
|
def version
|
|
16
18
|
raise(NoMethodError('Subclasses of BaseSupport should implement this method'))
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
19
22
|
def application_name
|
|
20
23
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
21
24
|
end
|
|
22
25
|
|
|
26
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
23
27
|
def server_type
|
|
24
28
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
25
29
|
end
|
|
@@ -27,18 +31,22 @@ module Contrast
|
|
|
27
31
|
# Find all the predefined routes for this application
|
|
28
32
|
#
|
|
29
33
|
# @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
|
|
34
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
30
35
|
def collect_routes
|
|
31
36
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
32
37
|
end
|
|
33
38
|
|
|
39
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
34
40
|
def current_route_coverage
|
|
35
41
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
36
42
|
end
|
|
37
43
|
|
|
44
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
38
45
|
def current_route
|
|
39
46
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
# @raise [NoMethodError] raises error if subclass does not implement this method
|
|
42
50
|
def retrieve_request _env
|
|
43
51
|
raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
|
|
44
52
|
end
|
|
@@ -7,7 +7,6 @@ require 'contrast/components/logger'
|
|
|
7
7
|
require 'contrast/extension/module'
|
|
8
8
|
require 'contrast/framework/grape/support'
|
|
9
9
|
require 'contrast/framework/manager_extend'
|
|
10
|
-
require 'contrast/framework/platform_version'
|
|
11
10
|
require 'contrast/framework/rack/support'
|
|
12
11
|
require 'contrast/framework/rails/support'
|
|
13
12
|
require 'contrast/framework/sinatra/support'
|
|
@@ -64,16 +63,6 @@ module Contrast
|
|
|
64
63
|
routes_for_all_frameworks
|
|
65
64
|
end
|
|
66
65
|
|
|
67
|
-
def platform_version
|
|
68
|
-
framework_version = first_framework_result(:version, '')
|
|
69
|
-
|
|
70
|
-
Contrast::Framework::PlatformVersion.from_string(framework_version)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def platform_version_string
|
|
74
|
-
first_framework_result(:version, '')
|
|
75
|
-
end
|
|
76
|
-
|
|
77
66
|
def server_type
|
|
78
67
|
first_framework_result(:server_type, 'rack')
|
|
79
68
|
end
|
|
@@ -157,16 +146,13 @@ module Contrast
|
|
|
157
146
|
next unless module_name == framework.detection_class
|
|
158
147
|
|
|
159
148
|
@_frameworks << framework
|
|
160
|
-
|
|
161
|
-
#
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
Contrast::Agent.reporter.send_event(report)
|
|
166
|
-
else
|
|
167
|
-
Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg)
|
|
149
|
+
report = Contrast::Agent::Reporting::ApplicationUpdate.new
|
|
150
|
+
# This convert here is left as it'll be easier to be replaced when the Library is being changed
|
|
151
|
+
report.libraries = Contrast::Agent::Inventory::DependencyAnalysis.instance.library_pb_list
|
|
152
|
+
[report, Contrast::Agent::Reporting::ApplicationInventory.new].each do |e|
|
|
153
|
+
Contrast::Agent.reporter.send_event(e)
|
|
168
154
|
end
|
|
169
|
-
|
|
155
|
+
|
|
170
156
|
logger.info('Framework detected after initialization. Enabling support.',
|
|
171
157
|
framework: framework.detection_class,
|
|
172
158
|
frameworks: @_frameworks)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'contrast/agent/inventory/dependency_usage_analysis'
|
|
5
|
+
|
|
4
6
|
module Contrast
|
|
5
7
|
module Framework
|
|
6
8
|
module Rails
|
|
@@ -9,27 +11,20 @@ module Contrast
|
|
|
9
11
|
# event on streamed responses.
|
|
10
12
|
module ActionControllerLiveBuffer
|
|
11
13
|
class << self
|
|
12
|
-
# TODO: RUBY-1353
|
|
13
|
-
# TODO: RUBY-1355
|
|
14
|
-
# TODO: RUBY-1357
|
|
15
|
-
# TODO: RUBY-1357
|
|
16
14
|
def send_messages
|
|
17
15
|
return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
|
|
18
16
|
|
|
17
|
+
[
|
|
18
|
+
context.observed_route
|
|
19
|
+
].each do |event|
|
|
20
|
+
Contrast::Agent.reporter&.send_event_immediately(event)
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
if Contrast::Agent::Reporter.enabled?
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
context.observed_library_usage,
|
|
23
|
-
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
|
|
24
|
-
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
|
|
25
|
-
].each do |event|
|
|
26
|
-
Contrast::Agent.reporter&.send_event_immediately(event)
|
|
27
|
-
end
|
|
24
|
+
event = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
|
|
25
|
+
Contrast::Agent.reporter&.send_event_immediately(event)
|
|
28
26
|
else
|
|
29
|
-
Contrast::Agent.
|
|
30
|
-
[context.server_activity, context.activity, context.observed_route].each do |msg|
|
|
31
|
-
Contrast::Agent.messaging_queue&.send_event_immediately(msg)
|
|
32
|
-
end
|
|
27
|
+
Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
|
|
33
28
|
end
|
|
34
29
|
end
|
|
35
30
|
|
|
@@ -88,6 +88,8 @@ module Contrast
|
|
|
88
88
|
end
|
|
89
89
|
message = Contrast::Agent::Telemetry::TelemetryException::Message.build(tags, [message_exception])
|
|
90
90
|
Contrast::Agent::Telemetry::TelemetryException::Event.new(message)
|
|
91
|
+
rescue ArgumentError => e
|
|
92
|
+
debug('TelemetryException failed from aliased logging with: ', e)
|
|
91
93
|
end
|
|
92
94
|
|
|
93
95
|
def get_stack_trace type
|
|
@@ -7,15 +7,6 @@ module Contrast
|
|
|
7
7
|
# This module will include all methods for some internal validations in the SourceMethod module
|
|
8
8
|
# and some other module methods from the same place, so we can ease the main module
|
|
9
9
|
module SourceMethodUtils
|
|
10
|
-
# Safely duplicate the target, or return nil
|
|
11
|
-
#
|
|
12
|
-
# @param target [Object] the thing to check for duplication
|
|
13
|
-
def safe_dup target
|
|
14
|
-
target.dup
|
|
15
|
-
rescue StandardError => _e
|
|
16
|
-
nil
|
|
17
|
-
end
|
|
18
|
-
|
|
19
10
|
# Find the name of the source
|
|
20
11
|
#
|
|
21
12
|
# @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
|
|
@@ -5,6 +5,9 @@ module Contrast
|
|
|
5
5
|
module Utils
|
|
6
6
|
# A LRU(Least Recently Used) Cache store.
|
|
7
7
|
class LRUCache
|
|
8
|
+
# Initializes new Least Recently Used Cache Store
|
|
9
|
+
#
|
|
10
|
+
# @raise [StandardError] raises error if provided capacity is invalid or less or equal to zero
|
|
8
11
|
def initialize capacity = 500
|
|
9
12
|
raise(StandardError('Capacity must be bigger than 0')) if capacity <= 0
|
|
10
13
|
|
|
@@ -34,6 +34,7 @@ module Contrast
|
|
|
34
34
|
SECURITY_EXCEPTION_MARKER = 'Contrast::SecurityException'
|
|
35
35
|
# We're only going to suppress SecurityExceptions indicating a blocked attack. And, only if the
|
|
36
36
|
# config.agent.ruby.exceptions.capture? is set
|
|
37
|
+
# @raise [Contrast::SecurityException] if exceptions.capture? is allowed
|
|
37
38
|
def handle_exception exception
|
|
38
39
|
if security_exception?(exception)
|
|
39
40
|
exception_control = ::Contrast::AGENT.exception_control
|
|
@@ -74,6 +75,7 @@ module Contrast
|
|
|
74
75
|
true
|
|
75
76
|
end
|
|
76
77
|
|
|
78
|
+
# @raise [Contrast::SecurityException] raises error to prevent an attack
|
|
77
79
|
def application_code env
|
|
78
80
|
logger.trace_with_time('application') do
|
|
79
81
|
app.call(env)
|
|
@@ -34,8 +34,8 @@ module Contrast
|
|
|
34
34
|
def build_request event
|
|
35
35
|
return unless valid_event?(event)
|
|
36
36
|
|
|
37
|
-
string_body = if
|
|
38
|
-
event.
|
|
37
|
+
string_body = if event.cs__is_a?(Contrast::Agent::Telemetry::TelemetryException::Event)
|
|
38
|
+
[event.to_controlled_hash]
|
|
39
39
|
else
|
|
40
40
|
[event.to_hash]
|
|
41
41
|
end
|
|
@@ -73,14 +73,14 @@ module Contrast
|
|
|
73
73
|
ready_after if status_code == 429
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
# This method will be responsible for validating the event
|
|
77
|
-
#
|
|
78
|
-
#
|
|
76
|
+
# This method will be responsible for validating the event. Valid if event is of a known
|
|
77
|
+
# Contrast::Agent::Telemetry type
|
|
78
|
+
#
|
|
79
|
+
# @param event [Object]
|
|
79
80
|
def valid_event? event
|
|
80
81
|
return true if event.cs__is_a?(Contrast::Agent::Telemetry::Event)
|
|
81
82
|
return true if event.cs__is_a?(Contrast::Agent::Telemetry::StartupMetricsEvent)
|
|
82
|
-
|
|
83
|
-
return true if Array(event).all?(Contrast::Agent::Telemetry::TelemetryException::Event)
|
|
83
|
+
return true if event.cs__is_a?(Contrast::Agent::Telemetry::TelemetryException::Event)
|
|
84
84
|
|
|
85
85
|
false
|
|
86
86
|
end
|
|
@@ -1092,17 +1092,8 @@
|
|
|
1092
1092
|
"patch_method": "sprintf_tagger",
|
|
1093
1093
|
"source": "O,P1",
|
|
1094
1094
|
"target": "R"
|
|
1095
|
-
},
|
|
1096
|
-
|
|
1097
|
-
"instance_method": true,
|
|
1098
|
-
"method_visibility": "public",
|
|
1099
|
-
"method_name":"quote",
|
|
1100
|
-
"source": "P0",
|
|
1101
|
-
"target": "R",
|
|
1102
|
-
"action": "SPLAT",
|
|
1103
|
-
"tags":["SQL_ENCODED"],
|
|
1104
|
-
"untags":["SQL_DECODED"]
|
|
1105
|
-
}, {
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1106
1097
|
"class_name":"ActiveRecord::ConnectionAdapters::Quoting",
|
|
1107
1098
|
"instance_method": true,
|
|
1108
1099
|
"method_visibility": "public",
|
data/ruby-agent.gemspec
CHANGED
|
@@ -113,7 +113,7 @@ end
|
|
|
113
113
|
# dependencies.csv in this directory to indicate that and create a
|
|
114
114
|
# corresponding update to the fake gem server data in TeamServer.
|
|
115
115
|
def self.add_dependencies spec
|
|
116
|
-
spec.add_dependency 'ougai', '
|
|
116
|
+
spec.add_dependency 'ougai', '>= 1.8', '< 3.0.0'
|
|
117
117
|
spec.add_dependency 'protobuf', '~> 3.10'
|
|
118
118
|
spec.add_dependency 'rack', '~> 2.0'
|
|
119
119
|
end
|