contrast-agent 6.2.0 → 6.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -3
- data/.simplecov +1 -0
- data/Rakefile +0 -27
- 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 +17 -11
- data/lib/contrast/agent/assess/events/source_event.rb +16 -12
- data/lib/contrast/agent/assess/finalizers/hash.rb +1 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +8 -42
- 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/propagator/custom.rb +4 -0
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +7 -47
- data/lib/contrast/agent/assess/policy/source_node.rb +1 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +9 -3
- 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/assess/tracker.rb +12 -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 +5 -4
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +11 -11
- data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- 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/method_policy.rb +3 -3
- 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 +22 -18
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +17 -21
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +26 -3
- 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 +16 -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 +3 -4
- data/lib/contrast/agent/static_analysis.rb +7 -12
- 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/agent/worker_thread.rb +10 -0
- 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/api/dtm.pb.rb +1 -1
- data/lib/contrast/api/settings.pb.rb +1 -1
- data/lib/contrast/components/agent.rb +51 -13
- data/lib/contrast/components/assess.rb +16 -6
- data/lib/contrast/components/config.rb +18 -2
- data/lib/contrast/components/contrast_service.rb +1 -1
- data/lib/contrast/components/heap_dump.rb +51 -1
- data/lib/contrast/components/inventory.rb +19 -13
- data/lib/contrast/components/logger.rb +18 -0
- data/lib/contrast/config/assess_configuration.rb +28 -0
- data/lib/contrast/config/base_configuration.rb +8 -15
- data/lib/contrast/config/root_configuration.rb +12 -8
- data/lib/contrast/config/ruby_configuration.rb +2 -9
- data/lib/contrast/config/service_configuration.rb +4 -4
- data/lib/contrast/config.rb +0 -6
- 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/extension/object.rb +19 -0
- 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/framework/rails/support.rb +4 -1
- data/lib/contrast/logger/aliased_logging.rb +2 -0
- data/lib/contrast/logger/log.rb +2 -1
- data/lib/contrast/utils/assess/event_limit_utils.rb +96 -0
- data/lib/contrast/utils/assess/propagation_method_utils.rb +27 -7
- data/lib/contrast/utils/assess/source_method_utils.rb +0 -9
- data/lib/contrast/utils/log_utils.rb +2 -2
- data/lib/contrast/utils/lru_cache.rb +3 -0
- data/lib/contrast/utils/middleware_utils.rb +2 -0
- data/lib/contrast/utils/patching/policy/patch_utils.rb +6 -23
- data/lib/contrast/utils/telemetry_client.rb +7 -7
- data/lib/contrast.rb +37 -18
- data/lib/protobuf/code_generator.rb +129 -0
- data/lib/protobuf/decoder.rb +28 -0
- data/lib/protobuf/deprecation.rb +117 -0
- data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +79 -0
- data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +360 -0
- data/lib/protobuf/descriptors.rb +3 -0
- data/lib/protobuf/encoder.rb +11 -0
- data/lib/protobuf/enum.rb +365 -0
- data/lib/protobuf/exceptions.rb +9 -0
- data/lib/protobuf/field/base_field.rb +380 -0
- data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
- data/lib/protobuf/field/bool_field.rb +64 -0
- data/lib/protobuf/field/bytes_field.rb +67 -0
- data/lib/protobuf/field/double_field.rb +25 -0
- data/lib/protobuf/field/enum_field.rb +56 -0
- data/lib/protobuf/field/field_array.rb +102 -0
- data/lib/protobuf/field/field_hash.rb +122 -0
- data/lib/protobuf/field/fixed32_field.rb +25 -0
- data/lib/protobuf/field/fixed64_field.rb +28 -0
- data/lib/protobuf/field/float_field.rb +43 -0
- data/lib/protobuf/field/int32_field.rb +21 -0
- data/lib/protobuf/field/int64_field.rb +34 -0
- data/lib/protobuf/field/integer_field.rb +23 -0
- data/lib/protobuf/field/message_field.rb +51 -0
- data/lib/protobuf/field/sfixed32_field.rb +27 -0
- data/lib/protobuf/field/sfixed64_field.rb +28 -0
- data/lib/protobuf/field/signed_integer_field.rb +29 -0
- data/lib/protobuf/field/sint32_field.rb +21 -0
- data/lib/protobuf/field/sint64_field.rb +21 -0
- data/lib/protobuf/field/string_field.rb +51 -0
- data/lib/protobuf/field/uint32_field.rb +21 -0
- data/lib/protobuf/field/uint64_field.rb +21 -0
- data/lib/protobuf/field/varint_field.rb +77 -0
- data/lib/protobuf/field.rb +74 -0
- data/lib/protobuf/generators/base.rb +85 -0
- data/lib/protobuf/generators/enum_generator.rb +39 -0
- data/lib/protobuf/generators/extension_generator.rb +27 -0
- data/lib/protobuf/generators/field_generator.rb +193 -0
- data/lib/protobuf/generators/file_generator.rb +262 -0
- data/lib/protobuf/generators/group_generator.rb +122 -0
- data/lib/protobuf/generators/message_generator.rb +104 -0
- data/lib/protobuf/generators/option_generator.rb +17 -0
- data/lib/protobuf/generators/printable.rb +160 -0
- data/lib/protobuf/generators/service_generator.rb +50 -0
- data/lib/protobuf/lifecycle.rb +33 -0
- data/lib/protobuf/logging.rb +39 -0
- data/lib/protobuf/message/fields.rb +233 -0
- data/lib/protobuf/message/serialization.rb +85 -0
- data/lib/protobuf/message.rb +241 -0
- data/lib/protobuf/optionable.rb +72 -0
- data/lib/protobuf/tasks/compile.rake +80 -0
- data/lib/protobuf/tasks.rb +1 -0
- data/lib/protobuf/varint.rb +20 -0
- data/lib/protobuf/varint_pure.rb +31 -0
- data/lib/protobuf/version.rb +3 -0
- data/lib/protobuf/wire_type.rb +10 -0
- data/lib/protobuf.rb +91 -0
- data/proto/dynamic_discovery.proto +46 -0
- data/proto/google/protobuf/compiler/plugin.proto +183 -0
- data/proto/google/protobuf/descriptor.proto +911 -0
- data/proto/rpc.proto +71 -0
- data/resources/assess/policy.json +6 -23
- data/ruby-agent.gemspec +4 -2
- metadata +122 -33
- 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/config/agent_configuration.rb +0 -63
- data/lib/contrast/config/heap_dump_configuration.rb +0 -59
- data/lib/contrast/config/inventory_configuration.rb +0 -33
- data/lib/contrast/config/logger_configuration.rb +0 -26
- data/lib/contrast/framework/platform_version.rb +0 -22
@@ -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
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Some developers override various methods on Object, which can often involve
|
5
|
+
# changing expected method parity/behavior which in turn prevents us from being
|
6
|
+
# able to reliably use affected methods.
|
7
|
+
# We alias these method so that we always have access to them.
|
8
|
+
#
|
9
|
+
# Because we use these methods in constructing classes (e.g., calling #freeze
|
10
|
+
# on constants within class definitions) we do this aliasing ASAP.
|
11
|
+
class Object
|
12
|
+
alias_method :cs__class, :class
|
13
|
+
alias_method :cs__freeze, :freeze
|
14
|
+
alias_method :cs__frozen?, :frozen?
|
15
|
+
alias_method :cs__is_a?, :is_a?
|
16
|
+
alias_method :cs__method, :method
|
17
|
+
alias_method :cs__respond_to?, :respond_to?
|
18
|
+
alias_method :cs__singleton_class, :singleton_class
|
19
|
+
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
|
|
@@ -135,8 +135,11 @@ module Contrast
|
|
135
135
|
# @return [bool] whether the router is an engine or not.
|
136
136
|
def engine_route? route
|
137
137
|
return false unless route&.app&.app
|
138
|
+
return false unless route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) ||
|
139
|
+
route.app.is_a?(::ActionDispatch::Routing::RouteSet::Dispatcher)
|
138
140
|
|
139
|
-
route.app.is_a?(
|
141
|
+
clazz = route.app.app.is_a?(Class) ? route.app.app : route.app.app.cs__class
|
142
|
+
clazz < ::Rails::Engine
|
140
143
|
end
|
141
144
|
|
142
145
|
# Recursively get final route traversing engines as required. Because this can only be called once, we store
|
@@ -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
|
data/lib/contrast/logger/log.rb
CHANGED
@@ -134,7 +134,8 @@ module Contrast
|
|
134
134
|
|
135
135
|
enable_trace_timing if current_level_const == ::Ougai::Logging::TRACE
|
136
136
|
|
137
|
-
|
137
|
+
progname = Contrast::CONFIG.root.agent.logger.progname
|
138
|
+
@_logger = build(path: current_path, level_const: current_level_const, progname: progname)
|
138
139
|
# If we're logging to a new path, then let's start it w/ our helpful
|
139
140
|
# data gathering messages
|
140
141
|
log_update if path_change
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/components/logger'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Utils
|
8
|
+
module Assess
|
9
|
+
# EventLimitUtils is used to check and validate the number of source, propagation, or trigger events collected
|
10
|
+
# during the reporting time frame
|
11
|
+
module EventLimitUtils
|
12
|
+
include Contrast::Components::Logger::InstanceMethods
|
13
|
+
# Checks to see if the event limit for the policy type has been met or exceeded
|
14
|
+
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] method to check for event limit
|
15
|
+
def event_limit? method_policy
|
16
|
+
return false unless (context = Contrast::Agent::REQUEST_TRACKER.current)
|
17
|
+
|
18
|
+
if method_policy.source_node
|
19
|
+
max = (::Contrast::ASSESS.max_source_events ||
|
20
|
+
Contrast::Config::AssessConfiguration::DEFAULT_MAX_SOURCE_EVENTS)
|
21
|
+
return at_limit?(method_policy, context.source_event_count, max)
|
22
|
+
|
23
|
+
end
|
24
|
+
if method_policy.propagation_node
|
25
|
+
max = (::Contrast::ASSESS.max_propagation_events ||
|
26
|
+
Contrast::Config::AssessConfiguration::DEFAULT_MAX_PROPAGATION_EVENTS)
|
27
|
+
return at_limit?(method_policy, context.propagation_event_count, max)
|
28
|
+
end
|
29
|
+
|
30
|
+
false # policy does not have limit
|
31
|
+
end
|
32
|
+
|
33
|
+
def event_limit_for_rule? rule_id
|
34
|
+
if Contrast::Utils::Timer.now_ms > threshold_time_limit
|
35
|
+
@_rule_counts = nil
|
36
|
+
@_threshold_time_limit = nil
|
37
|
+
threshold_time_limit
|
38
|
+
end
|
39
|
+
rule_counts[rule_id] += 1
|
40
|
+
# TODO: RUBY-1680 remove default
|
41
|
+
rule_counts[rule_id] >=
|
42
|
+
(::Contrast::ASSESS.max_rule_reported || Contrast::Config::AssessConfiguration::DEFAULT_MAX_RULE_REPORTED)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Increments the event count for the type of event that is being tracked
|
46
|
+
#
|
47
|
+
# @param node [Contrast::Agent::Assess::Policy::PolicyNode] policy to increment
|
48
|
+
def increment_event_count node
|
49
|
+
return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
|
50
|
+
|
51
|
+
context.source_event_count += 1 if node.cs__is_a?(Contrast::Agent::Assess::Policy::SourceNode)
|
52
|
+
context.propagation_event_count += 1 if node.cs__is_a?(Contrast::Agent::Assess::Policy::PropagationNode)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# helper method to check limit and log when necessary
|
58
|
+
def at_limit? method_policy, current_count, event_max
|
59
|
+
if current_count == event_max
|
60
|
+
logger.warn('Event Limit Reached:',
|
61
|
+
{
|
62
|
+
count: current_count,
|
63
|
+
max: event_max,
|
64
|
+
policy: method_policy.method_name,
|
65
|
+
node: method_policy
|
66
|
+
})
|
67
|
+
# increment to be over count for logging purposes
|
68
|
+
increment_event_count(method_policy)
|
69
|
+
return true
|
70
|
+
elsif current_count > event_max
|
71
|
+
# increment to be over count for logging purposes
|
72
|
+
increment_event_count(method_policy)
|
73
|
+
logger.warn('Event Limit Exceeded:',
|
74
|
+
{
|
75
|
+
count: current_count,
|
76
|
+
policy: method_policy.method_name,
|
77
|
+
node: method_policy
|
78
|
+
})
|
79
|
+
return true
|
80
|
+
end
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
def rule_counts
|
85
|
+
@_rule_counts ||= Hash.new { |h, k| h[k] = 0 }
|
86
|
+
end
|
87
|
+
|
88
|
+
# the time threshold for which to track rule counts resets when now >= threshold_time_limit
|
89
|
+
# @return [Integer]
|
90
|
+
def threshold_time_limit
|
91
|
+
@_threshold_time_limit ||= Contrast::Utils::Timer.now_ms + (::Contrast::ASSESS.time_limit_threshold || 0)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -92,20 +92,24 @@ module Contrast
|
|
92
92
|
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
93
93
|
# the invocation of the patched method.
|
94
94
|
# @param target [Object] the thing to which to propagate
|
95
|
+
# @param propagation_data [Contrast::Agent::Assess::Events::EventData] this will hold the
|
96
|
+
# object [Object] the Object on which the method was invoked
|
97
|
+
# args [Array<Object>] the Arguments with which the method was invoked
|
95
98
|
# @return [Boolean]
|
96
|
-
def can_propagate? propagation_node, preshift, target
|
99
|
+
def can_propagate? propagation_node, preshift, target, propagation_data
|
97
100
|
return false unless appropriate_target?(propagation_node, target)
|
98
101
|
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
|
99
|
-
|
100
|
-
# return true since we don't have preshift while using the original object.
|
101
|
-
return true
|
102
|
-
end
|
103
|
-
return false unless preshift
|
102
|
+
return false unless appropriate_source?(propagation_node, propagation_data, preshift)
|
104
103
|
|
105
104
|
propagation_node.sources.each do |source|
|
106
105
|
case source
|
107
106
|
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
108
|
-
|
107
|
+
source_object = if propagation_node.use_original_object?
|
108
|
+
propagation_data.object
|
109
|
+
else
|
110
|
+
preshift.object
|
111
|
+
end
|
112
|
+
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(source_object)
|
109
113
|
else
|
110
114
|
# has to be P, there's no ret source type (yet? ever?)
|
111
115
|
return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
|
@@ -129,6 +133,22 @@ module Contrast
|
|
129
133
|
|
130
134
|
Contrast::Agent::Assess::Tracker.trackable?(target)
|
131
135
|
end
|
136
|
+
|
137
|
+
# A source is appropriate if it is available for propagation
|
138
|
+
#
|
139
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
140
|
+
# propagation event.
|
141
|
+
# @param propagation_data [Contrast::Agent::Assess::Events::EventData] this will hold the
|
142
|
+
# object [Object] the Object on which the method was invoked
|
143
|
+
# args [Array<Object>] the Arguments with which the method was invoked
|
144
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
145
|
+
# the invocation of the patched method.
|
146
|
+
# @return [Boolean]
|
147
|
+
def appropriate_source? propagation_node, propagation_data, preshift
|
148
|
+
return true if preshift
|
149
|
+
|
150
|
+
propagation_node.use_original_object? && propagation_data&.object
|
151
|
+
end
|
132
152
|
end
|
133
153
|
end
|
134
154
|
end
|
@@ -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
|
@@ -19,7 +19,7 @@ module Contrast
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
def build path: STDOUT_STR, level_const: DEFAULT_LEVEL
|
22
|
+
def build path: STDOUT_STR, level_const: DEFAULT_LEVEL, progname: PROGNAME
|
23
23
|
logger = case path
|
24
24
|
when STDOUT_STR, STDERR_STR
|
25
25
|
::Ougai::Logger.new(Object.cs__const_get(path))
|
@@ -27,7 +27,7 @@ module Contrast
|
|
27
27
|
::Ougai::Logger.new(path)
|
28
28
|
end
|
29
29
|
add_contrast_loggers(logger)
|
30
|
-
logger.progname =
|
30
|
+
logger.progname = progname
|
31
31
|
logger.level = level_const
|
32
32
|
logger.formatter = Contrast::Logger::Format.new
|
33
33
|
logger.formatter.datetime_format = DATE_TIME_FORMAT
|
@@ -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)
|
@@ -7,18 +7,6 @@ module Contrast
|
|
7
7
|
# This module will include all methods for different patch applies from Patch module and some other module
|
8
8
|
# methods from the same place, so we can ease the main module
|
9
9
|
module PatchUtils
|
10
|
-
# Method to choose which replaced return from the post_patch to actually return.
|
11
|
-
#
|
12
|
-
# @param propagated_ret [Object, nil] The replaced return from the propagation patch.
|
13
|
-
# @param source_ret [Object, nil] The replaced return from the source patch.
|
14
|
-
# @param ret [Object, nil] The original return of the patched method.
|
15
|
-
# @return [Object, nil] The thing to return from the post patch.
|
16
|
-
def handle_return propagated_ret, source_ret, ret
|
17
|
-
safe_return = propagated_ret || source_ret || ret
|
18
|
-
safe_return.rewind if Contrast::Utils::IOUtil.should_rewind?(safe_return)
|
19
|
-
safe_return
|
20
|
-
end
|
21
|
-
|
22
10
|
# Given a module and method, construct an expected name for the alias by which Contrast will reference the
|
23
11
|
# original.
|
24
12
|
#
|
@@ -107,7 +95,7 @@ module Contrast
|
|
107
95
|
# @param object [Object] The object on which the method is invoked, typically what would be returned by self.
|
108
96
|
# @param args [Array<Object>] The arguments passed to the method being invoked.
|
109
97
|
def apply_inventory method_policy, method, exception, object, args
|
110
|
-
return unless ::Contrast::INVENTORY.
|
98
|
+
return unless ::Contrast::INVENTORY.enable
|
111
99
|
|
112
100
|
apply_trigger_only(method_policy&.inventory_node, method, exception, object, args)
|
113
101
|
end
|
@@ -123,8 +111,6 @@ module Contrast
|
|
123
111
|
# @param args [Array<Object>] The arguments passed to the method being invoked.
|
124
112
|
# @param block [Proc] The block passed to the method that was invoked.
|
125
113
|
def apply_assess method_policy, preshift, object, ret, args, block
|
126
|
-
source_ret = nil
|
127
|
-
propagated_ret = nil
|
128
114
|
return ret unless method_policy && ::Contrast::ASSESS.enabled?
|
129
115
|
|
130
116
|
current_context = Contrast::Agent::REQUEST_TRACKER.current
|
@@ -135,27 +121,24 @@ module Contrast
|
|
135
121
|
Contrast::Agent::Assess::Policy::TriggerMethod.apply_trigger_rule(trigger_node, object, ret, args)
|
136
122
|
end
|
137
123
|
if method_policy.source_node
|
138
|
-
|
139
|
-
# we'll need to replace the return. Note, this is not the default case.
|
140
|
-
source_ret = Contrast::Agent::Assess::Policy::SourceMethod.apply_source(method_policy, object, ret, args)
|
124
|
+
Contrast::Agent::Assess::Policy::SourceMethod.apply_source(method_policy, object, ret, args)
|
141
125
|
end
|
142
126
|
if method_policy.propagation_node
|
143
|
-
|
127
|
+
Contrast::Agent::Assess::Policy::PropagationMethod.apply_propagation(
|
144
128
|
method_policy,
|
145
129
|
preshift,
|
146
130
|
object,
|
147
|
-
|
131
|
+
ret,
|
148
132
|
args,
|
149
133
|
block)
|
150
134
|
end
|
151
|
-
handle_return(propagated_ret, source_ret, ret)
|
152
135
|
rescue StandardError => e
|
153
136
|
logger.error('Unable to assess method call.', e)
|
154
|
-
handle_return(propagated_ret, source_ret, ret)
|
155
137
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
156
138
|
logger.error('Unable to assess method call.', e)
|
157
|
-
handle_return(propagated_ret, source_ret, ret)
|
158
139
|
raise(e)
|
140
|
+
ensure
|
141
|
+
ret.rewind if Contrast::Utils::IOUtil.should_rewind?(ret)
|
159
142
|
end
|
160
143
|
|
161
144
|
# Generic invocation of the Inventory or Protect patch which apply to the given method.
|
@@ -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
|