contrast-agent 6.8.0 → 6.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/contrast/agent/assess/policy/trigger_method.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +11 -11
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/excluder.rb +1 -1
- data/lib/contrast/agent/middleware.rb +8 -2
- data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +116 -0
- data/lib/contrast/agent/protect/rule/base.rb +2 -2
- data/lib/contrast/agent/protect/rule/bot_blocker.rb +1 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +8 -7
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_chained_command.rb +0 -5
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +0 -5
- data/lib/contrast/agent/protect/rule/path_traversal.rb +4 -3
- data/lib/contrast/agent/protect/rule/sqli.rb +4 -3
- data/lib/contrast/agent/protect/rule/xss.rb +1 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +1 -1
- data/lib/contrast/agent/reporting/report.rb +1 -0
- data/lib/contrast/agent/reporting/reporter.rb +34 -0
- data/lib/contrast/agent/reporting/reporter_heartbeat.rb +3 -9
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +12 -7
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +239 -93
- data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -23
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +10 -9
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -0
- data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +8 -0
- data/lib/contrast/agent/reporting/reporting_events/server_settings.rb +40 -0
- data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +43 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +58 -4
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -3
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +76 -16
- data/lib/contrast/agent/reporting/server_settings_worker.rb +44 -0
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
- data/lib/contrast/agent/reporting/settings/helpers.rb +7 -0
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +39 -2
- data/lib/contrast/agent/reporting/settings/rule_definition.rb +3 -0
- data/lib/contrast/agent/reporting/settings/security_logger.rb +77 -0
- data/lib/contrast/agent/reporting/settings/server_features.rb +9 -0
- data/lib/contrast/agent/reporting/settings/syslog.rb +34 -5
- data/lib/contrast/agent/request.rb +1 -0
- data/lib/contrast/agent/request_handler.rb +5 -10
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +35 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +6 -0
- data/lib/contrast/api/communication/connection_status.rb +15 -0
- data/lib/contrast/components/agent.rb +34 -0
- data/lib/contrast/components/api.rb +23 -0
- data/lib/contrast/components/app_context.rb +23 -3
- data/lib/contrast/components/assess.rb +34 -4
- data/lib/contrast/components/assess_rules.rb +18 -0
- data/lib/contrast/components/base.rb +40 -0
- data/lib/contrast/components/config/sources.rb +95 -0
- data/lib/contrast/components/config.rb +18 -1
- data/lib/contrast/components/heap_dump.rb +10 -0
- data/lib/contrast/components/inventory.rb +15 -2
- data/lib/contrast/components/logger.rb +18 -0
- data/lib/contrast/components/polling.rb +36 -0
- data/lib/contrast/components/protect.rb +48 -1
- data/lib/contrast/components/ruby_component.rb +15 -0
- data/lib/contrast/components/sampling.rb +70 -13
- data/lib/contrast/components/security_logger.rb +13 -0
- data/lib/contrast/components/settings.rb +74 -7
- data/lib/contrast/config/certification_configuration.rb +14 -0
- data/lib/contrast/config/config.rb +46 -0
- data/lib/contrast/config/diagnostics.rb +114 -0
- data/lib/contrast/config/diagnostics_tools.rb +98 -0
- data/lib/contrast/config/effective_config.rb +65 -0
- data/lib/contrast/config/effective_config_value.rb +32 -0
- data/lib/contrast/config/exception_configuration.rb +12 -0
- data/lib/contrast/config/protect_rule_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +8 -7
- data/lib/contrast/config/request_audit_configuration.rb +13 -0
- data/lib/contrast/config/server_configuration.rb +41 -2
- data/lib/contrast/configuration.rb +28 -2
- data/lib/contrast/extension/assess/erb.rb +1 -1
- data/lib/contrast/utils/assess/event_limit_utils.rb +31 -9
- data/lib/contrast/utils/assess/trigger_method_utils.rb +5 -4
- data/lib/contrast/utils/hash_digest.rb +2 -2
- data/lib/contrast/utils/input_classification_base.rb +1 -2
- data/lib/contrast/utils/reporting/application_activity_batch_utils.rb +81 -0
- data/lib/contrast/utils/routes_sent.rb +60 -0
- data/lib/contrast/utils/telemetry_client.rb +1 -2
- data/lib/contrast/utils/timer.rb +16 -0
- data/lib/contrast.rb +3 -1
- data/ruby-agent.gemspec +5 -1
- metadata +29 -20
- data/lib/contrast/agent/assess/contrast_event.rb +0 -157
- data/lib/contrast/agent/assess/events/event_factory.rb +0 -34
- data/lib/contrast/agent/assess/events/source_event.rb +0 -46
- data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b850f63bce180f09f998f5363f58b01e9c69db61a2f98a31db97fb59b82564d7
|
4
|
+
data.tar.gz: 811072666998fb4daf0f49d2514d875b45c18d79d29398639862f1c2144aa930
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac0f4dcea0a62d6aa000659943f2b994a02940148fffdff2901ff4ff61d27fd257170ec4f48d0b1925d569dbc20de89a8866f76a17dd1c22aa15d9c80e4e9eb1
|
7
|
+
data.tar.gz: bd2490f015d1a5c8be5f0e7a0fc60e5acdc436b03e32420cbf19542a53b760c843cd84a17a0e7a8a3794ec69e3aa909e63fabdb32f4c32d5daa48ece261176aa
|
@@ -1,7 +1,6 @@
|
|
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/assess/events/event_factory'
|
5
4
|
require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
|
6
5
|
require 'contrast/agent/excluder'
|
7
6
|
require 'contrast/components/logger'
|
@@ -15,6 +14,7 @@ require 'contrast/agent/reporting/reporting_events/preflight_message'
|
|
15
14
|
require 'contrast/agent/reporting/reporting_events/route_discovery'
|
16
15
|
require 'contrast/agent/reporting/reporting_utilities/reporting_storage'
|
17
16
|
require 'contrast/agent/reporting/reporting_utilities/build_preflight'
|
17
|
+
require 'contrast/utils/assess/event_limit_utils'
|
18
18
|
|
19
19
|
module Contrast
|
20
20
|
module Agent
|
@@ -1,8 +1,7 @@
|
|
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/
|
5
|
-
require 'contrast/agent/assess/events/source_event'
|
4
|
+
require 'contrast/agent/reporting/reporting_events/finding_event'
|
6
5
|
|
7
6
|
module Contrast
|
8
7
|
module Agent
|
@@ -25,7 +24,7 @@ module Contrast
|
|
25
24
|
# the key used to accessed if from a map or nil if a type like
|
26
25
|
# BODY
|
27
26
|
def build_event event_data, source_type = nil, source_name = nil
|
28
|
-
@event = Contrast::Agent::
|
27
|
+
@event = Contrast::Agent::Reporting::FindingEvent.new(event_data, source_type, source_name)
|
29
28
|
report_sources(event_data.tagged, @event)
|
30
29
|
end
|
31
30
|
|
@@ -35,21 +34,22 @@ module Contrast
|
|
35
34
|
# context's observed route
|
36
35
|
#
|
37
36
|
# @param tagged [Object] The Target of the Event
|
38
|
-
# @param event [Contrast::Agent::
|
37
|
+
# @param event [Contrast::Agent::Reporting::FindingEvent]
|
39
38
|
def report_sources tagged, event
|
40
39
|
return unless tagged && !tagged.to_s.empty?
|
41
|
-
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
42
40
|
return unless event.source_type
|
43
41
|
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
event.event_sources&.each do |event_source|
|
44
|
+
if current_request.observed_route.sources.any? do |source|
|
45
|
+
source.source_type == event_source.source_type && source.source_name == event_source.source_name
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
|
48
|
+
next
|
49
|
+
end
|
51
50
|
|
52
|
-
|
51
|
+
current_request.observed_route.sources << event_source
|
52
|
+
end
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
@@ -82,7 +82,7 @@ module Contrast
|
|
82
82
|
event_sources = finding.events.flat_map(&:event_sources)
|
83
83
|
event_sources.each do |event_source|
|
84
84
|
return false unless rule_input_exclusions.any? do |exclusion|
|
85
|
-
input_match?(exclusion, event_source.
|
85
|
+
input_match?(exclusion, event_source.source_type, event_source.source_name)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -15,7 +15,7 @@ require 'contrast/agent/request_handler'
|
|
15
15
|
require 'contrast/agent/static_analysis'
|
16
16
|
require 'contrast/agent/telemetry/events/startup_metrics_event'
|
17
17
|
require 'contrast/utils/middleware_utils'
|
18
|
-
|
18
|
+
require 'contrast/utils/reporting/application_activity_batch_utils'
|
19
19
|
require 'contrast/utils/timer'
|
20
20
|
|
21
21
|
module Contrast
|
@@ -27,6 +27,7 @@ module Contrast
|
|
27
27
|
include Contrast::Components::Logger::InstanceMethods
|
28
28
|
include Contrast::Components::Scope::InstanceMethods
|
29
29
|
include Contrast::Utils::MiddlewareUtils
|
30
|
+
include Contrast::Utils::Reporting::ApplicationActivityBatchUtils
|
30
31
|
|
31
32
|
attr_reader :app
|
32
33
|
|
@@ -62,6 +63,7 @@ module Contrast
|
|
62
63
|
# the Rack framework.
|
63
64
|
def call env
|
64
65
|
logger.trace_with_time('Elapsed time for Contrast::Agent::Middleware#call') do
|
66
|
+
::Contrast::Agent::ThreadWatcher.check_before_start
|
65
67
|
return app.call(env) unless ::Contrast::AGENT.enabled?
|
66
68
|
|
67
69
|
Contrast::Agent.heapdump_util.start_thread!
|
@@ -173,13 +175,17 @@ module Contrast
|
|
173
175
|
Contrast::Agent::FINDINGS.report_collected_findings unless Contrast::Agent::FINDINGS.collection.empty?
|
174
176
|
# All protect rules, which are trigger but require response to be reported
|
175
177
|
Contrast::Agent::EXPLOITS.report_recorded_exploits(context) unless Contrast::Agent::EXPLOITS.collection.empty?
|
178
|
+
# Process Worth Watching Inputs for v2 rules
|
179
|
+
Contrast::Agent.worth_watching_analyzer&.add_to_queue(context.agent_input_analysis)
|
176
180
|
|
177
181
|
if Contrast::Agent.framework_manager.streaming?(env)
|
178
182
|
context.reset_activity
|
179
183
|
request_handler.stream_safe_postfilter
|
180
184
|
else
|
181
185
|
request_handler.ruleset.postfilter
|
182
|
-
request_handler.
|
186
|
+
request_handler.report_observed_route
|
187
|
+
add_activity_to_batch(context.activity)
|
188
|
+
report_batch
|
183
189
|
end
|
184
190
|
end
|
185
191
|
# unsuccessful attack
|
@@ -0,0 +1,116 @@
|
|
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/agent/worker_thread'
|
5
|
+
require 'contrast/agent/reporting/input_analysis/score_level'
|
6
|
+
require 'contrast/agent/reporting/reporting_events/application_activity'
|
7
|
+
require 'contrast/utils/input_classification_base'
|
8
|
+
|
9
|
+
module Contrast
|
10
|
+
module Agent
|
11
|
+
module Protect
|
12
|
+
# WorthWatchingInputAnalyzer Perform analysis of input tracing v2 worthwatching results in a
|
13
|
+
# separate thread, should only be run at the end of the request.
|
14
|
+
# Currently only includes: cmd_injection & sqli_injection rules
|
15
|
+
class WorthWatchingInputAnalyzer < WorkerThread
|
16
|
+
include Timeout
|
17
|
+
include Contrast::Agent::Protect::Rule::InputClassificationBase
|
18
|
+
|
19
|
+
QUEUE_SIZE = 1000.cs__freeze
|
20
|
+
AGENTLIB_TIMEOUT = 5.cs__freeze
|
21
|
+
# max size of inputs to evaluate
|
22
|
+
INPUT_BYTESIZE_THRESHOLD = 100_000.cs__freeze
|
23
|
+
REPORT_INTERVAL_SECOND = 30.cs__freeze
|
24
|
+
|
25
|
+
# Thread that will process all the InputAnalysisResults that have a score level of WORTHWATCHING and
|
26
|
+
# sends results to TeamServer
|
27
|
+
def start_thread!
|
28
|
+
return if running?
|
29
|
+
|
30
|
+
@_thread = Contrast::Agent::Thread.new do
|
31
|
+
logger.info('Starting Worth Watching Analyzer thread.')
|
32
|
+
loop do
|
33
|
+
sleep(REPORT_INTERVAL_SECOND)
|
34
|
+
next if queue.empty?
|
35
|
+
|
36
|
+
report = false
|
37
|
+
num_to_report = queue.length
|
38
|
+
activity = Contrast::Agent::Reporting::ApplicationActivity.new
|
39
|
+
num_to_report.times do
|
40
|
+
next unless (attack_result = eval_input(queue.pop))
|
41
|
+
|
42
|
+
activity.attach_defend(attack_result)
|
43
|
+
report = true
|
44
|
+
end
|
45
|
+
Contrast::Agent.reporter.send_event_immediately(activity) if report
|
46
|
+
rescue StandardError => e
|
47
|
+
logger.debug('Worth Watching Analyzer thread could not process result because of:', e)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# param Contrast::Agent::Reporting::InputAnalysis
|
53
|
+
def add_to_queue input_analysis
|
54
|
+
return if input_analysis&.results&.empty?
|
55
|
+
|
56
|
+
if queue.size >= QUEUE_SIZE
|
57
|
+
logger.debug('WorthWatching queue at max size, skip input_result')
|
58
|
+
return
|
59
|
+
end
|
60
|
+
input_analysis.results.select { |val| val.score_level == WORTHWATCHING }.
|
61
|
+
each do |ia_result|
|
62
|
+
queue << ia_result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the WorthWatching InputAnalysisResult
|
69
|
+
# @return [Contrast::Agent::Reporting::InputAnalysisResult, nil] InputAnalysisResult updated Result or nil
|
70
|
+
def eval_input ia_result
|
71
|
+
return if ia_result.nil?
|
72
|
+
|
73
|
+
if ia_result.value.to_s.bytesize >= INPUT_BYTESIZE_THRESHOLD
|
74
|
+
logger.debug("Skip anaylsis: Input size is larger than #{ INPUT_BYTESIZE_THRESHOLD / 1024 }KB")
|
75
|
+
return
|
76
|
+
end
|
77
|
+
|
78
|
+
begin
|
79
|
+
input_eval = Timeout.timeout(AGENTLIB_TIMEOUT) do
|
80
|
+
Contrast::AGENT_LIB.eval_input(ia_result.value,
|
81
|
+
convert_input_type(ia_result.input_type),
|
82
|
+
Contrast::AGENT_LIB.rule_set[ia_result.rule_id],
|
83
|
+
Contrast::AGENT_LIB.eval_option[:NONE])
|
84
|
+
end
|
85
|
+
score = input_eval&.score || 0
|
86
|
+
return if score <= THRESHOLD
|
87
|
+
|
88
|
+
ia_result.score_level = DEFINITEATTACK
|
89
|
+
build_attack_result(ia_result)
|
90
|
+
rescue Timeout::Error => e
|
91
|
+
logger.warn('AgentLib timed out when processing WORTHWATCHING InputAnalysisResult', e, ia_result)
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the updated InputAnalysisResult
|
97
|
+
# with a score of :DEFINITEATTACK
|
98
|
+
# @return [Contrast::Agent::Reporting::AttackResult] the attack result from
|
99
|
+
# this input
|
100
|
+
def build_attack_result ia_result
|
101
|
+
Contrast::PROTECT.rule(ia_result.rule_id).build_attack_without_match(nil, ia_result, nil)
|
102
|
+
end
|
103
|
+
|
104
|
+
def queue
|
105
|
+
@_queue ||= Queue.new
|
106
|
+
end
|
107
|
+
|
108
|
+
def delete_queue!
|
109
|
+
@_queue&.clear
|
110
|
+
@_queue&.close
|
111
|
+
@_queue = nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -148,14 +148,14 @@ module Contrast
|
|
148
148
|
# protect rule but did not exploit the application. As such, we need
|
149
149
|
# to build a result to report this violation to TeamServer.
|
150
150
|
#
|
151
|
-
# @param context [Contrast::Agent::RequestContext] the context of the
|
151
|
+
# @param context [Contrast::Agent::RequestContext, nil] the context of the
|
152
152
|
# request in which this input is evaluated.
|
153
153
|
# @param ia_result [Contrast::Agent::Reporting::InputAnalysis] the
|
154
154
|
# analysis of the input that was determined to be an attack
|
155
155
|
# @param result [Contrast::Agent::Reporting::AttackResult, nil] previous
|
156
156
|
# attack result for this rule, if one exists, in the case of
|
157
157
|
# multiple inputs being found to violate the protection criteria
|
158
|
-
# @param kwargs [Hash] key - value pairs of context individual rules
|
158
|
+
# @param kwargs [Hash, nil] key - value pairs of context individual rules
|
159
159
|
# need to build out details to send to TeamServer to tell the
|
160
160
|
# story of the attack
|
161
161
|
# @return [Contrast::Agent::Reporting::AttackResult] the attack result from
|
@@ -66,7 +66,7 @@ module Contrast
|
|
66
66
|
# @param ia_result [Contrast::Agent::Reporting::InputAnalysisResult]
|
67
67
|
# @param _candidate_string
|
68
68
|
# @param **_kwargs
|
69
|
-
# @return [Contrast::
|
69
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample]
|
70
70
|
def build_sample context, ia_result, _candidate_string, **_kwargs
|
71
71
|
sample = build_base_sample(context, ia_result)
|
72
72
|
sample.details = Contrast::Agent::Reporting::BotBlockerDetails.new
|
@@ -21,24 +21,25 @@ module Contrast
|
|
21
21
|
include Contrast::Components::Logger::InstanceMethods
|
22
22
|
include Contrast::Agent::Reporting::InputType
|
23
23
|
NAME = 'cmd-injection'
|
24
|
-
|
25
24
|
APPLICABLE_USER_INPUTS = [
|
26
25
|
BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME,
|
27
26
|
PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE,
|
28
27
|
MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE
|
29
28
|
].cs__freeze
|
30
|
-
SUB_RULES = [
|
31
|
-
Contrast::Agent::Protect::Rule::CmdiBackdoors.new,
|
32
|
-
Contrast::Agent::Protect::Rule::CmdiChainedCommand.new,
|
33
|
-
Contrast::Agent::Protect::Rule::CmdiDangerousPath.new
|
34
|
-
].cs__freeze
|
35
29
|
|
36
30
|
def rule_name
|
37
31
|
NAME
|
38
32
|
end
|
39
33
|
|
34
|
+
# Array of sub_rules:
|
35
|
+
#
|
36
|
+
# @return [Array]
|
40
37
|
def sub_rules
|
41
|
-
|
38
|
+
@_sub_rules ||= [
|
39
|
+
Contrast::Agent::Protect::Rule::CmdiBackdoors.new,
|
40
|
+
Contrast::Agent::Protect::Rule::CmdiChainedCommand.new,
|
41
|
+
Contrast::Agent::Protect::Rule::CmdiDangerousPath.new
|
42
|
+
].cs__freeze
|
42
43
|
end
|
43
44
|
|
44
45
|
def applicable_user_inputs
|
@@ -37,14 +37,15 @@ module Contrast
|
|
37
37
|
/windows/repair/
|
38
38
|
].cs__freeze
|
39
39
|
|
40
|
-
SUB_RULES = [Contrast::Agent::Protect::Rule::PathTraversalSemanticBypass.new].cs__freeze
|
41
|
-
|
42
40
|
def rule_name
|
43
41
|
NAME
|
44
42
|
end
|
45
43
|
|
44
|
+
# Array of sub_rules
|
45
|
+
#
|
46
|
+
# @return [Array]
|
46
47
|
def sub_rules
|
47
|
-
|
48
|
+
@_sub_rules ||= [Contrast::Agent::Protect::Rule::PathTraversalSemanticBypass.new].cs__freeze
|
48
49
|
end
|
49
50
|
|
50
51
|
def applicable_user_inputs
|
@@ -26,8 +26,6 @@ module Contrast
|
|
26
26
|
|
27
27
|
NAME = 'sql-injection'
|
28
28
|
|
29
|
-
SUB_RULES = [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
|
30
|
-
|
31
29
|
def rule_name
|
32
30
|
NAME
|
33
31
|
end
|
@@ -36,8 +34,11 @@ module Contrast
|
|
36
34
|
BLOCK_MESSAGE
|
37
35
|
end
|
38
36
|
|
37
|
+
# Array of sub_rules
|
38
|
+
#
|
39
|
+
# @return [Array]
|
39
40
|
def sub_rules
|
40
|
-
|
41
|
+
@_sub_rules ||= [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
|
41
42
|
end
|
42
43
|
|
43
44
|
def applicable_user_inputs
|
@@ -28,7 +28,7 @@ module Contrast
|
|
28
28
|
# @return [Contrast::Agent::Reporting::RaspRuleSample]
|
29
29
|
def build context, ia_result
|
30
30
|
sample = new
|
31
|
-
sample.time_stamp = context&.timer&.start_ms
|
31
|
+
sample.time_stamp = context&.timer&.start_ms || Contrast::Utils::Timer.now_ms
|
32
32
|
sample.user_input = build_user_input_from_ia(ia_result)
|
33
33
|
sample.user_input.document_type = if context&.request
|
34
34
|
Contrast::Utils::StringUtils.force_utf8(context.request.document_type)
|
@@ -26,5 +26,6 @@ require 'contrast/agent/reporting/reporting_events/observed_route'
|
|
26
26
|
require 'contrast/agent/reporting/reporting_events/route_coverage'
|
27
27
|
require 'contrast/agent/reporting/reporting_events/observed_library_usage'
|
28
28
|
require 'contrast/agent/reporting/reporting_events/poll'
|
29
|
+
require 'contrast/agent/reporting/reporting_events/server_settings'
|
29
30
|
# Sensitive data masking
|
30
31
|
require 'contrast/agent/reporting/masker/masker'
|
@@ -5,6 +5,8 @@ require 'contrast/agent/worker_thread'
|
|
5
5
|
require 'contrast/agent/reporting/report'
|
6
6
|
require 'contrast/components/logger'
|
7
7
|
require 'contrast/agent/reporting/reporting_events/agent_startup'
|
8
|
+
require 'contrast/agent/telemetry/events/exceptions/telemetry_exceptions'
|
9
|
+
require 'contrast/agent/telemetry/events/exceptions/obfuscate'
|
8
10
|
|
9
11
|
module Contrast
|
10
12
|
module Agent
|
@@ -13,6 +15,8 @@ module Contrast
|
|
13
15
|
include Contrast::Components::Logger::InstanceMethods
|
14
16
|
include Contrast::Utils::ObjectShare
|
15
17
|
|
18
|
+
MAX_QUEUE_SIZE = 1000
|
19
|
+
|
16
20
|
class << self
|
17
21
|
# check if we can report to TS
|
18
22
|
#
|
@@ -68,6 +72,14 @@ module Contrast
|
|
68
72
|
end
|
69
73
|
return unless event
|
70
74
|
|
75
|
+
if queue.size >= MAX_QUEUE_SIZE
|
76
|
+
if Contrast::Agent::Telemetry::Base.enabled?
|
77
|
+
Contrast::Agent.thread_watcher.telemetry_queue.send_event(queue_limit_telemetry_event)
|
78
|
+
end
|
79
|
+
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
71
83
|
queue << event
|
72
84
|
end
|
73
85
|
|
@@ -126,6 +138,28 @@ module Contrast
|
|
126
138
|
rescue StandardError => e
|
127
139
|
logger.error('Could not send message to TeamServer from Reporter queue.', e)
|
128
140
|
end
|
141
|
+
|
142
|
+
# @return [Contrast::Agent::Telemetry::TelemetryException::Event]
|
143
|
+
def queue_limit_telemetry_event
|
144
|
+
message_exception = Contrast::Agent::Telemetry::TelemetryException::MessageException.build(
|
145
|
+
'String',
|
146
|
+
"Maximum queue size (#{ MAX_QUEUE_SIZE }) reached for reporting events", nil, stack_frame)
|
147
|
+
message = Contrast::Agent::Telemetry::TelemetryException::Message.build({}, [message_exception])
|
148
|
+
Contrast::Agent::Telemetry::TelemetryException::Event.new(message)
|
149
|
+
end
|
150
|
+
|
151
|
+
def stack_frame
|
152
|
+
stack_trace = caller_locations(20, 20)
|
153
|
+
stack_frame_type = if stack_trace.nil? || stack_trace[1].nil?
|
154
|
+
'none'
|
155
|
+
else
|
156
|
+
Contrast::Agent::Telemetry::TelemetryException::Obfuscate.obfuscate_type(
|
157
|
+
stack_trace[1].path.delete_prefix(Dir.pwd))
|
158
|
+
end
|
159
|
+
|
160
|
+
stack_frame_function = stack_trace.nil? || stack_trace[1].nil? ? 'none' : stack_trace[1].label
|
161
|
+
Contrast::Agent::Telemetry::TelemetryException::StackFrame.build(stack_frame_function, stack_frame_type, nil)
|
162
|
+
end
|
129
163
|
end
|
130
164
|
end
|
131
165
|
end
|
@@ -1,18 +1,16 @@
|
|
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/
|
5
|
-
require 'contrast/agent/reporting/report'
|
4
|
+
require 'contrast/agent/reporting/reporter'
|
6
5
|
require 'contrast/agent/inventory/dependency_usage_analysis'
|
7
6
|
require 'contrast/agent/reporting/reporting_events/poll'
|
8
|
-
require 'contrast/agent/reporting/reporting_events/server_activity'
|
9
7
|
|
10
8
|
module Contrast
|
11
9
|
module Agent
|
12
10
|
# The ReporterHeartbeat will make sure that the process remains marked alive by TeamServer and that we periodically
|
13
11
|
# reach out to get the latest settings for this application. It also sends out those messages which do not need to
|
14
12
|
# be associated directly with a request, such as Server Activity and Library Observation.
|
15
|
-
class ReporterHeartbeat <
|
13
|
+
class ReporterHeartbeat < Reporter
|
16
14
|
# TeamServer will mark an application offline after 5 minutes. Sending this every one should be more than enough
|
17
15
|
# to satisfy our goals.
|
18
16
|
REFRESH_INTERVAL_SEC = 60
|
@@ -42,11 +40,7 @@ module Contrast
|
|
42
40
|
#
|
43
41
|
# @return [Array<Contrast::Agent::Reporting::ReportingEvent>]
|
44
42
|
def polling_events
|
45
|
-
[
|
46
|
-
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage,
|
47
|
-
Contrast::Agent::Reporting::ServerActivity.new,
|
48
|
-
poll_message
|
49
|
-
].compact
|
43
|
+
[Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage, poll_message].compact
|
50
44
|
end
|
51
45
|
end
|
52
46
|
end
|
@@ -110,7 +110,7 @@ module Contrast
|
|
110
110
|
|
111
111
|
# This is primary used for attaching new inventory reporting
|
112
112
|
#
|
113
|
-
# @param architecture [Contrast::Agent::Reporting::
|
113
|
+
# @param architecture [Contrast::Agent::Reporting::ArchitectureComponent]
|
114
114
|
def attach_inventory architecture
|
115
115
|
inventory.attach_data(architecture)
|
116
116
|
end
|
@@ -28,18 +28,23 @@ module Contrast
|
|
28
28
|
def attach_data attack_result
|
29
29
|
attacker_activity = Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.new
|
30
30
|
attacker_activity.attach_data(attack_result)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
rule = attack_result.rule_id
|
36
|
-
if existing_attacker_activity
|
37
|
-
attach_existing(existing_attacker_activity, attacker_activity, rule)
|
31
|
+
|
32
|
+
if (existing_attacker_activity = find_existing_attacker_activity(attacker_activity))
|
33
|
+
attach_existing(existing_attacker_activity, attacker_activity, attack_result.rule_id)
|
38
34
|
else
|
39
35
|
attackers << attacker_activity
|
40
36
|
end
|
41
37
|
end
|
42
38
|
|
39
|
+
# Find an existing attacker if it matches on source details
|
40
|
+
# @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
|
41
|
+
def find_existing_attacker_activity new_attacker_activity
|
42
|
+
attackers.find do |existing|
|
43
|
+
existing.source_forwarded_for == new_attacker_activity.source_forwarded_for &&
|
44
|
+
existing.source_ip == new_attacker_activity.source_ip
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
43
48
|
# @param existing_attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
|
44
49
|
# @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
|
45
50
|
# @param rule [String]
|
@@ -13,7 +13,7 @@ module Contrast
|
|
13
13
|
# about the inventory of the application which was discovered during exercise of the application
|
14
14
|
# during this activity period.
|
15
15
|
class ApplicationInventoryActivity < Contrast::Agent::Reporting::ApplicationReportingEvent
|
16
|
-
# return [Contrast::Agent::Reporting::ArchitectureComponent]
|
16
|
+
# return [Array<Contrast::Agent::Reporting::ArchitectureComponent>]
|
17
17
|
attr_reader :components
|
18
18
|
# @ return [Array<String>, nil] - User-Agent Header value
|
19
19
|
attr_reader :browsers
|
@@ -46,6 +46,11 @@ module Contrast
|
|
46
46
|
def duration
|
47
47
|
Contrast::Utils::Timer.now_ms - (Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0)
|
48
48
|
end
|
49
|
+
|
50
|
+
# Helper method to determine if InventoryActivity has no data
|
51
|
+
def empty?
|
52
|
+
@browsers.empty? && @components.empty?
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|
51
56
|
end
|
@@ -88,8 +88,8 @@ module Contrast
|
|
88
88
|
event_messages = Contrast::Agent::Reporting::FindingEvent.from_source(source)
|
89
89
|
events.concat(event_messages) if event_messages&.any?
|
90
90
|
event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
|
91
|
-
contrast_event = Contrast::Agent::
|
92
|
-
events <<
|
91
|
+
contrast_event = Contrast::Agent::Reporting::FindingEvent.new(event_data)
|
92
|
+
events << contrast_event
|
93
93
|
return unless request
|
94
94
|
|
95
95
|
@request = Contrast::Agent::Reporting::FindingRequest.convert(request)
|