contrast-agent 6.4.0 → 6.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/cs__contrast_patch/cs__contrast_patch.c +14 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +1 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +5 -1
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +4 -0
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +5 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +8 -2
- data/lib/contrast/agent/assess/tracker.rb +12 -0
- data/lib/contrast/agent/inventory/database_config.rb +2 -1
- data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -2
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -1
- data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/patching/policy/method_policy.rb +3 -3
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/reporting/reporter_heartbeat.rb +1 -3
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +17 -21
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +26 -3
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +5 -5
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
- data/lib/contrast/agent/request_context.rb +8 -0
- data/lib/contrast/agent/service_heartbeat.rb +2 -3
- data/lib/contrast/agent/static_analysis.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent/worker_thread.rb +10 -0
- data/lib/contrast/api/communication/response_processor.rb +1 -1
- data/lib/contrast/components/agent.rb +52 -14
- data/lib/contrast/components/api.rb +60 -23
- data/lib/contrast/components/assess.rb +16 -0
- data/lib/contrast/components/contrast_service.rb +1 -1
- data/lib/contrast/components/heap_dump.rb +51 -1
- data/lib/contrast/components/inventory.rb +19 -13
- data/lib/contrast/components/logger.rb +18 -0
- data/lib/contrast/components/protect.rb +41 -1
- data/lib/contrast/components/sampling.rb +29 -0
- data/lib/contrast/config/assess_configuration.rb +33 -3
- data/lib/contrast/config/base_configuration.rb +8 -2
- data/lib/contrast/config/root_configuration.rb +19 -16
- data/lib/contrast/config/service_configuration.rb +4 -4
- data/lib/contrast/config.rb +0 -9
- data/lib/contrast/extension/object.rb +19 -0
- data/lib/contrast/framework/rails/support.rb +7 -3
- data/lib/contrast/logger/log.rb +2 -1
- data/lib/contrast/utils/assess/event_limit_utils.rb +96 -0
- data/lib/contrast/utils/assess/propagation_method_utils.rb +27 -7
- data/lib/contrast/utils/log_utils.rb +2 -2
- data/lib/contrast/utils/net_http_base.rb +2 -2
- data/lib/contrast/utils/patching/policy/patch_utils.rb +1 -1
- data/lib/contrast.rb +6 -21
- data/resources/assess/policy.json +15 -12
- data/resources/deadzone/policy.json +139 -19
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +43 -20
- data/lib/contrast/config/agent_configuration.rb +0 -63
- data/lib/contrast/config/api_configuration.rb +0 -56
- data/lib/contrast/config/heap_dump_configuration.rb +0 -59
- data/lib/contrast/config/inventory_configuration.rb +0 -33
- data/lib/contrast/config/logger_configuration.rb +0 -26
- data/lib/contrast/config/protect_configuration.rb +0 -33
- data/lib/contrast/config/sampling_configuration.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9901918f58625ea7f9366f73110afcdd5f05e119261ba9a08f24f36903fe897
|
4
|
+
data.tar.gz: c618ebc74b006529e2317cd62ba59fabbba9d0c8fbf24c7988dfd98ec627b04e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2f584a6658ab0e316b41021575888d9ecc1eaacf79d971492fd2e6317609bfd0cb3a762a64d620b08fc18c76e2434d05cc3d82f8d587c86cdae5116df6cae61
|
7
|
+
data.tar.gz: df8f941fe730188be0bc3b69bd9a9d6d60f9695be8ee8e8eb0b6e35ab1207d97debba28220ff796bec702ad3b158f53f00f87a58f1c14939092ed843bef66715
|
@@ -101,7 +101,18 @@ VALUE rescue_func(VALUE arg1) {
|
|
101
101
|
return Qnil;
|
102
102
|
}
|
103
103
|
|
104
|
+
/**
|
105
|
+
* In the event that the original_method call throws an exception we need to ensure that contrast_post_patch is called
|
106
|
+
* to report that error. However, if there is no error we will call post_patch with the original_return instead of
|
107
|
+
* Qnil.
|
108
|
+
*
|
109
|
+
**/
|
104
110
|
VALUE contrast_patch_call_ensure(const VALUE *args) {
|
111
|
+
// we do not need to ensure that post patch is called if no error was thrown
|
112
|
+
if(!RTEST(rb_errinfo())) {
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
|
105
116
|
int argc;
|
106
117
|
VALUE object, preshift, method_policy, method;
|
107
118
|
VALUE *argv;
|
@@ -125,6 +136,7 @@ VALUE ensure_wrapper(const VALUE *args) {
|
|
125
136
|
original_args = (VALUE)args[1];
|
126
137
|
ensure_args = (VALUE)args[2];
|
127
138
|
|
139
|
+
//this ensure if being treated as a rescue due to issues surrounding Kernel#throw
|
128
140
|
return rb_ensure(original_method, original_args, contrast_patch_call_ensure,
|
129
141
|
(VALUE)ensure_args);
|
130
142
|
}
|
@@ -220,13 +232,14 @@ VALUE contrast_run_patches(const VALUE *wrapped_args) {
|
|
220
232
|
* If the original method threw an exception, contrast_patch_call_rescue
|
221
233
|
* re-raises the original exception, which unwinds the stack back to the
|
222
234
|
* call site. This means the rest of this function is not executed.
|
235
|
+
* post_patch is called in the ensure_wrapper on exception. rb_rescue
|
236
|
+
* raises the exception so the below will not be executed in that event.
|
223
237
|
*/
|
224
238
|
|
225
239
|
/* Invoke Contrast post-call patching. */
|
226
240
|
contrast_call_post_patch(method_policy, preshift, object,
|
227
241
|
original_ret, argc, argv);
|
228
242
|
|
229
|
-
/* Special case for tracking frozen sources */
|
230
243
|
return original_ret;
|
231
244
|
}
|
232
245
|
|
@@ -8,6 +8,7 @@ require 'contrast/components/logger'
|
|
8
8
|
require 'contrast/utils/object_share'
|
9
9
|
require 'contrast/utils/sha256_builder'
|
10
10
|
require 'contrast/utils/assess/propagation_method_utils'
|
11
|
+
require 'contrast/utils/assess/event_limit_utils'
|
11
12
|
require 'contrast/agent/assess/events/event_data'
|
12
13
|
require 'contrast/utils/assess/object_store'
|
13
14
|
|
@@ -21,6 +22,7 @@ module Contrast
|
|
21
22
|
module PropagationMethod
|
22
23
|
extend Contrast::Components::Logger::InstanceMethods
|
23
24
|
extend Contrast::Utils::Assess::PropagationMethodUtils
|
25
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
24
26
|
|
25
27
|
@properties = Contrast::Utils::Assess::ObjectStore.new
|
26
28
|
|
@@ -38,6 +40,7 @@ module Contrast
|
|
38
40
|
def apply_propagation method_policy, preshift, object, ret, args, block
|
39
41
|
return unless (propagation_node = method_policy.propagation_node)
|
40
42
|
return unless propagation_node.use_original_object? || preshift
|
43
|
+
return if event_limit?(method_policy)
|
41
44
|
|
42
45
|
target = determine_target(propagation_node, ret, object, args)
|
43
46
|
propagation_data = Contrast::Agent::Assess::Events::EventData.new(nil, nil, object, ret, args)
|
@@ -184,7 +187,7 @@ module Contrast
|
|
184
187
|
# @param _block [Block] the Block passed to the original method
|
185
188
|
def handle_cs_properties_propagation propagation_node, preshift, target, propagation_data, _block
|
186
189
|
return if propagation_node.action == NOOP_ACTION
|
187
|
-
return unless can_propagate?(propagation_node, preshift, target)
|
190
|
+
return unless can_propagate?(propagation_node, preshift, target, propagation_data)
|
188
191
|
return unless (propagation_class = find_propagation_class(propagation_node))
|
189
192
|
|
190
193
|
# If we are using the original object tracking, the preshift object is not created.
|
@@ -192,6 +195,7 @@ module Contrast
|
|
192
195
|
source = propagation_node.use_original_object? ? propagation_data.object : preshift
|
193
196
|
handle_propagation(propagation_class, propagation_node, source, target)
|
194
197
|
update_properties(propagation_node, target, propagation_data)
|
198
|
+
increment_event_count(propagation_node)
|
195
199
|
end
|
196
200
|
|
197
201
|
def handle_propagation propagation_class, propagation_node, source, target
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/extension/module'
|
5
|
+
require 'contrast/utils/assess/event_limit_utils'
|
5
6
|
|
6
7
|
module Contrast
|
7
8
|
module Agent
|
@@ -13,6 +14,8 @@ module Contrast
|
|
13
14
|
# action knows the class and method it should call to preform this
|
14
15
|
# action.
|
15
16
|
module Custom
|
17
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
18
|
+
|
16
19
|
class << self
|
17
20
|
def propagate propagation_node, preshift, ret, block
|
18
21
|
clazz = propagation_node.patch_class
|
@@ -26,6 +29,7 @@ module Contrast
|
|
26
29
|
propagation_node.patch_class = clazz
|
27
30
|
end
|
28
31
|
clazz.send(method, propagation_node, preshift, ret, block)
|
32
|
+
increment_event_count(propagation_node)
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'contrast/utils/assess/event_limit_utils'
|
5
|
+
|
4
6
|
module Contrast
|
5
7
|
module Agent
|
6
8
|
module Assess
|
@@ -11,6 +13,8 @@ module Contrast
|
|
11
13
|
# results in new source nodes to track which columns in the database
|
12
14
|
# have been tainted.
|
13
15
|
class DatabaseWrite < Contrast::Agent::Assess::Policy::Propagator::Base
|
16
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
17
|
+
|
14
18
|
class << self
|
15
19
|
def propagate propagation_node, preshift, target
|
16
20
|
return unless Contrast::ASSESS.require_dynamic_sources?
|
@@ -22,6 +26,7 @@ module Contrast
|
|
22
26
|
known_tainted = ::Contrast::ASSESS.tainted_columns[class_name]
|
23
27
|
propagation_node.sources.each do |source|
|
24
28
|
handle_write(propagation_node, source, preshift, target, known_tainted, tainted_columns)
|
29
|
+
increment_event_count(propagation_node)
|
25
30
|
end
|
26
31
|
return if tainted_columns.empty?
|
27
32
|
|
@@ -6,6 +6,7 @@ require 'contrast/components/agent'
|
|
6
6
|
require 'contrast/components/logger'
|
7
7
|
require 'contrast/components/scope'
|
8
8
|
require 'contrast/utils/thread_tracker'
|
9
|
+
require 'contrast/utils/assess/event_limit_utils'
|
9
10
|
require 'contrast/utils/assess/split_utils'
|
10
11
|
require 'contrast/agent/assess/events/event_data'
|
11
12
|
|
@@ -20,6 +21,7 @@ module Contrast
|
|
20
21
|
extend Contrast::Components::Scope::InstanceMethods
|
21
22
|
extend Contrast::Components::Logger::InstanceMethods
|
22
23
|
extend Contrast::Utils::Assess::SplitUtils
|
24
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
23
25
|
|
24
26
|
SPLIT_TRACKER = Contrast::Utils::ThreadTracker.new
|
25
27
|
|
@@ -42,6 +44,7 @@ module Contrast
|
|
42
44
|
return unless (source_properties = Contrast::Agent::Assess::Tracker.properties(source))
|
43
45
|
|
44
46
|
update_element_properties(propagation_node, target, preshift, source_properties)
|
47
|
+
increment_event_count(propagation_node)
|
45
48
|
nil
|
46
49
|
end
|
47
50
|
|
@@ -7,6 +7,7 @@ require 'contrast/components/logger'
|
|
7
7
|
require 'contrast/utils/object_share'
|
8
8
|
require 'contrast/utils/sha256_builder'
|
9
9
|
require 'contrast/utils/assess/source_method_utils'
|
10
|
+
require 'contrast/utils/assess/event_limit_utils'
|
10
11
|
require 'contrast/agent/assess/events/event_data'
|
11
12
|
|
12
13
|
module Contrast
|
@@ -19,6 +20,7 @@ module Contrast
|
|
19
20
|
module SourceMethod
|
20
21
|
extend Contrast::Components::Logger::InstanceMethods
|
21
22
|
extend Contrast::Utils::Assess::SourceMethodUtils
|
23
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
22
24
|
|
23
25
|
PARAMETER_TYPE = 'PARAMETER'
|
24
26
|
PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
|
@@ -37,6 +39,7 @@ module Contrast
|
|
37
39
|
# @param args [Array<Object>] the Arguments with which the method was invoked
|
38
40
|
def apply_source method_policy, object, ret, args
|
39
41
|
return unless analyze?(method_policy, object, ret, args)
|
42
|
+
return if event_limit?(method_policy)
|
40
43
|
return unless (source_node = method_policy.source_node)
|
41
44
|
|
42
45
|
# used to hold the object and ret
|
@@ -66,6 +69,8 @@ module Contrast
|
|
66
69
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
67
70
|
return unless context && source_node && target
|
68
71
|
|
72
|
+
increment_event_count(source_node)
|
73
|
+
|
69
74
|
source_name ||= determine_source_name(source_node, source_data.object, source_data.ret, *args)
|
70
75
|
# We know we only work on certain things.
|
71
76
|
# Skip if this isn't one of them
|
@@ -25,6 +25,7 @@ module Contrast
|
|
25
25
|
module TriggerMethod # rubocop:disable Metrics/ModuleLength
|
26
26
|
extend Contrast::Components::Logger::InstanceMethods
|
27
27
|
extend Contrast::Utils::Assess::TriggerMethodUtils
|
28
|
+
extend Contrast::Utils::Assess::EventLimitUtils
|
28
29
|
|
29
30
|
# The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
|
30
31
|
# without routes.
|
@@ -48,6 +49,7 @@ module Contrast
|
|
48
49
|
# @param args [Array<Object>] the Arguments with which the method was invoked
|
49
50
|
def apply_trigger_rule trigger_node, object, ret, args
|
50
51
|
return if trigger_node.nil?
|
52
|
+
return if event_limit_for_rule?(trigger_node.rule_id)
|
51
53
|
|
52
54
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
53
55
|
# return if there is no context and the flag is set to default => false
|
@@ -102,6 +104,12 @@ module Contrast
|
|
102
104
|
request = find_request(source)
|
103
105
|
return unless reportable?(request&.env)
|
104
106
|
|
107
|
+
process_reportable_finding(trigger_node, source, object, ret, request, *args)
|
108
|
+
rescue StandardError => e
|
109
|
+
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
110
|
+
end
|
111
|
+
|
112
|
+
def process_reportable_finding trigger_node, source, object, ret, request, *args
|
105
113
|
if Contrast::Agent::Reporter.enabled?
|
106
114
|
handle_new_finding(trigger_node, source, object, ret, request, *args)
|
107
115
|
else # TODO: RUBY-1438 -- remove
|
@@ -112,8 +120,6 @@ module Contrast
|
|
112
120
|
rule: trigger_node.rule_id)
|
113
121
|
report_finding(finding, request)
|
114
122
|
end
|
115
|
-
rescue StandardError => e
|
116
|
-
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
117
123
|
end
|
118
124
|
|
119
125
|
# Given a finding, append it to an activity message and send it to the Service for processing. If an
|
@@ -12,6 +12,7 @@ module Contrast
|
|
12
12
|
# have tightly coupled dependencies on each other.
|
13
13
|
class Tracker
|
14
14
|
PROPERTIES_HASH = Contrast::Agent::Assess::Finalizers::Hash.new
|
15
|
+
KEEP_AGE = 600_000.cs__freeze # 10 minutes
|
15
16
|
|
16
17
|
class << self
|
17
18
|
# Retrieve the properties of the given Object, iff they exist.
|
@@ -55,6 +56,17 @@ module Contrast
|
|
55
56
|
def copy source, target
|
56
57
|
PROPERTIES_HASH[target] ||= properties(source).dup
|
57
58
|
end
|
59
|
+
|
60
|
+
# Clean PROPERTIES_HASH of any values older than KEEP_AGE ms or
|
61
|
+
# have nil properties
|
62
|
+
def cleanup!
|
63
|
+
PROPERTIES_HASH.delete_if do |_k, properties|
|
64
|
+
return true if properties.nil?
|
65
|
+
return false unless (event = properties&.event)
|
66
|
+
|
67
|
+
KEEP_AGE <= (Contrast::Utils::Timer.now_ms - event.time)
|
68
|
+
end
|
69
|
+
end
|
58
70
|
end
|
59
71
|
end
|
60
72
|
end
|
@@ -18,8 +18,8 @@ module Contrast
|
|
18
18
|
# @return [Array<Contrast::Agent::Reporting::LibraryDiscovery>] direct report form of
|
19
19
|
# Gem::Specification that have been loaded for this application.
|
20
20
|
def library_pb_list
|
21
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.
|
22
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.analyze_libraries
|
21
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.enable
|
22
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless ::Contrast::INVENTORY.analyze_libraries
|
23
23
|
|
24
24
|
loaded_specs.each_with_object([]) do |(_name, spec), reported_lib_list|
|
25
25
|
next unless spec
|
@@ -111,7 +111,7 @@ module Contrast
|
|
111
111
|
|
112
112
|
# We only use this if inventory and library analysis are enabled
|
113
113
|
def enabled?
|
114
|
-
@_enabled = ::Contrast::INVENTORY.
|
114
|
+
@_enabled = ::Contrast::INVENTORY.enable && ::Contrast::INVENTORY.analyze_libraries if @_enabled.nil?
|
115
115
|
@_enabled
|
116
116
|
end
|
117
117
|
end
|
@@ -21,7 +21,7 @@ module Contrast
|
|
21
21
|
DATA_STORE_MARKER = 'data_store'
|
22
22
|
|
23
23
|
def report_data_store _method, _exception, properties, object, _args
|
24
|
-
return unless ::Contrast::INVENTORY.
|
24
|
+
return unless ::Contrast::INVENTORY.enable
|
25
25
|
|
26
26
|
marker = properties[DATA_STORE_MARKER]
|
27
27
|
return unless marker
|
@@ -17,9 +17,9 @@ module Contrast
|
|
17
17
|
#
|
18
18
|
# @param method_policy [Hash]
|
19
19
|
# {
|
20
|
-
# source_node [ Contrast::Agent::
|
21
|
-
# propagation_node [ Contrast::Agent::
|
22
|
-
# trigger_node [ Contrast::Agent::
|
20
|
+
# source_node [ Contrast::Agent::Assess::Policy::SourceNode ]
|
21
|
+
# propagation_node [ Contrast::Agent::Assess::Policy::PropagationNode ]
|
22
|
+
# trigger_node [ Contrast::Agent::Assess::Policy::TriggerNode ]
|
23
23
|
# inventory_node [ Contrast::Agent::Inventory::Policy::TriggerNode ]
|
24
24
|
# protect_node [ Contrast::Agent::Protect::Policy::TriggerNode ]
|
25
25
|
# deadzone_node [ Contrast::Agent::Deadzone::Policy::DeadzoneNode ]
|
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/worker_thread'
|
5
5
|
require 'contrast/agent/reporting/report'
|
6
|
-
require 'contrast/components/logger'
|
7
6
|
require 'contrast/agent/inventory/dependency_usage_analysis'
|
8
7
|
require 'contrast/agent/reporting/reporting_events/poll'
|
9
8
|
require 'contrast/agent/reporting/reporting_events/server_activity'
|
@@ -14,8 +13,6 @@ module Contrast
|
|
14
13
|
# reach out to get the latest settings for this application. It also sends out those messages which do not need to
|
15
14
|
# be associated directly with a request, such as Server Activity and Library Observation.
|
16
15
|
class ReporterHeartbeat < WorkerThread
|
17
|
-
include Contrast::Components::Logger::InstanceMethods
|
18
|
-
|
19
16
|
# TeamServer will mark an application offline after 5 minutes. Sending this every one should be more than enough
|
20
17
|
# to satisfy our goals.
|
21
18
|
REFRESH_INTERVAL_SEC = 60
|
@@ -29,6 +26,7 @@ module Contrast
|
|
29
26
|
polling_events.each do |event|
|
30
27
|
Contrast::Agent.reporter&.send_event(event)
|
31
28
|
end
|
29
|
+
clean_properties
|
32
30
|
sleep(REFRESH_INTERVAL_SEC)
|
33
31
|
end
|
34
32
|
end
|
@@ -56,35 +56,31 @@ module Contrast
|
|
56
56
|
# @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
|
57
57
|
# @param rule [String]
|
58
58
|
def attach_existing existing_attacker_activity, attacker_activity, rule
|
59
|
-
# TODO: RUBY-1663 figure out attack time mapping
|
60
59
|
new_violation = attacker_activity.protection_rules[rule]
|
60
|
+
sample_activity = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity
|
61
61
|
if (previously_violated = existing_attacker_activity.protection_rules[rule])
|
62
|
-
if (new_blocked = new_violation.blocked
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
previously_violated.blocked.samples.concat(new_blocked)
|
62
|
+
if (new_blocked = new_violation.blocked)
|
63
|
+
previously_violated.blocked ||= sample_activity.new
|
64
|
+
previously_violated.blocked.samples.concat(new_blocked.samples) if new_blocked.samples
|
65
|
+
previously_violated.blocked.merge_time_maps(new_blocked.time_map)
|
67
66
|
end
|
68
67
|
|
69
|
-
if (new_exploited = new_violation.exploited
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
previously_violated.exploited.samples.concat(new_exploited)
|
68
|
+
if (new_exploited = new_violation.exploited)
|
69
|
+
previously_violated.exploited ||= sample_activity.new
|
70
|
+
previously_violated.exploited.samples.concat(new_exploited.samples) if new_exploited.samples
|
71
|
+
previously_violated.exploited.merge_time_maps(new_exploited.time_map)
|
74
72
|
end
|
75
73
|
|
76
|
-
if (new_ineffective = new_violation.ineffective
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
previously_violated.ineffective.samples.concat(new_ineffective)
|
74
|
+
if (new_ineffective = new_violation.ineffective)
|
75
|
+
previously_violated.ineffective ||= sample_activity.new
|
76
|
+
previously_violated.ineffective.samples.concat(new_ineffective.samples) if new_ineffective.samples
|
77
|
+
previously_violated.ineffective.merge_time_maps(new_ineffective.time_map)
|
81
78
|
end
|
82
79
|
|
83
|
-
if (new_suspicious = new_violation.suspicious
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
previously_violated.suspicious.samples.concat(new_suspicious)
|
80
|
+
if (new_suspicious = new_violation.suspicious)
|
81
|
+
previously_violated.suspicious ||= sample_activity.new
|
82
|
+
previously_violated.suspicious.samples.concat(new_suspicious.samples) if new_suspicious.samples
|
83
|
+
previously_violated.suspicious.merge_time_maps(new_suspicious.time_map)
|
88
84
|
end
|
89
85
|
else
|
90
86
|
existing_attacker_activity.protection_rules[rule] = new_violation
|
data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/components/logger'
|
5
|
+
require 'contrast/utils/timer'
|
5
6
|
require 'contrast/agent/reporting/reporting_events/application_defend_attack_sample'
|
6
7
|
|
7
8
|
module Contrast
|
@@ -26,7 +27,7 @@ module Contrast
|
|
26
27
|
|
27
28
|
def initialize
|
28
29
|
@samples = []
|
29
|
-
@start_time =
|
30
|
+
@start_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
|
30
31
|
@event_type = :application_defend_attack_sample_activity
|
31
32
|
@time_map = Hash.new { |h, k| h[k] = 0 }
|
32
33
|
super
|
@@ -36,7 +37,7 @@ module Contrast
|
|
36
37
|
{
|
37
38
|
attackTimeMap: time_map,
|
38
39
|
samples: samples.map(&:to_controlled_hash),
|
39
|
-
startTime: @start_time,
|
40
|
+
startTime: @start_time, # Start time in ms.
|
40
41
|
total: 1 # there will only ever be 1 attack sample, until batching is done
|
41
42
|
}
|
42
43
|
end
|
@@ -44,12 +45,34 @@ module Contrast
|
|
44
45
|
# @param attack_result [Contrast::Api::Dtm::AttackResult]
|
45
46
|
def attach_data attack_result
|
46
47
|
attack_result.samples.each do |attack_sample|
|
48
|
+
base_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
|
49
|
+
dmt_time = attack_sample.timestamp_ms.to_i
|
47
50
|
converted = Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
|
48
51
|
samples << converted
|
49
|
-
|
52
|
+
@start_time = if dmt_time.zero?
|
53
|
+
@start_time
|
54
|
+
else
|
55
|
+
dmt_time
|
56
|
+
end
|
57
|
+
attack_second = (@start_time - base_time) / 1000 # in seconds
|
50
58
|
time_map[attack_second] += 1
|
51
59
|
end
|
52
60
|
end
|
61
|
+
|
62
|
+
# This method will merge time_maps of attack samples with same
|
63
|
+
# type.
|
64
|
+
#
|
65
|
+
# @param map [Hash<Integer,Integer>] TimeMap to append to previously_violated rule
|
66
|
+
# samples.
|
67
|
+
# @return time_map [Hash<Integer,Integer>] merged time map with updated occurrences.
|
68
|
+
def merge_time_maps map
|
69
|
+
# If the second is the same (key) if we just merge there won't be a new entry,
|
70
|
+
# so just increase the attack count.
|
71
|
+
map.each_key do |key|
|
72
|
+
@time_map[key] = @time_map.fetch(key, 0) + map[key]
|
73
|
+
end
|
74
|
+
@time_map
|
75
|
+
end
|
53
76
|
end
|
54
77
|
end
|
55
78
|
end
|
@@ -26,12 +26,12 @@ module Contrast
|
|
26
26
|
# event field of Contrast::Agent::Reporting::ReportingEvent
|
27
27
|
# @param response_data [Net::HTTP::Response]
|
28
28
|
def audit_event event, response_data = nil
|
29
|
-
return unless ::Contrast::API.request_audit_requests
|
29
|
+
return unless ::Contrast::API.request_audit_requests || ::Contrast::API.request_audit_responses
|
30
30
|
|
31
31
|
file_name = event.cs__respond_to?(:file_name) ? event.file_name : event.cs__class.cs__name.to_s.downcase
|
32
32
|
data = event.to_controlled_hash.to_json
|
33
33
|
log_data(:request, file_name, data) if data
|
34
|
-
return unless ::Contrast::API.request_audit_responses
|
34
|
+
return unless ::Contrast::API.request_audit_responses
|
35
35
|
|
36
36
|
data = response_data&.body || 'There is no available response'
|
37
37
|
log_data(:response, file_name, data)
|
@@ -94,7 +94,7 @@ module Contrast
|
|
94
94
|
# Retrieves the configuration value if the request audit is enabled
|
95
95
|
# @return [Boolean]
|
96
96
|
def enabled?
|
97
|
-
::Contrast::API.request_audit_enable
|
97
|
+
::Contrast::API.request_audit_enable
|
98
98
|
end
|
99
99
|
|
100
100
|
# The boolean values for the requests and the responses should be taken under
|
@@ -107,13 +107,13 @@ module Contrast
|
|
107
107
|
# Retrieve the configuration value if the audit for requests is enabled
|
108
108
|
# @return [Boolean]
|
109
109
|
def enabled_for_requests?
|
110
|
-
::Contrast::API.request_audit_requests
|
110
|
+
::Contrast::API.request_audit_requests
|
111
111
|
end
|
112
112
|
|
113
113
|
# Retrieve the configuration value if the audit for responses is enabled
|
114
114
|
# @return [Boolean]
|
115
115
|
def enabled_for_responses?
|
116
|
-
::Contrast::API.request_audit_requests
|
116
|
+
::Contrast::API.request_audit_requests
|
117
117
|
end
|
118
118
|
|
119
119
|
# Retrieve the configuration value for the path of the audits
|
@@ -24,7 +24,7 @@ module Contrast
|
|
24
24
|
@app_language = RUBY
|
25
25
|
@app_path = Base64.strict_encode64(Contrast::APP_CONTEXT.path)
|
26
26
|
@app_version = Contrast::APP_CONTEXT.app_version
|
27
|
-
@authorization = Base64.strict_encode64("#{ Contrast::API.
|
27
|
+
@authorization = Base64.strict_encode64("#{ Contrast::API.user_name }:#{ Contrast::API.service_key }")
|
28
28
|
@server_name = Base64.strict_encode64(Contrast::APP_CONTEXT.server_name)
|
29
29
|
@server_path = Base64.strict_encode64(Contrast::APP_CONTEXT.server_path)
|
30
30
|
@server_type = Base64.strict_encode64(Contrast::APP_CONTEXT.server_type)
|
@@ -59,7 +59,7 @@ module Contrast
|
|
59
59
|
|
60
60
|
request = build_request(event)
|
61
61
|
response = connection.request(request)
|
62
|
-
audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
|
62
|
+
audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
|
63
63
|
process_settings_response(response)
|
64
64
|
process_preflight_response(event, response, connection)
|
65
65
|
response
|
@@ -185,7 +185,7 @@ module Contrast
|
|
185
185
|
::Contrast::SETTINGS.build_protect_rules if ::Contrast::PROTECT.enabled?
|
186
186
|
::Contrast::AGENT.reset_ruleset
|
187
187
|
logger.info('Current rule settings:')
|
188
|
-
::Contrast::PROTECT.
|
188
|
+
::Contrast::PROTECT.defend_rules.each { |k, v| logger.info('Protect Rule mode set', rule: k, mode: v.mode) }
|
189
189
|
logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules)
|
190
190
|
end
|
191
191
|
end
|
@@ -43,6 +43,8 @@ module Contrast
|
|
43
43
|
# @return [Contrast::Utils::Timer] when the context was created
|
44
44
|
attr_reader :timer
|
45
45
|
|
46
|
+
attr_accessor :propagation_event_count, :source_event_count
|
47
|
+
|
46
48
|
def initialize rack_request, app_loaded: true
|
47
49
|
with_contrast_scope do
|
48
50
|
# all requests get a timer and hash
|
@@ -68,6 +70,12 @@ module Contrast
|
|
68
70
|
# generic holder for properties that can be set throughout this request
|
69
71
|
@_properties = {}
|
70
72
|
|
73
|
+
# count of propagation events
|
74
|
+
@propagation_event_count = 0
|
75
|
+
|
76
|
+
# count of source events
|
77
|
+
@source_event_count = 0
|
78
|
+
|
71
79
|
if ::Contrast::ASSESS.enabled?
|
72
80
|
@sample_req, @sample_res = Contrast::Utils::Assess::SamplingUtil.instance.sample?(@request)
|
73
81
|
end
|
@@ -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/components/logger'
|
5
4
|
require 'contrast/agent/worker_thread'
|
6
5
|
require 'contrast/agent/reporting/report'
|
7
6
|
|
@@ -10,8 +9,6 @@ module Contrast
|
|
10
9
|
# The ServiceHeartbeat functions to keep the Contrast Service alive and ensure that it maintains this Agent's
|
11
10
|
# ApplicationContext.
|
12
11
|
class ServiceHeartbeat < WorkerThread
|
13
|
-
include Contrast::Components::Logger::InstanceMethods
|
14
|
-
|
15
12
|
# Spec recommends 30 seconds, we're going with 15.
|
16
13
|
REFRESH_INTERVAL_SEC = 15
|
17
14
|
|
@@ -22,7 +19,9 @@ module Contrast
|
|
22
19
|
@_thread = Contrast::Agent::Thread.new do
|
23
20
|
logger.info('Starting heartbeat thread.')
|
24
21
|
loop do
|
22
|
+
logger.info("Queue Size: #{ Contrast::Agent.messaging_queue.queue&.length }")
|
25
23
|
Contrast::Agent.messaging_queue&.send_event_eventually(poll_message)
|
24
|
+
clean_properties
|
26
25
|
sleep(REFRESH_INTERVAL_SEC)
|
27
26
|
end
|
28
27
|
end
|
@@ -24,7 +24,7 @@ module Contrast
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def send_inventory_message
|
27
|
-
return unless ::Contrast::INVENTORY.
|
27
|
+
return unless ::Contrast::INVENTORY.enable
|
28
28
|
|
29
29
|
report = Contrast::Agent::Reporting::ApplicationUpdate.new
|
30
30
|
# This convert here is left as it'll be easier to be replaced when the Library is being changed
|