contrast-agent 6.8.0 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -5,9 +5,17 @@ require 'contrast/agent/reporting/reporting_events/finding_event_object'
|
|
|
5
5
|
require 'contrast/agent/reporting/reporting_events/finding_event_parent_object'
|
|
6
6
|
require 'contrast/agent/reporting/reporting_events/finding_event_property'
|
|
7
7
|
require 'contrast/agent/reporting/reporting_events/finding_event_signature'
|
|
8
|
-
require 'contrast/agent/reporting/reporting_events/finding_event_source'
|
|
9
8
|
require 'contrast/agent/reporting/reporting_events/finding_event_stack'
|
|
10
9
|
require 'contrast/agent/reporting/reporting_events/finding_event_taint_range'
|
|
10
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_source'
|
|
11
|
+
require 'contrast/agent/assess/contrast_object'
|
|
12
|
+
require 'contrast/utils/assess/tracking_util'
|
|
13
|
+
require 'contrast/utils/class_util'
|
|
14
|
+
require 'contrast/utils/duck_utils'
|
|
15
|
+
require 'contrast/utils/object_share'
|
|
16
|
+
require 'contrast/utils/stack_trace_utils'
|
|
17
|
+
require 'contrast/utils/string_utils'
|
|
18
|
+
require 'contrast/utils/timer'
|
|
11
19
|
require 'contrast/components/logger'
|
|
12
20
|
|
|
13
21
|
module Contrast
|
|
@@ -24,23 +32,39 @@ module Contrast
|
|
|
24
32
|
# TAG, TRIGGER.
|
|
25
33
|
attr_reader :action
|
|
26
34
|
# @return [Array<Contrast::Agent::Reporting::FindingEventObject>] the arguments passed to the method.
|
|
35
|
+
attr_reader :reportable_args
|
|
36
|
+
# @return [Array<Contrast::Agent::Assess::ContrastObject, nil>] the safe representation of the Arguments with
|
|
37
|
+
# which the method was invoked
|
|
27
38
|
attr_reader :args
|
|
28
39
|
# @return [nil] unused.
|
|
29
40
|
attr_reader :code
|
|
30
41
|
# @return [Integer] the id of this event.
|
|
31
42
|
attr_reader :event_id
|
|
32
|
-
# @return [Array<Contrast::Agent::Reporting::
|
|
43
|
+
# @return [Array<Contrast::Agent::Reporting::EventSource>] the source of taint
|
|
33
44
|
attr_reader :event_sources
|
|
45
|
+
# @return [String, nil]
|
|
46
|
+
attr_reader :source_type
|
|
47
|
+
# @return [String, nil]
|
|
48
|
+
attr_reader :source_name
|
|
34
49
|
# @return [nil] unused.
|
|
35
50
|
attr_reader :field_name
|
|
36
51
|
# @return [Contrast::Agent::Reporting::FindingEventObject] the object this method was invoked on.
|
|
52
|
+
attr_reader :reportable_object
|
|
53
|
+
# @return [Contrast::Agent::Request, nil] our wrapper around the Rack::Request for this context
|
|
54
|
+
attr_reader :request
|
|
55
|
+
# @return [Contrast::Agent::Assess::ContrastObject] the safe representation of the Object on which the method
|
|
56
|
+
# was invoked
|
|
37
57
|
attr_reader :object
|
|
38
|
-
#
|
|
58
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventParentObject>] the ids of all the events directly
|
|
39
59
|
# preceding this
|
|
40
60
|
attr_reader :parent_object_ids
|
|
61
|
+
# @return [Contrast::Agent::Assess::Policy::PolicyNode] the node that governs this event.
|
|
62
|
+
attr_reader :policy_node
|
|
41
63
|
# @return [Array<Contrast::Agent::Reporting::FindingEventProperty>]
|
|
42
64
|
attr_reader :properties
|
|
43
65
|
# @return [Contrast::Agent::Reporting::FindingEventObject] the return of the method.
|
|
66
|
+
attr_reader :reportable_ret
|
|
67
|
+
# @return [Contrast::Agent::Assess::ContrastObject] the safe representation of the Return of the invoked method
|
|
44
68
|
attr_reader :ret
|
|
45
69
|
# @return [Contrast::Agent::Reporting::FindingEventSignature] the signature of the method.
|
|
46
70
|
attr_reader :signature
|
|
@@ -49,6 +73,8 @@ module Contrast
|
|
|
49
73
|
# @return [Array<Contrast::Agent::Reporting::FindingEventStack>]
|
|
50
74
|
attr_reader :stack
|
|
51
75
|
# @return [String] comma separated list of descriptions of what's happened to the data
|
|
76
|
+
attr_reader :reportable_tags
|
|
77
|
+
# @return [Hash<Contrast::Agent::Assess::Tag>]
|
|
52
78
|
attr_reader :tags
|
|
53
79
|
# @return [Array<Contrast::Agent::Reporting::FindingEventTaintRange>] the tags and spans of the source that are
|
|
54
80
|
# tracked
|
|
@@ -61,6 +87,70 @@ module Contrast
|
|
|
61
87
|
attr_reader :time
|
|
62
88
|
# @return [String] the type of event; METHOD, PROPAGATION, TAG
|
|
63
89
|
attr_reader :type
|
|
90
|
+
# @return [Array<String>] the execution stack at the time the method for this event was invoked
|
|
91
|
+
attr_reader :stack_trace
|
|
92
|
+
|
|
93
|
+
# Creates new FindingEvent.
|
|
94
|
+
#
|
|
95
|
+
# @param event_data [Contrast::Agent::Assess::Events::EventData]
|
|
96
|
+
# @param source_type [String, nil] the type of this source, from the
|
|
97
|
+
# source_node, or a KEY_TYPE if invoked for a map,
|
|
98
|
+
# @param source_name [String, nil] the name of this source, i.e.
|
|
99
|
+
# the key used to accessed if from a map or nil if a type like,
|
|
100
|
+
# @return [Contrast::Agent::Reporting::FindingEvent]
|
|
101
|
+
def initialize event_data = nil, source_type = nil, source_name = nil
|
|
102
|
+
@event_sources = []
|
|
103
|
+
@stack = []
|
|
104
|
+
@time = Contrast::Utils::Timer.now_ms
|
|
105
|
+
@thread = Thread.current.object_id.to_s
|
|
106
|
+
@event_id = Contrast::Agent::Reporting::FindingEvent.next_atomic_id
|
|
107
|
+
initialize_routine(event_data, source_type, source_name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Init routine to find parents events, capture stack trace and retrieve object, args, ret and properties.
|
|
111
|
+
#
|
|
112
|
+
# @param event_data [Contrast::Agent::Assess::Events::EventData]
|
|
113
|
+
# @param source_type [String, nil] the type of this source, from the
|
|
114
|
+
# source_node, or a KEY_TYPE if invoked for a map,
|
|
115
|
+
# @param source_name [String, nil] the name of this source, i.e.
|
|
116
|
+
# the key used to accessed if from a map or nil if a type like,
|
|
117
|
+
def initialize_routine event_data, source_type = nil, source_name = nil
|
|
118
|
+
return unless event_data&.cs__is_a?(Contrast::Agent::Assess::Events::EventData)
|
|
119
|
+
|
|
120
|
+
# Initialize source event:
|
|
121
|
+
if event_data.policy_node.cs__class == Contrast::Agent::Assess::Policy::SourceNode
|
|
122
|
+
build_source_event(source_type, source_name)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
@policy_node = event_data.policy_node
|
|
126
|
+
@tags = Contrast::Agent::Assess::Tracker.properties(event_data.tagged)&.get_tags
|
|
127
|
+
find_parent_events!(event_data.policy_node, event_data.object, event_data.ret, event_data.args)
|
|
128
|
+
snapshot!(event_data.object, event_data.ret, event_data.args)
|
|
129
|
+
display_params!
|
|
130
|
+
capture_stacktrace!
|
|
131
|
+
stack!
|
|
132
|
+
properties!
|
|
133
|
+
# following methods must be called after snapshot!
|
|
134
|
+
dataflow!
|
|
135
|
+
@signature = Contrast::Agent::Reporting::FindingEventSignature.new(policy_node, args, ret)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# We need this to track the parent id's of events to build up a flow chart of the finding
|
|
139
|
+
@atomic_id = 0
|
|
140
|
+
@atomic_mutex = Mutex.new
|
|
141
|
+
def self.next_atomic_id
|
|
142
|
+
@atomic_mutex.synchronize do
|
|
143
|
+
@atomic_id += 1
|
|
144
|
+
# Rollover things
|
|
145
|
+
rescue StandardError
|
|
146
|
+
@atomic_id = 1
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEvent>]
|
|
151
|
+
def parent_events
|
|
152
|
+
@_parent_events ||= []
|
|
153
|
+
end
|
|
64
154
|
|
|
65
155
|
class << self
|
|
66
156
|
# Find all the events leading up to the given source and return an array of FindingEvents
|
|
@@ -73,21 +163,13 @@ module Contrast
|
|
|
73
163
|
build_events([], props.event) if props.event
|
|
74
164
|
end
|
|
75
165
|
|
|
76
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
77
|
-
# @return [Contrast::Agent::Reporting::FindingEvent]
|
|
78
|
-
def convert event
|
|
79
|
-
report = new
|
|
80
|
-
report.attach_data(event)
|
|
81
|
-
report
|
|
82
|
-
end
|
|
83
|
-
|
|
84
166
|
private
|
|
85
167
|
|
|
86
168
|
# Pull the parent events from the given event, generating an array of FindingEvents, passing back the given
|
|
87
169
|
# array of events after populating it.
|
|
88
170
|
#
|
|
89
171
|
# @param events [Array<Contrast::Agent::Reporting::FindingEvent>]
|
|
90
|
-
# @param event [Contrast::Agent::
|
|
172
|
+
# @param event [Contrast::Agent::Reporting::FindingEvent]
|
|
91
173
|
# @return [Array<Contrast::Agent::Reporting::FindingEvent>]
|
|
92
174
|
def build_events events, event
|
|
93
175
|
return unless event
|
|
@@ -95,32 +177,11 @@ module Contrast
|
|
|
95
177
|
event.parent_events&.each do |parent_event|
|
|
96
178
|
build_events(events, parent_event)
|
|
97
179
|
end
|
|
98
|
-
events <<
|
|
180
|
+
events << event
|
|
99
181
|
events
|
|
100
182
|
end
|
|
101
183
|
end
|
|
102
184
|
|
|
103
|
-
def initialize
|
|
104
|
-
@event_sources = []
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Parse the data from a Contrast::Agent::Assess::ContrastEvent to attach what is required for reporting to
|
|
108
|
-
# TeamServer to this Contrast::Agent::Reporting::FindingEvent
|
|
109
|
-
#
|
|
110
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
111
|
-
def attach_data event
|
|
112
|
-
@event_id = event.event_id
|
|
113
|
-
@time = event.time.to_i
|
|
114
|
-
@thread = event.thread.to_s
|
|
115
|
-
display_params!(event)
|
|
116
|
-
dataflow!(event)
|
|
117
|
-
event_sources!(event)
|
|
118
|
-
@signature = Contrast::Agent::Reporting::FindingEventSignature.convert(event)
|
|
119
|
-
stack!(event)
|
|
120
|
-
parent_ids!(event)
|
|
121
|
-
properties!(event)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
185
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
125
186
|
# TeamServer to process the JSON form of this message.
|
|
126
187
|
#
|
|
@@ -136,19 +197,19 @@ module Contrast
|
|
|
136
197
|
|
|
137
198
|
{
|
|
138
199
|
action: action,
|
|
139
|
-
args:
|
|
200
|
+
args: reportable_args.map(&:to_controlled_hash),
|
|
140
201
|
# code: code, # Unused by our agent
|
|
141
202
|
objectId: event_id,
|
|
142
203
|
eventSources: event_sources.map(&:to_controlled_hash),
|
|
143
204
|
# fieldName: field_name, # Unused by our agent
|
|
144
|
-
object:
|
|
205
|
+
object: reportable_object.to_controlled_hash,
|
|
145
206
|
parentObjectIds: parent_object_ids.map(&:to_controlled_hash),
|
|
146
207
|
properties: properties.map(&:to_controlled_hash),
|
|
147
|
-
ret:
|
|
208
|
+
ret: reportable_ret&.to_controlled_hash,
|
|
148
209
|
signature: signature.to_controlled_hash,
|
|
149
210
|
source: source || '',
|
|
150
211
|
stack: stack.map(&:to_controlled_hash),
|
|
151
|
-
tags:
|
|
212
|
+
tags: reportable_tags.join(','),
|
|
152
213
|
taintRanges: taint_ranges.map(&:to_controlled_hash),
|
|
153
214
|
target: target || '',
|
|
154
215
|
thread: thread,
|
|
@@ -165,13 +226,22 @@ module Contrast
|
|
|
165
226
|
|
|
166
227
|
private
|
|
167
228
|
|
|
229
|
+
# @param source_type [String, nil] the type of this source, from the
|
|
230
|
+
# source_node, or a KEY_TYPE if invoked for a map,
|
|
231
|
+
# @param source_name [String, nil] the name of this source, i.e.
|
|
232
|
+
# the key used to accessed if from a map or nil if a type like,
|
|
233
|
+
def build_source_event source_type = nil, source_name = nil
|
|
234
|
+
@source_type = Contrast::Utils::StringUtils.force_utf8(source_type)
|
|
235
|
+
@source_name = Contrast::Utils::StringUtils.force_utf8(source_name)
|
|
236
|
+
@event_sources << Contrast::Agent::Reporting::FindingEventSource.new(@source_type, @source_name)
|
|
237
|
+
@request = Contrast::Agent::REQUEST_TRACKER.current&.request
|
|
238
|
+
end
|
|
239
|
+
|
|
168
240
|
# Find the action and type of this event, used by TeamServer to display type of the event and the
|
|
169
241
|
# transformation if made.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
@action = event.policy_node.build_action
|
|
174
|
-
@type = case event.policy_node.node_type
|
|
242
|
+
def display_params!
|
|
243
|
+
@action = policy_node.build_action
|
|
244
|
+
@type = case policy_node.node_type
|
|
175
245
|
when :TYPE_TAG
|
|
176
246
|
'TAG'
|
|
177
247
|
when :TYPE_PROPAGATION
|
|
@@ -182,65 +252,59 @@ module Contrast
|
|
|
182
252
|
end
|
|
183
253
|
|
|
184
254
|
# Build the dataflow components of this FindingEvent.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def dataflow! event
|
|
188
|
-
taint_target = taint_target!(event)
|
|
255
|
+
def dataflow!
|
|
256
|
+
taint_target = taint_target!
|
|
189
257
|
truncate_obj = Contrast::Utils::ObjectShare::OBJECT_KEY != taint_target
|
|
190
|
-
@
|
|
258
|
+
@reportable_object = Contrast::Agent::Reporting::FindingEventObject.convert(@object, truncate_obj)
|
|
191
259
|
truncate_ret = Contrast::Utils::ObjectShare::RETURN_KEY != taint_target
|
|
192
|
-
@
|
|
193
|
-
event_args!(
|
|
194
|
-
taint_ranges!
|
|
260
|
+
@reportable_ret = Contrast::Agent::Reporting::FindingEventObject.convert(@ret, truncate_ret)
|
|
261
|
+
event_args!(taint_target)
|
|
262
|
+
taint_ranges!
|
|
195
263
|
end
|
|
196
264
|
|
|
197
265
|
# Convert the arguments of the given ContrastEvent to the reportable form for this FindingEvent. We'll
|
|
198
266
|
# truncate any argument that isn't the taint target of this event.
|
|
199
267
|
#
|
|
200
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
201
268
|
# @param taint_target [String,nil] the target of the taint in this event.
|
|
202
|
-
def event_args!
|
|
203
|
-
@
|
|
269
|
+
def event_args!taint_target
|
|
270
|
+
@reportable_args = []
|
|
271
|
+
return unless args
|
|
272
|
+
|
|
204
273
|
idx = 0
|
|
205
|
-
while idx <
|
|
206
|
-
@
|
|
274
|
+
while idx < args.length
|
|
275
|
+
@reportable_args << Contrast::Agent::Reporting::FindingEventObject.convert(args[idx], taint_target != idx)
|
|
207
276
|
idx += 1
|
|
208
277
|
end
|
|
209
278
|
end
|
|
210
279
|
|
|
211
|
-
# Convert the sources of the event to sources here
|
|
212
|
-
#
|
|
213
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
214
|
-
def event_sources! event
|
|
215
|
-
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
|
216
|
-
|
|
217
|
-
event_sources << event.event_source if event.event_source
|
|
218
|
-
end
|
|
219
|
-
|
|
220
280
|
# Convert the parent id's of the given ContrastEvent to the reportable form for this FindingEvent.
|
|
221
|
-
|
|
222
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
223
|
-
def parent_ids! event
|
|
281
|
+
def parent_ids!
|
|
224
282
|
@parent_object_ids = []
|
|
225
|
-
|
|
283
|
+
parent_events&.each do |parent_event|
|
|
226
284
|
parent_object_ids << Contrast::Agent::Reporting::FindingEventParentObject.new(parent_event.event_id.to_i)
|
|
227
285
|
end
|
|
228
286
|
end
|
|
229
287
|
|
|
230
|
-
#
|
|
288
|
+
# A set of properties that pertain to this event. This is not required for all rules.
|
|
289
|
+
# Dataflow rules can safely omit this property from their report. The properties are set
|
|
290
|
+
# by properties based rules on build_evidence method ( evidence - legacy, now new rules use properties).
|
|
291
|
+
# It returns a key-value pair of properties on each violated rule. The build finding does not create event,
|
|
292
|
+
# since it checks for violation and if there is a violation in the response it is reported, and properties
|
|
293
|
+
# are populated only for Contrast::Agent::Reporting::Finding.
|
|
231
294
|
# TODO: RUBY-99999 How do properties get to events? Do they? I thought Stored-XSS needed it.
|
|
232
295
|
#
|
|
233
|
-
# @
|
|
234
|
-
def properties!
|
|
296
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventProperty>]
|
|
297
|
+
def properties!
|
|
235
298
|
@properties = []
|
|
236
299
|
end
|
|
237
300
|
|
|
238
|
-
#
|
|
301
|
+
# Capture stack traces only as configured. We'll use this to grab the start of the call stack as if the
|
|
302
|
+
# instrumented method were the caller. This means we'll start at the entry just after the first block of
|
|
303
|
+
# Contrast code.
|
|
239
304
|
#
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
event.stack_trace.compact.map! do |stack_event|
|
|
305
|
+
def stack!
|
|
306
|
+
@stack = if stack_trace
|
|
307
|
+
stack_trace.compact.map! do |stack_event|
|
|
244
308
|
Contrast::Agent::Reporting::FindingEventStack.new(stack_event)
|
|
245
309
|
end
|
|
246
310
|
else
|
|
@@ -249,13 +313,13 @@ module Contrast
|
|
|
249
313
|
end
|
|
250
314
|
|
|
251
315
|
# Convert the taint ranges of the given ContrastEvent to the reportable form for this FindingEvent.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def taint_ranges! event
|
|
255
|
-
@tags = []
|
|
316
|
+
def taint_ranges!
|
|
317
|
+
@reportable_tags = []
|
|
256
318
|
@taint_ranges = []
|
|
257
|
-
|
|
258
|
-
|
|
319
|
+
return unless tags
|
|
320
|
+
|
|
321
|
+
tags.each_pair do |tag_key, tag_ranges|
|
|
322
|
+
reportable_tags << tag_key
|
|
259
323
|
tag_ranges.each do |range|
|
|
260
324
|
taint_ranges << Contrast::Agent::Reporting::FindingEventTaintRange.convert(range)
|
|
261
325
|
end
|
|
@@ -264,16 +328,98 @@ module Contrast
|
|
|
264
328
|
|
|
265
329
|
# Find the source and target for this FindingEvent based on the provided event.
|
|
266
330
|
#
|
|
267
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
268
331
|
# @return [String,nil] the target of the taint in this event.
|
|
269
|
-
def taint_target!
|
|
270
|
-
return unless
|
|
332
|
+
def taint_target!
|
|
333
|
+
return unless policy_node
|
|
334
|
+
|
|
335
|
+
@source = policy_node.source_string if policy_node.source_string
|
|
336
|
+
@target = policy_node.target_string if policy_node.target_string
|
|
337
|
+
return policy_node.targets[0] if policy_node.targets&.any?
|
|
338
|
+
|
|
339
|
+
policy_node.sources[0] if policy_node.sources&.any?
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Parent events are the events of all the sources of this event which were tracked prior to this event
|
|
343
|
+
# occurring. Depending on which, if any of the sources were tracked, there may be more than one parent.
|
|
344
|
+
#
|
|
345
|
+
# All events except for [Contrast::Agent::Assess::Events::SourceEvent] will have at least one parent.
|
|
346
|
+
#
|
|
347
|
+
# We set those events to this event's instance variables.
|
|
348
|
+
#
|
|
349
|
+
# @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode] the node that governs this event.
|
|
350
|
+
# @param object [Object] the Object on which the method was invoked
|
|
351
|
+
# @param ret [Object] the Return of the invoked method
|
|
352
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
353
|
+
# @return event [Contrast::Agent::Reporting::FindingEvent]
|
|
354
|
+
def find_parent_events! policy_node, object, ret, args
|
|
355
|
+
policy_node.sources.each do |source_marker|
|
|
356
|
+
source = value_of_source(source_marker, object, ret, args)
|
|
357
|
+
next unless source
|
|
271
358
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
359
|
+
event = Contrast::Agent::Assess::Tracker.properties(source)&.event
|
|
360
|
+
parent_events << event if event
|
|
361
|
+
end
|
|
362
|
+
parent_ids!
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# @param source [String] the marker for the source type
|
|
366
|
+
# @param object [Object] the Object on which the method was invoked
|
|
367
|
+
# @param ret [Object] the Return of the invoked method
|
|
368
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
369
|
+
# @return [Object,nil] the literal value of the source indicated by the given marker
|
|
370
|
+
def value_of_source source, object, ret, args
|
|
371
|
+
case source
|
|
372
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
373
|
+
object
|
|
374
|
+
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
375
|
+
ret
|
|
376
|
+
else
|
|
377
|
+
args[source]
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Everything* is mutable in Ruby. As such, to ensure we can accurately report the application state at the time
|
|
382
|
+
# of this method's invocation, we have to snapshot the given values, making safe representations of them for
|
|
383
|
+
# our later use. We set those safe values to this event's instance variables.
|
|
384
|
+
#
|
|
385
|
+
# @param object [Object] the Object on which the method was invoked
|
|
386
|
+
# @param ret [Object] the Return of the invoked method
|
|
387
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
388
|
+
def snapshot! object, ret, args
|
|
389
|
+
@object = Contrast::Agent::Assess::ContrastObject.new(object) if object
|
|
390
|
+
@ret = Contrast::Agent::Assess::ContrastObject.new(ret) if ret
|
|
391
|
+
@args = safe_args_representation(args)
|
|
392
|
+
self
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Given an array of arguments, copy them into a safe, meaning String, format that we can use to send to SR and
|
|
396
|
+
# TS for rendering.
|
|
397
|
+
#
|
|
398
|
+
# @param args [Array<Object>] the arguments to translate
|
|
399
|
+
# @return [Array<Contrast::Agent::Assess::ContrastObject>] the String forms of those Objects, as determined by
|
|
400
|
+
# Contrast::Utils::ClassUtil.to_contrast_string
|
|
401
|
+
def safe_args_representation args
|
|
402
|
+
return unless args
|
|
403
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
|
|
404
|
+
|
|
405
|
+
args.map { |arg| arg ? Contrast::Agent::Assess::ContrastObject.new(arg) : nil }
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Capture stack traces only as configured. We'll use this to grab the start of the call stack as if the
|
|
409
|
+
# instrumented method were the caller. This means we'll start at the entry just after the first block of
|
|
410
|
+
# Contrast code.
|
|
411
|
+
def capture_stacktrace!
|
|
412
|
+
# If we're configured to not capture the stacktrace, usually for performance reasons, then don't and return an
|
|
413
|
+
# empty array instead
|
|
414
|
+
unless ::Contrast::ASSESS.capture_stacktrace?(policy_node)
|
|
415
|
+
@stack_trace = Contrast::Utils::ObjectShare::EMPTY_ARRAY
|
|
416
|
+
return
|
|
417
|
+
end
|
|
275
418
|
|
|
276
|
-
|
|
419
|
+
# Otherwise, find where in the stack the application / Ruby code starts
|
|
420
|
+
start = caller(0, 20)&.find_index { |stack| !stack.include?('/lib/contrast') }
|
|
421
|
+
# And then use that to build out the reported stacktrace, or a fallback if we couldn't find it.
|
|
422
|
+
@stack_trace = start ? caller(start + 1, 20) : caller(20, 20)
|
|
277
423
|
end
|
|
278
424
|
|
|
279
425
|
# @raise [ArgumentError]
|
|
@@ -291,11 +437,11 @@ module Contrast
|
|
|
291
437
|
raise(ArgumentError, "#{ self } did not have a proper parentObjectIds. Unable to continue.")
|
|
292
438
|
end
|
|
293
439
|
raise(ArgumentError, "#{ self } did not have a proper taintRanges. Unable to continue.") unless taint_ranges
|
|
294
|
-
raise(ArgumentError, "#{ self } did not have a proper args. Unable to continue.") unless
|
|
295
|
-
raise(ArgumentError, "#{ self } did not have a proper object. Unable to continue.") unless
|
|
440
|
+
raise(ArgumentError, "#{ self } did not have a proper args. Unable to continue.") unless reportable_args
|
|
441
|
+
raise(ArgumentError, "#{ self } did not have a proper object. Unable to continue.") unless reportable_object
|
|
296
442
|
raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.") unless signature
|
|
297
443
|
raise(ArgumentError, "#{ self } did not have a proper stack. Unable to continue.") unless stack
|
|
298
|
-
raise(ArgumentError, "#{ self } did not have a proper tags. Unable to continue.") unless
|
|
444
|
+
raise(ArgumentError, "#{ self } did not have a proper tags. Unable to continue.") unless reportable_tags
|
|
299
445
|
end
|
|
300
446
|
end
|
|
301
447
|
end
|
|
@@ -36,38 +36,25 @@ module Contrast
|
|
|
36
36
|
# @return [Boolean] if the method is void or not; may be different for each invocation of the method.
|
|
37
37
|
attr_reader :void_method
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Parse the data from a Contrast::Agent::Assess::ContrastEvent to attach what is required for reporting to
|
|
50
|
-
# TeamServer to this Contrast::Agent::Reporting::FindingEventSignature
|
|
51
|
-
#
|
|
52
|
-
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
53
|
-
def attach_data event
|
|
54
|
-
node = event.policy_node
|
|
39
|
+
# @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode] the node that governs this event.
|
|
40
|
+
# @param args[Array<Contrast::Agent::Assess::ContrastObject, nil>] the safe representation of the Arguments with
|
|
41
|
+
# which the method was invoked
|
|
42
|
+
# @param ret [Contrast::Agent::Assess::ContrastObject] the safe representation of the Return of the
|
|
43
|
+
# invoked method
|
|
44
|
+
def initialize policy_node, args, ret
|
|
45
|
+
node = policy_node
|
|
55
46
|
@arg_types = []
|
|
56
|
-
|
|
57
|
-
arg_types << type_name(arg)
|
|
58
|
-
end
|
|
47
|
+
args&.each { |arg| arg_types << type_name(arg) }
|
|
59
48
|
@class_name = node.class_name
|
|
60
49
|
@constructor = node.method_name == :new || node.method_name == :initialize
|
|
61
50
|
# 8 is STATIC in Java... we have to placate them for now it has been requested that flags be removed since it
|
|
62
51
|
# isn't used
|
|
63
52
|
@flags = 8 unless node.instance_method?
|
|
64
53
|
@method_name = node.method_name.to_s
|
|
65
|
-
@return_type = type_name(
|
|
54
|
+
@return_type = type_name(ret)
|
|
66
55
|
# if there's a ret, then this method isn't nil. not 100% full proof since you can return nil, but this is the
|
|
67
56
|
# best we've got currently.
|
|
68
|
-
@void_method =
|
|
69
|
-
event.ret.object.nil? ||
|
|
70
|
-
event.ret.object == Contrast::Utils::ObjectShare::NIL_STRING
|
|
57
|
+
@void_method = ret.nil? || ret.object.nil? || ret.object == Contrast::Utils::ObjectShare::NIL_STRING
|
|
71
58
|
end
|
|
72
59
|
|
|
73
60
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
@@ -15,15 +15,15 @@ module Contrast
|
|
|
15
15
|
include Contrast::Components::Logger::InstanceMethods
|
|
16
16
|
|
|
17
17
|
# @return [String] the name of the source
|
|
18
|
-
attr_reader :
|
|
18
|
+
attr_reader :source_name
|
|
19
19
|
# @return [String] the type of the source
|
|
20
|
-
attr_reader :
|
|
20
|
+
attr_reader :source_type
|
|
21
21
|
|
|
22
22
|
# @param type [String]
|
|
23
23
|
# @param name [String]
|
|
24
24
|
def initialize type, name
|
|
25
|
-
@
|
|
26
|
-
@
|
|
25
|
+
@source_type = type
|
|
26
|
+
@source_name = name
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
@@ -40,8 +40,8 @@ module Contrast
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
{
|
|
43
|
-
sourceName:
|
|
44
|
-
sourceType:
|
|
43
|
+
sourceName: source_name,
|
|
44
|
+
sourceType: source_type
|
|
45
45
|
}
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -58,14 +58,15 @@ module Contrast
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
{
|
|
61
|
-
name:
|
|
62
|
-
type:
|
|
61
|
+
name: source_name,
|
|
62
|
+
type: source_type
|
|
63
63
|
}
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# @raise [ArgumentError]
|
|
67
67
|
def validate
|
|
68
|
-
raise(ArgumentError, "#{ self } did not have a proper type. Unable to continue.") unless
|
|
68
|
+
raise(ArgumentError, "#{ self } did not have a proper type. Unable to continue.") unless source_type &&
|
|
69
|
+
!source_type.empty?
|
|
69
70
|
end
|
|
70
71
|
end
|
|
71
72
|
end
|
|
@@ -1,6 +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 'digest'
|
|
4
5
|
require 'json'
|
|
5
6
|
require 'contrast/components/logger'
|
|
6
7
|
require 'contrast/agent/reporting/reporting_events/application_reporting_event'
|
|
@@ -63,6 +64,17 @@ module Contrast
|
|
|
63
64
|
}.compact
|
|
64
65
|
end
|
|
65
66
|
|
|
67
|
+
# Generates a hash id for the observed route, based on the sources, signature, verb, and url.
|
|
68
|
+
#
|
|
69
|
+
# @return [String]
|
|
70
|
+
#
|
|
71
|
+
def hash_id
|
|
72
|
+
hashable_data = to_controlled_hash.except(:session_id)
|
|
73
|
+
hashable_data[:sources] = hashable_data[:sources].sort_by { |s| s[:name] }
|
|
74
|
+
|
|
75
|
+
Digest::SHA2.new(256).hexdigest(hashable_data.to_s)
|
|
76
|
+
end
|
|
77
|
+
|
|
66
78
|
# @raise [ArgumentError]
|
|
67
79
|
def validate
|
|
68
80
|
raise(ArgumentError, "#{ self } did not have a proper sources. Unable to continue.") if @sources.nil?
|
|
@@ -21,6 +21,14 @@ module Contrast
|
|
|
21
21
|
def since_last_update
|
|
22
22
|
(update_time = Contrast::SETTINGS.last_server_update_ms) ? Contrast::Utils::Timer.now_ms - update_time : 0
|
|
23
23
|
end
|
|
24
|
+
|
|
25
|
+
# Human readable last time update for header set. Set to 0 if the agent is just starting and have not received
|
|
26
|
+
# the latest header from TS.
|
|
27
|
+
#
|
|
28
|
+
# @return [String]
|
|
29
|
+
def since_last_update_httpdate
|
|
30
|
+
Contrast::SETTINGS.server_settings_last_httpdate || Contrast::Utils::Timer.ms_to_httpdate(0)
|
|
31
|
+
end
|
|
24
32
|
end
|
|
25
33
|
end
|
|
26
34
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
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/reporting/reporting_events/server_reporting_event'
|
|
5
|
+
require 'contrast/agent/reporting/reporting_utilities/endpoints'
|
|
6
|
+
require 'contrast/utils/timer'
|
|
7
|
+
|
|
8
|
+
module Contrast
|
|
9
|
+
module Agent
|
|
10
|
+
module Reporting
|
|
11
|
+
# This class will initialize a GET request to be send to TS. The server settings endpoint is the way the Agent
|
|
12
|
+
# receives server sittings - Protect rules settings, patterns, keywords and deny/allow lists, log setting.
|
|
13
|
+
class ServerSettings < Contrast::Agent::Reporting::ServerReportingEvent
|
|
14
|
+
def initialize
|
|
15
|
+
@event_method = :GET
|
|
16
|
+
@event_endpoint = Contrast::Agent::Reporting::Endpoints.server_settings
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def file_name
|
|
21
|
+
'server-settings'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Attach the last server settings received timestamp to the request as it is required.
|
|
25
|
+
#
|
|
26
|
+
# If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
|
27
|
+
# @param request [Net::HTTPRequest]
|
|
28
|
+
def attach_headers request
|
|
29
|
+
request['If-Modified-Since'] = since_last_update_httpdate
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Hash]
|
|
33
|
+
# @raise [ArgumentError]
|
|
34
|
+
def to_controlled_hash
|
|
35
|
+
{}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|