contrast-agent 6.1.0 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +1 -1
- data/Rakefile +1 -1
- data/ext/build_funchook.rb +3 -3
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +5 -1
- data/ext/extconf_common.rb +1 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +2 -2
- data/lib/contrast/agent/assess/policy/policy.rb +9 -10
- data/lib/contrast/agent/assess/policy/policy_node.rb +9 -10
- data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -3
- data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -3
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/buffer.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/split.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +1 -1
- data/lib/contrast/agent/assess/policy/source_node.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +7 -7
- data/lib/contrast/agent/assess/policy/trigger_node.rb +16 -16
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +2 -2
- data/lib/contrast/agent/assess/property/tagged.rb +2 -2
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +6 -8
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +6 -7
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +5 -5
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +2 -3
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +8 -9
- data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +6 -6
- data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +3 -4
- data/lib/contrast/agent/assess/tag.rb +13 -14
- data/lib/contrast/agent/at_exit_hook.rb +13 -1
- data/lib/contrast/agent/inventory/database_config.rb +12 -4
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -5
- data/lib/contrast/agent/middleware.rb +6 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +2 -2
- data/lib/contrast/agent/patching/policy/method_policy_extend.rb +4 -4
- data/lib/contrast/agent/patching/policy/patch.rb +9 -9
- data/lib/contrast/agent/patching/policy/patch_status.rb +10 -3
- data/lib/contrast/agent/patching/policy/policy.rb +13 -15
- data/lib/contrast/agent/patching/policy/policy_node.rb +19 -21
- data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +125 -125
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +4 -4
- data/lib/contrast/agent/protect/rule/base.rb +30 -18
- data/lib/contrast/agent/protect/rule/base_service.rb +31 -14
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +16 -9
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +3 -3
- data/lib/contrast/agent/protect/rule/default_scanner.rb +2 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +18 -7
- data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +74 -74
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +71 -53
- data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +3 -3
- data/lib/contrast/agent/protect/rule/no_sqli.rb +15 -16
- data/lib/contrast/agent/protect/rule/path_traversal.rb +13 -3
- data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +2 -2
- data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +1 -1
- data/lib/contrast/agent/protect/rule/sqli.rb +16 -23
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +61 -61
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +29 -29
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +32 -32
- data/lib/contrast/agent/protect/rule/xss.rb +21 -0
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +14 -13
- data/lib/contrast/agent/protect/rule/xxe.rb +25 -3
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +36 -36
- data/lib/contrast/agent/reporting/masker/masker.rb +10 -10
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +2 -2
- data/lib/contrast/agent/reporting/reporter.rb +2 -11
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +8 -10
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +53 -5
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +25 -19
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +129 -17
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +20 -21
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_stack.rb +22 -0
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +26 -12
- data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +7 -22
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +7 -5
- data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +4 -10
- data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +75 -15
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -19
- data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -22
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -3
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +1 -3
- data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +9 -0
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -10
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +6 -7
- data/lib/contrast/agent/reporting/reporting_utilities/response.rb +61 -3
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +33 -11
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +8 -8
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +64 -32
- data/lib/contrast/agent/reporting/settings/application_settings.rb +8 -23
- data/lib/contrast/agent/reporting/settings/assess.rb +5 -5
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +28 -34
- data/lib/contrast/agent/reporting/settings/bot_blocker.rb +68 -0
- data/lib/contrast/agent/reporting/settings/code_exclusion.rb +27 -0
- data/lib/contrast/agent/reporting/settings/exclusion_base.rb +33 -0
- data/lib/contrast/agent/reporting/settings/exclusions.rb +39 -57
- data/lib/contrast/agent/reporting/settings/helpers.rb +56 -0
- data/lib/contrast/agent/reporting/settings/input_exclusion.rb +37 -0
- data/lib/contrast/agent/reporting/settings/ip_filter.rb +35 -0
- data/lib/contrast/agent/reporting/settings/keyword.rb +74 -0
- data/lib/contrast/agent/reporting/settings/log_enhancer.rb +65 -0
- data/lib/contrast/agent/reporting/settings/protect.rb +22 -5
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +62 -115
- data/lib/contrast/agent/reporting/settings/reaction.rb +13 -4
- data/lib/contrast/agent/reporting/settings/rule_definition.rb +63 -0
- data/lib/contrast/agent/reporting/settings/sampling.rb +10 -0
- data/lib/contrast/agent/reporting/settings/sanitizer.rb +38 -0
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +11 -3
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +9 -2
- data/lib/contrast/agent/reporting/settings/server_features.rb +10 -2
- data/lib/contrast/agent/reporting/settings/syslog.rb +176 -0
- data/lib/contrast/agent/reporting/settings/url_exclusion.rb +42 -0
- data/lib/contrast/agent/reporting/settings/validator.rb +17 -0
- data/lib/contrast/agent/request.rb +2 -2
- data/lib/contrast/agent/request_context.rb +27 -19
- data/lib/contrast/agent/request_context_extend.rb +10 -23
- data/lib/contrast/agent/request_handler.rb +9 -5
- data/lib/contrast/agent/rule_set.rb +2 -2
- data/lib/contrast/agent/scope.rb +1 -1
- data/lib/contrast/agent/static_analysis.rb +4 -8
- data/lib/contrast/agent/telemetry/base.rb +9 -5
- data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +119 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -2
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +1 -1
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +1 -1
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +16 -18
- data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +2 -2
- data/lib/contrast/agent/thread_watcher.rb +4 -5
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +1 -3
- data/lib/contrast/api/communication/messaging_queue.rb +1 -1
- data/lib/contrast/api/communication/service_lifecycle.rb +1 -1
- data/lib/contrast/api/communication/socket.rb +1 -1
- data/lib/contrast/api/communication/socket_client.rb +1 -1
- data/lib/contrast/api/communication/speedracer.rb +2 -2
- data/lib/contrast/api/decorators/agent_startup.rb +10 -9
- data/lib/contrast/api/decorators/application_settings.rb +1 -1
- data/lib/contrast/api/decorators/application_startup.rb +4 -4
- data/lib/contrast/api/decorators/application_update.rb +0 -8
- data/lib/contrast/api/decorators/response_type.rb +4 -17
- data/lib/contrast/api/decorators.rb +0 -1
- data/lib/contrast/components/agent.rb +1 -1
- data/lib/contrast/components/base.rb +1 -1
- data/lib/contrast/components/config.rb +6 -6
- data/lib/contrast/components/contrast_service.rb +4 -1
- data/lib/contrast/components/sampling.rb +1 -1
- data/lib/contrast/components/settings.rb +52 -28
- data/lib/contrast/config/assess_rules_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +1 -1
- data/lib/contrast/config/root_configuration.rb +1 -1
- data/lib/contrast/configuration.rb +4 -4
- data/lib/contrast/extension/assess/array.rb +1 -1
- data/lib/contrast/extension/assess/erb.rb +1 -1
- data/lib/contrast/extension/assess/marshal.rb +1 -1
- data/lib/contrast/extension/assess/string.rb +1 -1
- data/lib/contrast/extension/extension.rb +2 -2
- data/lib/contrast/framework/base_support.rb +13 -12
- data/lib/contrast/framework/grape/support.rb +9 -9
- data/lib/contrast/framework/manager.rb +7 -9
- data/lib/contrast/framework/manager_extend.rb +2 -1
- data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +15 -3
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +3 -3
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
- data/lib/contrast/framework/rails/patch/support.rb +1 -1
- data/lib/contrast/framework/rails/support.rb +11 -4
- data/lib/contrast/framework/sinatra/support.rb +4 -3
- data/lib/contrast/logger/aliased_logging.rb +27 -15
- data/lib/contrast/logger/cef_log.rb +14 -14
- data/lib/contrast/logger/format.rb +1 -1
- data/lib/contrast/logger/log.rb +8 -8
- data/lib/contrast/tasks/config.rb +12 -12
- data/lib/contrast/tasks/service.rb +2 -2
- data/lib/contrast/utils/assess/tracking_util.rb +4 -4
- data/lib/contrast/utils/class_util.rb +4 -4
- data/lib/contrast/utils/findings.rb +3 -3
- data/lib/contrast/utils/hash_digest.rb +6 -7
- data/lib/contrast/utils/head_dump_utils_extend.rb +1 -1
- data/lib/contrast/utils/invalid_configuration_util.rb +1 -1
- data/lib/contrast/utils/log_utils.rb +4 -4
- data/lib/contrast/utils/lru_cache.rb +1 -1
- data/lib/contrast/utils/metrics_hash.rb +1 -1
- data/lib/contrast/utils/middleware_utils.rb +5 -5
- data/lib/contrast/utils/net_http_base.rb +4 -4
- data/lib/contrast/utils/os.rb +1 -1
- data/lib/contrast/utils/patching/policy/patch_utils.rb +2 -2
- data/lib/contrast/utils/request_utils.rb +2 -2
- data/lib/contrast/utils/response_utils.rb +14 -1
- data/lib/contrast/utils/sha256_builder.rb +4 -4
- data/lib/contrast/utils/stack_trace_utils.rb +31 -13
- data/lib/contrast/utils/telemetry.rb +15 -9
- data/lib/contrast/utils/telemetry_client.rb +5 -5
- data/lib/contrast/utils/telemetry_hash.rb +37 -13
- data/lib/contrast/utils/telemetry_identifier.rb +10 -2
- data/lib/contrast/utils/thread_tracker.rb +26 -9
- data/lib/contrast/utils/timer.rb +7 -2
- data/lib/contrast.rb +1 -3
- data/resources/assess/policy.json +1 -1
- metadata +28 -14
- data/lib/contrast/api/decorators/library_usage_update.rb +0 -31
@@ -34,7 +34,6 @@ module Contrast
|
|
34
34
|
def send_activity_messages
|
35
35
|
events = [context.activity]
|
36
36
|
unless Contrast::Agent::Reporter.enabled?
|
37
|
-
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage(context.activity)
|
38
37
|
events << context.server_activity
|
39
38
|
events << context.observed_route
|
40
39
|
end
|
@@ -48,26 +47,31 @@ module Contrast
|
|
48
47
|
# more endpoints over, this method will take the messages originally sent by #send_actiivty_messages. At the end,
|
49
48
|
# that method should be removed.
|
50
49
|
def report_activity # rubocop:disable Metrics/AbcSize
|
50
|
+
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.
|
51
|
+
generate_library_usage(context.observed_library_usage)
|
52
|
+
|
53
|
+
Contrast::Agent.reporter.send_event(context.observed_library_usage)
|
51
54
|
return unless Contrast::Agent::Reporter.enabled?
|
52
55
|
|
53
56
|
reporter = Contrast::Agent.reporter
|
54
57
|
return unless reporter
|
55
58
|
|
56
59
|
# Mask Sensitive Data
|
57
|
-
Contrast::Agent::Reporting::Masker.mask
|
60
|
+
Contrast::Agent::Reporting::Masker.mask(context.activity)
|
58
61
|
|
59
|
-
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.
|
62
|
+
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.
|
63
|
+
generate_library_usage(context.observed_library_usage)
|
60
64
|
[
|
61
65
|
context.new_observed_route,
|
66
|
+
context.observed_library_usage,
|
62
67
|
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
|
63
|
-
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity.library_usages),
|
64
68
|
Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
|
65
69
|
].each do |event|
|
66
70
|
reporter.send_event(event)
|
67
71
|
rescue StandardError => e
|
68
72
|
logger.warn('Unable to send Event Activity', e)
|
69
73
|
end
|
70
|
-
context.
|
74
|
+
context.observed_library_usage.clear # TODO: RUBY-1355 remove when no longer using activity
|
71
75
|
end
|
72
76
|
|
73
77
|
# If the response is streaming, we should only perform filtering on our stream safe rules
|
@@ -23,7 +23,7 @@ module Contrast
|
|
23
23
|
end
|
24
24
|
rescue Contrast::SecurityException => e
|
25
25
|
logger.warn('RASP threw security exception in prefilter', e)
|
26
|
-
raise
|
26
|
+
raise(e)
|
27
27
|
rescue StandardError => e
|
28
28
|
logger.error('Unexpected exception during prefilter', e)
|
29
29
|
end
|
@@ -39,7 +39,7 @@ module Contrast
|
|
39
39
|
end
|
40
40
|
rescue Contrast::SecurityException => e
|
41
41
|
logger.warn('RASP threw security exception in postfilter', e)
|
42
|
-
raise
|
42
|
+
raise(e)
|
43
43
|
rescue StandardError => e
|
44
44
|
logger.error('Unexpected exception during postfilter', e)
|
45
45
|
end
|
data/lib/contrast/agent/scope.rb
CHANGED
@@ -142,7 +142,7 @@ module Contrast
|
|
142
142
|
# @param scope_sym [Symbol] scope to check.
|
143
143
|
# @return [Boolean] true | false
|
144
144
|
def valid_scope? scope_sym
|
145
|
-
Contrast::Agent::Scope::SCOPE_LIST.include?
|
145
|
+
Contrast::Agent::Scope::SCOPE_LIST.include?(scope_sym)
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -24,24 +24,20 @@ module Contrast
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
# TODO: RUBY-
|
27
|
+
# TODO: RUBY-1703
|
28
28
|
def send_inventory_message
|
29
29
|
return unless ::Contrast::INVENTORY.enabled?
|
30
30
|
|
31
31
|
app_update_msg = Contrast::Api::Dtm::ApplicationUpdate.build
|
32
|
-
|
33
32
|
Contrast::Agent::Inventory::DatabaseConfig.append_db_config(app_update_msg)
|
34
|
-
# TODO: RUBY-
|
35
|
-
if Contrast::Agent.
|
33
|
+
# TODO: RUBY-1703 -- remove and build ReportingEvent directly
|
34
|
+
if Contrast::Agent::Reporter.enabled?
|
36
35
|
report = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(app_update_msg)
|
37
36
|
Contrast::Agent.reporter.send_event(report)
|
38
|
-
|
39
|
-
# This is being reported separately because we extract the data from the same message
|
40
|
-
inventory_report = Contrast::Agent::Reporting::ApplicationInventory.convert(app_update_msg)
|
41
|
-
Contrast::Agent.reporter.send_event(inventory_report)
|
42
37
|
else
|
43
38
|
Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg, force: true)
|
44
39
|
end
|
40
|
+
Contrast::Agent.reporter.send_event(Contrast::Agent::Reporting::ApplicationInventory.new)
|
45
41
|
end
|
46
42
|
|
47
43
|
private
|
@@ -7,6 +7,7 @@ require 'contrast/utils/telemetry_client'
|
|
7
7
|
require 'contrast/agent/worker_thread'
|
8
8
|
require 'contrast/utils/telemetry'
|
9
9
|
require 'contrast/agent/telemetry/events/exceptions/telemetry_exceptions'
|
10
|
+
require 'contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report'
|
10
11
|
|
11
12
|
module Contrast
|
12
13
|
module Agent
|
@@ -14,6 +15,8 @@ module Contrast
|
|
14
15
|
# This class will initialize and hold everything needed for the telemetry
|
15
16
|
class Base < WorkerThread
|
16
17
|
include Contrast::Components::Logger::InstanceMethods
|
18
|
+
include Contrast::Agent::Telemetry::TelemetryExceptionReport
|
19
|
+
|
17
20
|
# this is where we will send the data from the agents
|
18
21
|
URL = 'https://telemetry.ruby.contrastsecurity.com/'
|
19
22
|
# Suggested timeout after each send is to be 3 hours (10800 seconds)
|
@@ -87,16 +90,17 @@ module Contrast
|
|
87
90
|
loop do
|
88
91
|
next unless client && connection
|
89
92
|
|
90
|
-
|
93
|
+
# Start pushing exceptions to queue for reporting.
|
94
|
+
push_exceptions
|
91
95
|
until queue.empty?
|
92
96
|
event = queue.pop
|
93
97
|
begin
|
94
98
|
logger.debug('This is the current processed event', event)
|
95
|
-
sleep_time = request_with_response
|
99
|
+
sleep_time = request_with_response(event)
|
96
100
|
if sleep_time
|
97
101
|
sleep(sleep_time)
|
98
102
|
logger.debug('Retrying to process event', event)
|
99
|
-
retry_sleep_time = request_with_response
|
103
|
+
retry_sleep_time = request_with_response(event)
|
100
104
|
sleep(retry_sleep_time) unless retry_sleep_time.nil?
|
101
105
|
end
|
102
106
|
rescue StandardError => e
|
@@ -136,8 +140,8 @@ module Contrast
|
|
136
140
|
end
|
137
141
|
|
138
142
|
def request_with_response event
|
139
|
-
res = client.send_request
|
140
|
-
client.handle_response
|
143
|
+
res = client.send_request(event, connection)
|
144
|
+
client.handle_response(res)
|
141
145
|
end
|
142
146
|
|
143
147
|
private
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Contrast
|
5
|
+
module Agent
|
6
|
+
module Telemetry
|
7
|
+
module TelemetryException
|
8
|
+
# Here will be all known gems to obfuscate during the building of TelemetryExceptions
|
9
|
+
module Obfuscate
|
10
|
+
# List of known gems to be third parties not containing any
|
11
|
+
# user sensitive data.
|
12
|
+
KNOWN_GEMS = %w[
|
13
|
+
activesupport redis-activesupport redis builder dry-types rails rails-html-sanitizer rails-observers sinatra
|
14
|
+
benchmark-ips bundler climate_control execjs factory_bot debride fake_ftp fasterer flay openssl
|
15
|
+
parallel_tests contrast contrast-agent ruby pry-byebug byebug pry resolv ougai protobuf async nokogiri
|
16
|
+
benchmark grape-order grape-activerecord newrelic newrelic-grape grape-rails-cache grape-msgpack
|
17
|
+
grape-batch rspec rake rubocop grape-jbuilder rack-test rack-protection minitest grape-instrumentation
|
18
|
+
minitest-global_expectations rack-proxy rack-attack rack-ssl grape-cancan grape-attack grape-rake-tasks
|
19
|
+
grape-route-helpers benchmark-memory grape-rabl grape-kaminari grape-path-helpers grape-swagger-entity
|
20
|
+
grape-swagger-rails grape-roar newrelic-rake grapes-wagger-representative grape-forgery_protection
|
21
|
+
grape-papertrail grape-app grape-middleware-logger rack-protection rake-compile rhino rspec-benchmark
|
22
|
+
rspec_junit_formatter rspec-rails rake-performance rubocop-rails rubocop-rake rubocop-rspec
|
23
|
+
ruby-debug-ide simplecov sqlite steep tilt tzinfo-data warning xpath sinatra-activerecord sinatra-param
|
24
|
+
sinatra-partial sinatra-flash sinatra-reloader sinatra-sinatra rake_fly rack rack-accept grape grape-entity
|
25
|
+
sinatra-advanced-routes sinatra-warden grape_logging grape-swagger sinatra-namespace sinatra-resource
|
26
|
+
sinatra-hashfix sinatra-instrumentation sinatra-helpers redis-rack sinatra-support sinatra-assetpack
|
27
|
+
sinatra-router sinatra-cross_origin sinatra_more sinatra-jsonp faraday-rack zlib mustermann mustermann-grape
|
28
|
+
rspec-expectations rspec-core rspec-mocks rspec-sidekiq
|
29
|
+
].cs__freeze
|
30
|
+
KNOWN_ERRORS = %w[
|
31
|
+
ActiveModel Error Errors Resolv Rspec ActiveRecord nil NilClass NoMemoryError ScriptError
|
32
|
+
LoadError NotImplementedError SyntaxError SecurityError SignalException Interrupt
|
33
|
+
StandardError ArgumentError UncaughtThrowError FiberError IOError EOFError IndexError KeyError
|
34
|
+
StopIteration LocalJumpError NameError NoMethodError RangeError FloatDomainError RegexpError
|
35
|
+
RuntimeError FrozenError SystemCallError ThreadError TypeError ZeroDivisionError SystemExit
|
36
|
+
SystemStackError fatal Warning buffer OpenSSL SSL SSLError Errno Timeout Exception EOFError
|
37
|
+
ECONNRESET ECONNREFUSED ETIMEDOUT EINVAL ESHUTDOWN EHOSTDOWN EHOSTUNREACH EISCONN
|
38
|
+
ECONNABORTED ENETRESET ENETUNREACH Net HTTPBadResponse HTTPHeaderSyntaxError ProtocolError
|
39
|
+
Faraday BadRequestError UnauthorizedError ForbiddenError ResourceNotFound ProxyAuthError
|
40
|
+
ConflictError UnprocessableEntityError ClientError
|
41
|
+
].cs__freeze
|
42
|
+
CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.cs__freeze
|
43
|
+
# This will generate different chars for each agent startup but will be constant
|
44
|
+
# for current run and will make identical obfuscation to be compared if given type
|
45
|
+
# is the same.
|
46
|
+
CYPHER = CHARS.chars.shuffle.join
|
47
|
+
|
48
|
+
class << self
|
49
|
+
# Returns paths for known gems.
|
50
|
+
#
|
51
|
+
# @return [Array<Regexp>] known paths
|
52
|
+
def known_gem_paths
|
53
|
+
@_known_gem_paths ||= KNOWN_GEMS.map do |gem_name|
|
54
|
+
to_regexp(File.join(
|
55
|
+
'gems', gem_name.tr('-', ''), 'lib'))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns known modules names.
|
60
|
+
#
|
61
|
+
# @return [Array<Regexp>] known modules
|
62
|
+
def known_modules
|
63
|
+
@_known_modules ||= KNOWN_ERRORS.map { |module_name| to_regexp(module_name) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Obfuscate a type and replace it with random characters.
|
67
|
+
#
|
68
|
+
# @param type [String] the StackFrame type to obfuscate
|
69
|
+
# @return [String] obfuscated type
|
70
|
+
def obfuscate_type type
|
71
|
+
return unless type
|
72
|
+
|
73
|
+
check = type.tr('[^0-9].-', '')
|
74
|
+
type = cypher(type) unless match_known(known_gem_paths, check)
|
75
|
+
type
|
76
|
+
end
|
77
|
+
|
78
|
+
# Obfuscate a type and replace it with random characters.
|
79
|
+
#
|
80
|
+
# @param exception_type [String] the exception type to obfuscate
|
81
|
+
# @return [String] obfuscated exception type
|
82
|
+
def obfuscate_exception_type exception_type
|
83
|
+
return unless exception_type
|
84
|
+
|
85
|
+
exceptions = exception_type.split('::').each do |exception|
|
86
|
+
cypher(exception) unless match_known(known_modules, exception)
|
87
|
+
end
|
88
|
+
exceptions.join('::')
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Transforms string to regexp
|
94
|
+
#
|
95
|
+
# @param string [String]
|
96
|
+
def to_regexp string
|
97
|
+
/#{ string }/
|
98
|
+
end
|
99
|
+
|
100
|
+
# Add cypher to path to make it obscure, but unique enough for
|
101
|
+
# comparisons. Mutates original or duplicate if frozen string.
|
102
|
+
#
|
103
|
+
# @param string [String] string to be transformed.
|
104
|
+
def cypher string
|
105
|
+
string = string.dup if string.cs__frozen?
|
106
|
+
string.to_s.tr!(CHARS, CYPHER)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param known [Array<Regexp>] Array of regexp to match againts
|
110
|
+
# @param type [String] type to check
|
111
|
+
def match_known known, type
|
112
|
+
known.any? { |regexp| type =~ regexp }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -37,7 +37,7 @@ module Contrast
|
|
37
37
|
value_to_validate.entries.length
|
38
38
|
end
|
39
39
|
unless validation_pair[:range].include?(value_length)
|
40
|
-
raise
|
40
|
+
raise(ArgumentError, "The provided value for #{ key } is invalid: #{ value_to_validate }")
|
41
41
|
end
|
42
42
|
|
43
43
|
nil
|
@@ -50,7 +50,7 @@ module Contrast
|
|
50
50
|
# @param field [Object] The field with the error
|
51
51
|
def validate_class message, klass, field
|
52
52
|
message = message[0] if message.cs__is_a?(Array)
|
53
|
-
raise
|
53
|
+
raise(ArgumentError, "The provided value for #{ field } is of wrong class") unless message.cs__is_a?(klass)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -6,25 +6,23 @@ module Contrast
|
|
6
6
|
module Telemetry
|
7
7
|
# This module will handle the reporting of the TelemetryExceptionHash
|
8
8
|
module TelemetryExceptionReport
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
return unless Contrast::TELEMETRY_EXCEPTIONS&.any?
|
9
|
+
# Here we will send any exceptions gathered. The telemetry_hash is split into batches of 256
|
10
|
+
# and then added to the telemetry queue. Since this method is called before entering the
|
11
|
+
# until queue loop any updates after clearing the Contrast::TELEMETRY_EXCEPTIONS would have
|
12
|
+
# to wait for the sending process to be completed, so accumulating new batches.
|
13
|
+
# This methods expects queue and error_messages methods from Contrast::Agent::Telemetry::Base
|
14
|
+
def push_exceptions
|
15
|
+
return unless Contrast::TELEMETRY_EXCEPTIONS&.any?
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
17
|
+
Contrast::TELEMETRY_EXCEPTIONS.each_value { |value| error_messages.push(value) }
|
18
|
+
# Clear the hash. All exceptions now live in @_error_messages instance variable. and we will
|
19
|
+
# add them to the queue. Clearing would make the hash available to be populated again while the
|
20
|
+
# sending is proceeding.
|
21
|
+
Contrast::TELEMETRY_EXCEPTIONS.clear
|
22
|
+
# Add batch to queue. We need to shift here, because we want to report from the oldest batch to
|
23
|
+
# the newest. And even if somehow the array is filled during sending the new messages would stay
|
24
|
+
# and wait their turn.
|
25
|
+
queue << error_messages.shift until error_messages.empty?
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
@@ -50,7 +50,7 @@ module Contrast
|
|
50
50
|
def initialize
|
51
51
|
super
|
52
52
|
@settings = []
|
53
|
-
add_config_keys
|
53
|
+
add_config_keys(::Contrast::CONFIG.root, 'root')
|
54
54
|
@settings << ENV.keys.select { |v| v.starts_with?('CONTRAST') }
|
55
55
|
@settings.flatten
|
56
56
|
add_tags
|
@@ -78,7 +78,7 @@ module Contrast
|
|
78
78
|
next
|
79
79
|
end
|
80
80
|
|
81
|
-
add_config_keys
|
81
|
+
add_config_keys(v, "#{ nested_key }.#{ k }")
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -32,10 +32,8 @@ module Contrast
|
|
32
32
|
@heartbeat = Contrast::Agent::ServiceHeartbeat.new
|
33
33
|
@messaging_queue = Contrast::Api::Communication::MessagingQueue.new
|
34
34
|
end
|
35
|
-
|
36
|
-
|
37
|
-
@reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
|
38
|
-
end
|
35
|
+
@reporter = Contrast::Agent::Reporter.new
|
36
|
+
@reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new if Contrast::Agent::Reporter.enabled?
|
39
37
|
@telemetry = Contrast::Agent::Telemetry::Base.new if Contrast::Agent::Telemetry::Base.enabled?
|
40
38
|
end
|
41
39
|
|
@@ -51,9 +49,10 @@ module Contrast
|
|
51
49
|
telemetry_status = init_thread(telemetry_queue)
|
52
50
|
@pids[Process.pid] = @pids[Process.pid] && telemetry_status
|
53
51
|
end
|
52
|
+
reporter_status = init_thread(reporter)
|
53
|
+
|
54
54
|
return @pids unless Contrast::Agent::Reporter.enabled?
|
55
55
|
|
56
|
-
reporter_status = init_thread(reporter)
|
57
56
|
reporter_heartbeat_status = init_thread(reporter_heartbeat)
|
58
57
|
@pids[Process.pid] = @pids[Process.pid] && reporter_status && reporter_heartbeat_status
|
59
58
|
@pids
|
data/lib/contrast/agent.rb
CHANGED
@@ -58,7 +58,7 @@ module Contrast
|
|
58
58
|
# application to our post-filter operations as well as those that don't alter the application's execution (i.e.
|
59
59
|
# Assess' vulnerability reporting).
|
60
60
|
#
|
61
|
-
# @param event [Contrast::Api::Dtm] One of the DTMs valid for the event field of
|
61
|
+
# @param event [Contrast::Api::Dtm, Contrast::Api::Dtm::Poll] One of the DTMs valid for the event field of
|
62
62
|
# Contrast::Api::Dtm::Message|Contrast::Api::Dtm::Activity
|
63
63
|
# @param force [Boolean] if we should always queue this event, even if the service isn't running, in case the
|
64
64
|
# message may be ready before the service has initiated. usually for those events which happen in a separate
|
@@ -55,7 +55,7 @@ module Contrast
|
|
55
55
|
def spawn_service
|
56
56
|
options = determine_startup_options
|
57
57
|
logger.debug('Spawning service')
|
58
|
-
spawn
|
58
|
+
spawn('contrast_service', options)
|
59
59
|
end
|
60
60
|
|
61
61
|
# Create a thread to start the SpeedRacer, making calls to spawn until one starts successfully. As soon as the
|
@@ -35,7 +35,7 @@ module Contrast
|
|
35
35
|
|
36
36
|
# Override this method to return a socket. Should be interface compatible with TCPSocket, UNIXSocket, etc.
|
37
37
|
def new_socket
|
38
|
-
raise
|
38
|
+
raise(NoMethodError, 'This is abstract, override it.')
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -114,7 +114,7 @@ module Contrast
|
|
114
114
|
rescue StandardError => e
|
115
115
|
logger.error('Sending failed for message.', e, msg_id: msg.__id__, p_id: msg.pid,
|
116
116
|
msg_count: msg.message_count, response_id: response&.__id__)
|
117
|
-
raise
|
117
|
+
raise(e) # reraise to let SpeedRacer manage the connection
|
118
118
|
end
|
119
119
|
|
120
120
|
# Send the marshaled Contrast::Api::Dtm::Message across the socket used to talk to SpeedRacer
|
@@ -87,9 +87,9 @@ module Contrast
|
|
87
87
|
ensure_startup!
|
88
88
|
|
89
89
|
logger.debug_with_time(event.cs__class.cs__name) do
|
90
|
-
response = socket_client.send_one
|
90
|
+
response = socket_client.send_one(event)
|
91
91
|
status.success!
|
92
|
-
yield
|
92
|
+
yield(response)
|
93
93
|
end
|
94
94
|
rescue StandardError => e
|
95
95
|
status.failure!
|
@@ -27,11 +27,11 @@ module Contrast
|
|
27
27
|
def build name, path, type
|
28
28
|
msg = new
|
29
29
|
msg.server_version = Contrast::Agent::VERSION
|
30
|
-
msg.server_name = Contrast::Utils::StringUtils.protobuf_format
|
31
|
-
msg.server_path = Contrast::Utils::StringUtils.protobuf_format
|
32
|
-
msg.server_type = Contrast::Utils::StringUtils.protobuf_format
|
30
|
+
msg.server_name = Contrast::Utils::StringUtils.protobuf_format(name)
|
31
|
+
msg.server_path = Contrast::Utils::StringUtils.protobuf_format(path)
|
32
|
+
msg.server_type = Contrast::Utils::StringUtils.protobuf_format(type)
|
33
33
|
config!(msg)
|
34
|
-
msg.finding_tags = Contrast::Utils::StringUtils.protobuf_format
|
34
|
+
msg.finding_tags = Contrast::Utils::StringUtils.protobuf_format(::Contrast::ASSESS.tags)
|
35
35
|
msg
|
36
36
|
end
|
37
37
|
|
@@ -41,11 +41,12 @@ module Contrast
|
|
41
41
|
#
|
42
42
|
# @param msg [Contrast::Api::Dtm::AgentStartup]
|
43
43
|
def config! msg
|
44
|
-
msg.version = Contrast::Utils::StringUtils.protobuf_format
|
45
|
-
msg.server_tags = Contrast::Utils::StringUtils.protobuf_format
|
46
|
-
msg.library_tags = Contrast::Utils::StringUtils.protobuf_format
|
47
|
-
msg.environment = Contrast::Utils::StringUtils.protobuf_format
|
48
|
-
msg.application_tags =
|
44
|
+
msg.version = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.server.version)
|
45
|
+
msg.server_tags = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.server.tags)
|
46
|
+
msg.library_tags = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.inventory.tags)
|
47
|
+
msg.environment = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.server.environment)
|
48
|
+
msg.application_tags =
|
49
|
+
Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.application.tags)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
@@ -23,7 +23,7 @@ module Contrast
|
|
23
23
|
# Convert protobuf protect rule settings into a hash that settings state understands
|
24
24
|
def translated_protection_mode_by_id
|
25
25
|
protection_rules = self.protection_rules.map { |pr, _hash| [pr.id, pr.mode] } # [[RULE_ID, MODE]]
|
26
|
-
protection_rules.select! { |(_id, mode)| MODE_ALLOWLIST.include?
|
26
|
+
protection_rules.select! { |(_id, mode)| MODE_ALLOWLIST.include?(mode) } # [REMOVE IF MODE INVALID]
|
27
27
|
protection_rules.to_h # {RULE_ID => MODE}
|
28
28
|
end
|
29
29
|
|
@@ -24,12 +24,12 @@ module Contrast
|
|
24
24
|
# @return [Contrast::Api::Dtm::ApplicationCreate]
|
25
25
|
def build
|
26
26
|
msg = new
|
27
|
-
msg.code = Contrast::Utils::StringUtils.protobuf_format
|
28
|
-
msg.group = Contrast::Utils::StringUtils.protobuf_format
|
29
|
-
msg.metadata = Contrast::Utils::StringUtils.protobuf_format
|
27
|
+
msg.code = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.application.code)
|
28
|
+
msg.group = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.application.group)
|
29
|
+
msg.metadata = Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.application.metadata)
|
30
30
|
msg.mode = Contrast::Api::Dtm::InstrumentationMode.build
|
31
31
|
msg.app_version =
|
32
|
-
Contrast::Utils::StringUtils.protobuf_format
|
32
|
+
Contrast::Utils::StringUtils.protobuf_format(::Contrast::CONFIG.root.application.version.to_s) # rubocop:disable Layout/AssignmentIndentation Layout/FirstArgumentIndentation:
|
33
33
|
session!(msg)
|
34
34
|
msg
|
35
35
|
end
|
@@ -20,13 +20,6 @@ module Contrast
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
# TS only allows you to report 500 routes per application
|
24
|
-
def append_route_coverage_data route_coverage_dtms
|
25
|
-
route_coverage_dtms.take(500).each do |route_coverage_dtm|
|
26
|
-
routes << route_coverage_dtm
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
23
|
def append_platform_version platform_version
|
31
24
|
self.platform = Contrast::Api::Dtm::Platform.new if platform.nil?
|
32
25
|
platform.major = platform_version.major
|
@@ -38,7 +31,6 @@ module Contrast
|
|
38
31
|
module ClassMethods
|
39
32
|
def build
|
40
33
|
msg = new
|
41
|
-
msg.append_route_coverage_data(Contrast::Agent.framework_manager.find_route_discovery_data)
|
42
34
|
msg.append_platform_version(Contrast::Agent.framework_manager.platform_version)
|
43
35
|
msg.append_library_update(Contrast::Agent::Inventory::DependencyAnalysis.instance.library_pb_list)
|
44
36
|
msg
|
@@ -6,25 +6,12 @@ require 'protobuf/message'
|
|
6
6
|
|
7
7
|
module Contrast
|
8
8
|
module Api
|
9
|
-
module
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
::Protobuf::Enum.define(:SUSPICIOUS, 6)
|
14
|
-
|
15
|
-
def self.included klass
|
16
|
-
klass.extend(ClassMethods)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Used to add class methods to the class on inclusion of the decorator
|
20
|
-
module ClassMethods
|
21
|
-
def build
|
22
|
-
new
|
23
|
-
end
|
9
|
+
module Dtm
|
10
|
+
class AttackResult < ::Protobuf::Message
|
11
|
+
class ResponseType < ::Protobuf::Enum
|
12
|
+
define :SUSPICIOUS, 6
|
24
13
|
end
|
25
14
|
end
|
26
15
|
end
|
27
16
|
end
|
28
17
|
end
|
29
|
-
|
30
|
-
Contrast::Api::Dtm::AttackResult::ResponseType.include(Contrast::Api::Decorators::ResponseType)
|
@@ -18,7 +18,6 @@ require 'contrast/api/decorators/input_analysis'
|
|
18
18
|
require 'contrast/api/decorators/application_settings'
|
19
19
|
require 'contrast/api/decorators/server_features'
|
20
20
|
require 'contrast/api/decorators/library'
|
21
|
-
require 'contrast/api/decorators/library_usage_update'
|
22
21
|
require 'contrast/api/decorators/route_coverage'
|
23
22
|
require 'contrast/api/decorators/trace_event_object'
|
24
23
|
require 'contrast/api/decorators/trace_event_signature'
|
@@ -75,7 +75,7 @@ module Contrast
|
|
75
75
|
|
76
76
|
# Insert ourselves into the application, keeping our middleware at the outermost layer of the onion
|
77
77
|
def insert_middleware app
|
78
|
-
app.middleware.insert_before
|
78
|
+
app.middleware.insert_before(0, Contrast::Agent::Middleware)
|
79
79
|
end
|
80
80
|
|
81
81
|
def enable_tracepoint
|