contrast-agent 6.8.0 → 6.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- 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/rule/response/body_rule.rb +1 -1
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/excluder.rb +1 -1
- data/lib/contrast/agent/middleware.rb +12 -4
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +76 -83
- data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +121 -0
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +2 -0
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +6 -3
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +3 -0
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +3 -0
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +12 -0
- data/lib/contrast/agent/protect/rule/base.rb +21 -7
- data/lib/contrast/agent/protect/rule/base_service.rb +6 -0
- data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +1 -1
- data/lib/contrast/agent/protect/rule/bot_blocker.rb +9 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +8 -7
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +8 -0
- 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/deserialization.rb +2 -2
- data/lib/contrast/agent/protect/rule/no_sqli.rb +24 -2
- data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_input_classification.rb +1 -1
- data/lib/contrast/agent/protect/rule/path_traversal.rb +12 -3
- data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +0 -1
- data/lib/contrast/agent/protect/rule/sqli.rb +10 -13
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +6 -2
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
- data/lib/contrast/agent/protect/rule/xss/reflected_xss_input_classification.rb +1 -1
- data/lib/contrast/agent/protect/rule/xss.rb +9 -0
- data/lib/contrast/agent/protect/rule/xxe.rb +2 -2
- data/lib/contrast/agent/protect/rule.rb +0 -3
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +1 -1
- data/lib/contrast/agent/reporting/attack_result/user_input.rb +0 -1
- data/lib/contrast/agent/reporting/details/details.rb +0 -1
- data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +12 -0
- data/lib/contrast/agent/reporting/report.rb +2 -0
- data/lib/contrast/agent/reporting/reporter.rb +42 -7
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +5 -6
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +24 -7
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +20 -5
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +0 -1
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -0
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -1
- data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -1
- data/lib/contrast/agent/reporting/reporting_events/application_reporting_event.rb +10 -0
- data/lib/contrast/agent/reporting/reporting_events/application_settings.rb +40 -0
- 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/ng_response_extractor.rb +137 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +52 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -4
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +105 -58
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +9 -7
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +143 -49
- data/lib/contrast/agent/reporting/reporting_workers/application_server_worker.rb +46 -0
- data/lib/contrast/agent/reporting/reporting_workers/reporter_heartbeat.rb +51 -0
- data/lib/contrast/agent/reporting/reporting_workers/reporting_workers.rb +14 -0
- data/lib/contrast/agent/reporting/reporting_workers/server_settings_worker.rb +46 -0
- data/lib/contrast/agent/reporting/settings/assess.rb +14 -1
- data/lib/contrast/agent/reporting/settings/assess_rule.rb +18 -0
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +14 -2
- data/lib/contrast/agent/reporting/settings/helpers.rb +9 -0
- data/lib/contrast/agent/reporting/settings/protect.rb +17 -12
- data/lib/contrast/agent/reporting/settings/protect_rule.rb +18 -0
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +40 -3
- 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/sensitive_data_masking.rb +1 -1
- 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/reporting/settings/virtual_patch.rb +56 -0
- data/lib/contrast/agent/reporting/settings/virtual_patch_condition.rb +47 -0
- data/lib/contrast/agent/request.rb +1 -0
- data/lib/contrast/agent/request_context_extend.rb +20 -0
- data/lib/contrast/agent/request_handler.rb +5 -10
- data/lib/contrast/agent/telemetry/base.rb +11 -10
- data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +108 -103
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +1 -1
- data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +46 -6
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +18 -0
- data/lib/contrast/agent_lib/api/init.rb +1 -7
- data/lib/contrast/agent_lib/api/input_tracing.rb +2 -4
- data/lib/contrast/agent_lib/interface.rb +1 -16
- data/lib/contrast/agent_lib/interface_base.rb +52 -39
- data/lib/contrast/agent_lib/return_types/eval_result.rb +2 -2
- 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 +60 -8
- 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 +39 -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 +120 -10
- 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 +2 -2
- data/lib/contrast/config/protect_rules_configuration.rb +7 -6
- 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/array.rb +3 -3
- data/lib/contrast/extension/assess/erb.rb +1 -1
- data/lib/contrast/extension/assess/regexp.rb +2 -2
- data/lib/contrast/logger/aliased_logging.rb +48 -15
- 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 +21 -5
- 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.rb +1 -1
- data/lib/contrast/utils/telemetry_client.rb +2 -3
- data/lib/contrast/utils/timer.rb +16 -0
- data/lib/contrast.rb +3 -1
- data/resources/protect/policy.json +8 -0
- data/ruby-agent.gemspec +6 -2
- metadata +43 -24
- 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/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +0 -96
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +0 -83
- data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +0 -27
- data/lib/contrast/agent/reporting/reporter_heartbeat.rb +0 -53
- data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -36
- data/lib/contrast/agent_lib/api/method_tempering.rb +0 -29
@@ -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
|