contrast-agent 6.1.0 → 6.1.1
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 +12 -1
- 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 +17 -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/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 +5 -5
- 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 +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +6 -6
- data/lib/contrast/agent/reporting/reporting_utilities/response.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +7 -7
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +15 -15
- data/lib/contrast/agent/reporting/settings/application_settings.rb +1 -1
- data/lib/contrast/agent/reporting/settings/assess.rb +5 -5
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +3 -3
- data/lib/contrast/agent/reporting/settings/exclusions.rb +3 -3
- data/lib/contrast/agent/reporting/settings/protect.rb +20 -5
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +5 -5
- data/lib/contrast/agent/reporting/settings/reaction.rb +3 -3
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +2 -2
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +2 -2
- data/lib/contrast/agent/reporting/settings/server_features.rb +2 -2
- data/lib/contrast/agent/request.rb +2 -2
- data/lib/contrast/agent/request_context.rb +23 -19
- data/lib/contrast/agent/request_context_extend.rb +10 -23
- data/lib/contrast/agent/request_handler.rb +1 -1
- data/lib/contrast/agent/rule_set.rb +2 -2
- data/lib/contrast/agent/scope.rb +1 -1
- 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/version.rb +1 -1
- 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/response_type.rb +4 -17
- 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 +8 -8
- data/lib/contrast/framework/grape/support.rb +3 -3
- data/lib/contrast/framework/manager.rb +5 -5
- data/lib/contrast/framework/manager_extend.rb +1 -1
- data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -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 +2 -2
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/logger/aliased_logging.rb +29 -22
- 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/sha256_builder.rb +4 -4
- data/lib/contrast/utils/stack_trace_utils.rb +31 -13
- data/lib/contrast/utils/telemetry.rb +6 -9
- data/lib/contrast/utils/telemetry_client.rb +5 -5
- data/lib/contrast/utils/telemetry_hash.rb +1 -1
- data/lib/contrast/utils/telemetry_identifier.rb +2 -2
- data/lib/contrast/utils/timer.rb +1 -1
- data/resources/assess/policy.json +1 -1
- metadata +15 -13
@@ -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.values.each_slice(256) { |tuple| error_messages.push(tuple) }
|
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
|
|
@@ -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
|
@@ -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)
|
@@ -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
|
@@ -28,6 +28,12 @@ module Contrast
|
|
28
28
|
CONTRAST_NAME = 'Contrast Agent'
|
29
29
|
|
30
30
|
class Interface # :nodoc:
|
31
|
+
SESSION_VARIABLES = 'Invalid configuration. '\
|
32
|
+
"Setting both application.session_id and application.session_metadata is not allowed.\n"
|
33
|
+
API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
|
34
|
+
API_KEY = "Invalid configuration. Missing a required connection value 'api_key' is not set."
|
35
|
+
API_SERVICE_KEY = "Invalid configuration. Missing a required connection value 'service_tag' is not set."
|
36
|
+
API_USERNAME = "Invalid configuration. Missing a required connection value 'user_name' is not set."
|
31
37
|
def initialize
|
32
38
|
build
|
33
39
|
end
|
@@ -87,12 +93,6 @@ module Contrast
|
|
87
93
|
|
88
94
|
private
|
89
95
|
|
90
|
-
SESSION_VARIABLES = 'Invalid configuration. '\
|
91
|
-
"Setting both application.session_id and application.session_metadata is not allowed.\n"
|
92
|
-
API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
|
93
|
-
API_KEY = "Invalid configuration. Missing a required connection value 'api_key' is not set."
|
94
|
-
API_SERVICE_KEY = "Invalid configuration. Missing a required connection value 'service_tag' is not set."
|
95
|
-
API_USERNAME = "Invalid configuration. Missing a required connection value 'user_name' is not set."
|
96
96
|
# The config has information about how to construct the logger. If the config is invalid, and you want to know
|
97
97
|
# about it, then you have a circular dependency if you try to log it, so we use basic proto_logger to do this
|
98
98
|
# job.
|
@@ -18,7 +18,10 @@ module Contrast
|
|
18
18
|
DEFAULT_SERVICE_LEVEL = :TRACE
|
19
19
|
# The Rails ActionDispatch regexp for localhost IP + literal localhost
|
20
20
|
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/request.rb#L32
|
21
|
-
LOCALHOST = Regexp.union
|
21
|
+
LOCALHOST = Regexp.union([
|
22
|
+
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/,
|
23
|
+
/^localhost$/
|
24
|
+
])
|
22
25
|
|
23
26
|
def use_bundled_service?
|
24
27
|
# Validates the config to decide if it's suitable for starting
|
@@ -25,7 +25,7 @@ module Contrast
|
|
25
25
|
def sampling_control
|
26
26
|
@_sampling_control ||= begin
|
27
27
|
config_settings = ::Contrast::CONFIG.root.assess&.sampling
|
28
|
-
settings = ::Contrast::SETTINGS&.assess_state&.
|
28
|
+
settings = ::Contrast::SETTINGS&.assess_state&.sampling_settings
|
29
29
|
{
|
30
30
|
enabled: enabled?(config_settings, settings),
|
31
31
|
baseline: baseline(config_settings, settings),
|
@@ -99,43 +99,29 @@ module Contrast
|
|
99
99
|
@application_state.exclusion_matchers.select(&:code?)
|
100
100
|
end
|
101
101
|
|
102
|
-
# @param
|
103
|
-
def update_from_server_features
|
104
|
-
if
|
105
|
-
|
106
|
-
return unless server_features
|
107
|
-
|
108
|
-
log_file = server_features.log_file
|
109
|
-
log_level = server_features.log_level
|
110
|
-
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
|
111
|
-
@protect_state.enabled = server_features.protect.enabled?
|
112
|
-
@assess_state.enabled = server_features.assess.enabled?
|
113
|
-
@assess_state.sampling_settings = server_features.assess.sampling
|
102
|
+
# @param features_response [Contrast::Api::Settings::ServerFeatures, Contrast::Agent::Reporting::Response]
|
103
|
+
def update_from_server_features features_response
|
104
|
+
if features_response.cs__is_a?(Contrast::Agent::Reporting::Response)
|
105
|
+
update_from_response(features_response)
|
114
106
|
else
|
115
|
-
@protect_state.enabled =
|
116
|
-
@assess_state.enabled =
|
117
|
-
@assess_state.sampling_settings =
|
107
|
+
@protect_state.enabled = features_response.protect_enabled?
|
108
|
+
@assess_state.enabled = features_response.assess_enabled?
|
109
|
+
@assess_state.sampling_settings = features_response.assess.sampling
|
110
|
+
@last_server_update_ms = Contrast::Utils::Timer.now_ms
|
118
111
|
end
|
119
112
|
@last_server_update_ms = Contrast::Utils::Timer.now_ms
|
120
113
|
end
|
121
114
|
|
122
|
-
# @param
|
123
|
-
def update_from_application_settings
|
124
|
-
if
|
125
|
-
|
126
|
-
return unless settings
|
127
|
-
|
128
|
-
@application_state.modes_by_id = settings.protect.protection_rules_to_settings_hash
|
129
|
-
# TODO: RUBY-1438 this needs to be translated
|
130
|
-
# @application_state.exclusion_matchers = new_vals[:exclusion_matchers]
|
131
|
-
update_sensitive_data_policy(settings.sensitive_data_masking)
|
132
|
-
@assess_state.disabled_assess_rules = settings.assess.disabled_rules
|
133
|
-
@assess_state.session_id = settings.assess.session_id if settings.assess.session_id
|
115
|
+
# @param settings_response [Contrast::Api::Settings::ApplicationSettings, Contrast::Agent::Reporting::Response]
|
116
|
+
def update_from_application_settings settings_response
|
117
|
+
if settings_response&.cs__class == Contrast::Agent::Reporting::Response
|
118
|
+
update_from_response(settings_response)
|
134
119
|
else
|
135
|
-
new_vals =
|
120
|
+
new_vals = settings_response.application_state_translation
|
136
121
|
@application_state.modes_by_id = new_vals[:modes_by_id]
|
137
122
|
@application_state.exclusion_matchers = new_vals[:exclusion_matchers]
|
138
123
|
@assess_state.disabled_assess_rules = new_vals[:disabled_assess_rules]
|
124
|
+
@last_app_update_ms = Contrast::Utils::Timer.now_ms
|
139
125
|
end
|
140
126
|
@last_app_update_ms = Contrast::Utils::Timer.now_ms
|
141
127
|
end
|
@@ -184,6 +170,44 @@ module Contrast
|
|
184
170
|
|
185
171
|
private
|
186
172
|
|
173
|
+
# @param response [Contrast::Agent::Reporting::Response]
|
174
|
+
def update_from_response response
|
175
|
+
if (server_features = response.server_features)
|
176
|
+
update_server_features(server_features)
|
177
|
+
end
|
178
|
+
return unless (app_settings = response.application_settings)
|
179
|
+
|
180
|
+
update_application_settings(app_settings)
|
181
|
+
end
|
182
|
+
|
183
|
+
# @param server_features [Contrast::Agent::Reporting::Settings::FeatureSettings]
|
184
|
+
def update_server_features server_features
|
185
|
+
return unless server_features
|
186
|
+
|
187
|
+
log_file = server_features.log_file
|
188
|
+
log_level = server_features.log_level
|
189
|
+
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
|
190
|
+
@protect_state.enabled = server_features.protect.enabled?
|
191
|
+
@assess_state.enabled = server_features.assess.enabled?
|
192
|
+
@assess_state.sampling_settings = server_features.assess.sampling
|
193
|
+
@last_server_update_ms = Contrast::Utils::Timer.now_ms
|
194
|
+
end
|
195
|
+
|
196
|
+
# @param app_settings [Contrast::Agent::Reporting::Settings::ApplicationSettings]
|
197
|
+
def update_application_settings app_settings
|
198
|
+
return unless app_settings
|
199
|
+
|
200
|
+
@application_state.modes_by_id = app_settings.protect.protection_rules_to_settings_hash
|
201
|
+
# TODO: RUBY-1438 this needs to be translated
|
202
|
+
# @application_state.exclusion_matchers = new_vals[:exclusion_matchers]
|
203
|
+
update_sensitive_data_policy(app_settings.sensitive_data_masking)
|
204
|
+
@assess_state.disabled_assess_rules = app_settings.assess.disabled_rules
|
205
|
+
if app_settings.assess.session_id && !app_settings.assess.session_id.blank?
|
206
|
+
@assess_state.session_id = app_settings.assess.session_id
|
207
|
+
end
|
208
|
+
@last_app_update_ms = Contrast::Utils::Timer.now_ms
|
209
|
+
end
|
210
|
+
|
187
211
|
# check if settings are empty and return true if so.
|
188
212
|
#
|
189
213
|
# @param settings [String, Boolean, Array, Hash]
|
@@ -11,7 +11,7 @@ module Contrast
|
|
11
11
|
|
12
12
|
attr_accessor :disabled_rules
|
13
13
|
attr_writer :bot_blocker, :cmd_injection, :sql_injection, :nosql_injection, :untrusted_deserialization,
|
14
|
-
:
|
14
|
+
:xxe, :path_traversal, :reflected_xss, :unsafe_file_upload, :rule_base
|
15
15
|
|
16
16
|
BASE_RULE = 'Contrast::Agent::Protect::Rule::Base'.cs__freeze
|
17
17
|
|
@@ -27,7 +27,7 @@ module Contrast
|
|
27
27
|
attr_accessor :enable
|
28
28
|
|
29
29
|
def initialize hsh = {}
|
30
|
-
raise
|
30
|
+
raise(ArgumentError, 'Expected a hash') unless hsh.is_a?(Hash)
|
31
31
|
|
32
32
|
@api = Contrast::Config::ApiConfiguration.new(hsh[:api])
|
33
33
|
@enable = hsh[:enable]
|