contrast-agent 5.1.0 → 5.2.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/ext/cs__assess_kernel/cs__assess_kernel.c +7 -4
- data/ext/cs__assess_module/cs__assess_module.c +7 -7
- data/ext/cs__common/cs__common.c +4 -0
- data/ext/cs__common/cs__common.h +1 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +52 -27
- data/ext/cs__contrast_patch/cs__contrast_patch.h +2 -0
- data/ext/cs__scope/cs__scope.c +747 -0
- data/ext/cs__scope/cs__scope.h +88 -0
- data/ext/cs__scope/extconf.rb +5 -0
- data/lib/contrast/agent/assess/contrast_event.rb +20 -13
- data/lib/contrast/agent/assess/contrast_object.rb +4 -1
- data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -5
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -1
- data/lib/contrast/agent/assess/rule/response/{autocomplete_rule.rb → auto_complete_rule.rb} +4 -3
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +12 -79
- data/lib/contrast/agent/assess/rule/response/body_rule.rb +109 -0
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +157 -0
- data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +26 -0
- data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +14 -15
- data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +5 -25
- data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +29 -0
- data/lib/contrast/agent/assess/rule/response/header_rule.rb +70 -0
- data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +12 -36
- data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +2 -1
- data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +26 -0
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +36 -0
- data/lib/contrast/agent/middleware.rb +1 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +1 -3
- data/lib/contrast/agent/patching/policy/patch.rb +2 -6
- data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +94 -0
- data/lib/contrast/agent/protect/rule/base.rb +28 -1
- data/lib/contrast/agent/protect/rule/base_service.rb +10 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +2 -0
- data/lib/contrast/agent/protect/rule/deserialization.rb +6 -0
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +5 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -0
- data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -0
- data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +124 -0
- data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +121 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +33 -0
- data/lib/contrast/agent/protect/rule/xxe.rb +4 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +44 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +115 -0
- data/lib/contrast/agent/reporting/input_analysis/input_type.rb +44 -0
- data/lib/contrast/agent/reporting/input_analysis/score_level.rb +21 -0
- data/lib/contrast/agent/reporting/report.rb +1 -0
- data/lib/contrast/agent/reporting/reporter.rb +8 -1
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +69 -36
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +88 -59
- data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb} +24 -20
- data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +39 -0
- data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +40 -0
- data/lib/contrast/agent/reporting/reporting_events/{finding_signature.rb → finding_event_signature.rb} +29 -24
- data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +12 -8
- data/lib/contrast/agent/reporting/reporting_events/{finding_stack.rb → finding_event_stack.rb} +23 -19
- data/lib/contrast/agent/reporting/reporting_events/{finding_taint_range.rb → finding_event_taint_range.rb} +17 -15
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +26 -53
- data/lib/contrast/agent/reporting/reporting_events/poll.rb +29 -0
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +5 -4
- data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
- data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +0 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +28 -20
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +13 -1
- data/lib/contrast/agent/request_context.rb +6 -1
- data/lib/contrast/agent/request_context_extend.rb +85 -21
- data/lib/contrast/agent/scope.rb +102 -107
- data/lib/contrast/agent/service_heartbeat.rb +45 -2
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/decorators/bot_blocker.rb +37 -0
- data/lib/contrast/api/decorators/ip_denylist.rb +37 -0
- data/lib/contrast/api/decorators/rasp_rule_sample.rb +29 -0
- data/lib/contrast/api/decorators/user_input.rb +11 -1
- data/lib/contrast/api/decorators/virtual_patch.rb +34 -0
- data/lib/contrast/components/logger.rb +5 -0
- data/lib/contrast/components/protect.rb +4 -2
- data/lib/contrast/components/scope.rb +98 -91
- data/lib/contrast/config/agent_configuration.rb +58 -12
- data/lib/contrast/config/api_configuration.rb +100 -12
- data/lib/contrast/config/api_proxy_configuration.rb +55 -3
- data/lib/contrast/config/application_configuration.rb +114 -15
- data/lib/contrast/config/assess_configuration.rb +106 -12
- data/lib/contrast/config/assess_rules_configuration.rb +44 -3
- data/lib/contrast/config/base_configuration.rb +1 -0
- data/lib/contrast/config/certification_configuration.rb +74 -3
- data/lib/contrast/config/exception_configuration.rb +61 -3
- data/lib/contrast/config/heap_dump_configuration.rb +101 -17
- data/lib/contrast/config/inventory_configuration.rb +64 -3
- data/lib/contrast/config/logger_configuration.rb +46 -3
- data/lib/contrast/config/protect_rule_configuration.rb +36 -9
- data/lib/contrast/config/protect_rules_configuration.rb +120 -17
- data/lib/contrast/config/request_audit_configuration.rb +68 -3
- data/lib/contrast/config/ruby_configuration.rb +96 -22
- data/lib/contrast/config/sampling_configuration.rb +76 -10
- data/lib/contrast/config/server_configuration.rb +56 -11
- data/lib/contrast/configuration.rb +6 -3
- data/lib/contrast/logger/cef_log.rb +151 -0
- data/lib/contrast/utils/hash_digest.rb +14 -6
- data/lib/contrast/utils/log_utils.rb +114 -0
- data/lib/contrast/utils/middleware_utils.rb +6 -7
- data/lib/contrast/utils/net_http_base.rb +12 -9
- data/lib/contrast/utils/patching/policy/patch_utils.rb +0 -4
- data/lib/contrast.rb +4 -3
- data/ruby-agent.gemspec +1 -1
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +41 -21
- data/lib/contrast/agent/assess/rule/response/cachecontrol_rule.rb +0 -184
- data/lib/contrast/agent/assess/rule/response/clickjacking_rule.rb +0 -66
- data/lib/contrast/agent/assess/rule/response/x_content_type_rule.rb +0 -52
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_rule.rb +0 -53
- data/lib/contrast/extension/kernel.rb +0 -54
|
@@ -16,28 +16,42 @@ module Contrast
|
|
|
16
16
|
# relay this information in the Finding/Trace messages. These findings are used by TeamServer to construct the
|
|
17
17
|
# vulnerability information for the assess feature. They represent those parts of the application, either through
|
|
18
18
|
# configuration, method invocation, or dataflow, which are determined to be insecure.
|
|
19
|
-
#
|
|
20
|
-
# @attr_accessor events [Array<Contrast::Agent::Assess::ContrastEvent>] if a dataflow based finding, the
|
|
21
|
-
# representation of those method calls which constitute a dangerous code path.
|
|
22
|
-
# @attr_accessor properties [Hash<String,String>] a set of values that TeamServer can use to provide more context
|
|
23
|
-
# to the user when rendering the finding. For some findings, a specific set of keys and values are required.
|
|
24
|
-
# @attr_accessor request [Contrast::Agent::Request, nil] the request, if any, in which this finding occurred
|
|
25
|
-
# @attr_accessor hash_code [String] the unique identifier of this finding.
|
|
26
|
-
# @attr_reader rule_id [String] the name of the rule violated; must match those in TeamServer.
|
|
27
19
|
class Finding < Contrast::Agent::Reporting::ReportingEvent
|
|
28
|
-
|
|
20
|
+
include Contrast::Components::Logger::InstanceMethods
|
|
21
|
+
|
|
22
|
+
# @return [Integer] the time, in ms, that this object was initialized
|
|
23
|
+
attr_reader :created
|
|
24
|
+
# @return [String] the ID of the rule associated with this finding
|
|
25
|
+
attr_reader :rule_id
|
|
26
|
+
# @return [Array<Contrast::Agent::Reporting::RouteDiscovery>] the routes associated with this finding, if the
|
|
27
|
+
# finding is request based.
|
|
28
|
+
attr_reader :routes
|
|
29
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEvent>] the events associated with this finding, if the
|
|
30
|
+
# finding is event (dataflow) based.
|
|
31
|
+
attr_reader :events
|
|
32
|
+
# @return [String] the evidence associated with this finding, if the finding is event based. deprecated in
|
|
33
|
+
# favor of properties
|
|
34
|
+
# attr_reader :evidence
|
|
35
|
+
# @return [Hash<String,String>] properties that prove the violation of the rule for this finding
|
|
36
|
+
attr_reader :properties
|
|
37
|
+
# @return [Contrast::Agent::Reporting::FindingRequest] the request associated with this finding, if the finding
|
|
38
|
+
# is request based
|
|
39
|
+
attr_reader :request
|
|
40
|
+
# @return [String] the uniquely identifying hash of this finding
|
|
29
41
|
attr_accessor :hash_code
|
|
30
42
|
|
|
43
|
+
CONFIGURATION_RULES = %w[rails-http-only-disabled secure-flag-missing session-timeout].cs__freeze
|
|
44
|
+
HARDCODED_RULES = %w[hardcoded-key hardcoded-password].cs__freeze
|
|
31
45
|
PROPERTIES_RULES = %w[
|
|
32
46
|
autocomplete-missing
|
|
33
47
|
cache-controls-missing
|
|
34
48
|
clickjacking-control-missing
|
|
35
|
-
xcontenttype-header-missing
|
|
36
|
-
hsts-header-missing
|
|
37
|
-
xxssprotection-header-disabled
|
|
38
49
|
csp-header-missing
|
|
39
50
|
csp-header-insecure
|
|
51
|
+
hsts-header-missing
|
|
40
52
|
parameter-pollution
|
|
53
|
+
xcontenttype-header-missing
|
|
54
|
+
xxssprotection-header-disabled
|
|
41
55
|
].cs__freeze
|
|
42
56
|
|
|
43
57
|
class << self
|
|
@@ -57,6 +71,7 @@ module Contrast
|
|
|
57
71
|
@routes = []
|
|
58
72
|
@rule_id = Contrast::Utils::StringUtils.truncate(rule_id)
|
|
59
73
|
@properties = {}
|
|
74
|
+
@created = Contrast::Utils::Timer.now_ms
|
|
60
75
|
super()
|
|
61
76
|
end
|
|
62
77
|
|
|
@@ -76,15 +91,16 @@ module Contrast
|
|
|
76
91
|
# @param request [Contrast::Agent::Request]
|
|
77
92
|
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
78
93
|
def attach_data trigger_node, source, object, ret, request, *args
|
|
94
|
+
event_messages = Contrast::Agent::Reporting::FindingEvent.from_source(source)
|
|
95
|
+
events.concat(event_messages) if event_messages&.any?
|
|
79
96
|
event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
|
|
80
|
-
|
|
81
|
-
events.
|
|
82
|
-
if request
|
|
83
|
-
@request = Contrast::Agent::Reporting::FindingRequest.convert(request)
|
|
84
|
-
@routes << Contrast::Agent::Reporting::RouteDiscovery.convert(request&.route)
|
|
85
|
-
end
|
|
86
|
-
events << Contrast::Agent::Assess::ContrastEvent.new(event_data)
|
|
97
|
+
contrast_event = Contrast::Agent::Assess::ContrastEvent.new(event_data)
|
|
98
|
+
events << Contrast::Agent::Reporting::FindingEvent.convert(contrast_event)
|
|
87
99
|
attach_properties
|
|
100
|
+
return unless request
|
|
101
|
+
|
|
102
|
+
@request = Contrast::Agent::Reporting::FindingRequest.convert(request)
|
|
103
|
+
@routes << Contrast::Agent::Reporting::RouteDiscovery.convert(request.route)
|
|
88
104
|
end
|
|
89
105
|
|
|
90
106
|
# Attach the data from a Contrast::Api::Dtm::Finding required for property based findings generated during
|
|
@@ -92,7 +108,6 @@ module Contrast
|
|
|
92
108
|
#
|
|
93
109
|
# @param finding_dtm [Contrast::Api::Dtm::Finding]
|
|
94
110
|
def attach_property_data finding_dtm
|
|
95
|
-
@created = Contrast::Utils::Timer.now_ms
|
|
96
111
|
@hash_code = finding_dtm.hash_code
|
|
97
112
|
@rule_id = finding_dtm.rule_id
|
|
98
113
|
finding_dtm.properties.each_pair do |key, value|
|
|
@@ -101,7 +116,7 @@ module Contrast
|
|
|
101
116
|
finding_dtm.routes.each do |route|
|
|
102
117
|
@routes << Contrast::Agent::Reporting::RouteDiscovery.convert(route)
|
|
103
118
|
end
|
|
104
|
-
request = Contrast::Agent::REQUEST_TRACKER.current&.
|
|
119
|
+
request = Contrast::Agent::REQUEST_TRACKER.current&.request
|
|
105
120
|
@request = Contrast::Agent::Reporting::FindingRequest.convert(request) if request
|
|
106
121
|
end
|
|
107
122
|
|
|
@@ -113,20 +128,20 @@ module Contrast
|
|
|
113
128
|
def to_controlled_hash
|
|
114
129
|
validate
|
|
115
130
|
hsh = {
|
|
116
|
-
created:
|
|
117
|
-
hash: hash_code,
|
|
118
|
-
ruleId:
|
|
119
|
-
session_id: @agent_session_id_value,
|
|
131
|
+
created: created,
|
|
132
|
+
hash: hash_code.to_s,
|
|
133
|
+
ruleId: rule_id,
|
|
134
|
+
session_id: @agent_session_id_value.to_s,
|
|
120
135
|
version: 4
|
|
121
136
|
}
|
|
122
|
-
hsh[:events] = events if event_based?
|
|
123
|
-
hsh[:evidence] =
|
|
137
|
+
hsh[:events] = events.map(&:to_controlled_hash) if event_based?
|
|
138
|
+
# hsh[:evidence] = evidence unless event_based? || property_based?
|
|
124
139
|
hsh[:properties] = properties if property_based?
|
|
125
140
|
hsh[:tags] = Contrast::ASSESS.tags if Contrast::ASSESS.tags
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
return hsh unless request_based?
|
|
142
|
+
|
|
143
|
+
hsh[:request] = request.to_controlled_hash
|
|
144
|
+
hsh[:routes] = routes.map(&:to_controlled_hash)
|
|
130
145
|
hsh
|
|
131
146
|
end
|
|
132
147
|
|
|
@@ -135,14 +150,14 @@ module Contrast
|
|
|
135
150
|
raise(ArgumentError, "#{ self } did not have a proper rule. Unable to continue.") unless @rule_id
|
|
136
151
|
|
|
137
152
|
if event_based? && events.empty?
|
|
138
|
-
raise(ArgumentError, "#{ self } did not have proper events. Unable to continue.")
|
|
153
|
+
raise(ArgumentError, "#{ self } did not have proper events for #{ @rule_id }. Unable to continue.")
|
|
139
154
|
end
|
|
140
155
|
if property_based? && properties.empty?
|
|
141
|
-
raise(ArgumentError, "#{ self } did not have proper properties. Unable to continue.")
|
|
156
|
+
raise(ArgumentError, "#{ self } did not have proper properties for #{ @rule_id }. Unable to continue.")
|
|
142
157
|
end
|
|
143
158
|
return unless request_based? && request.nil?
|
|
144
159
|
|
|
145
|
-
raise(ArgumentError, "#{ self } did not have a proper request. Unable to continue.")
|
|
160
|
+
raise(ArgumentError, "#{ self } did not have a proper request for #{ @rule_id }. Unable to continue.")
|
|
146
161
|
end
|
|
147
162
|
|
|
148
163
|
private
|
|
@@ -169,7 +184,7 @@ module Contrast
|
|
|
169
184
|
#
|
|
170
185
|
# @return [Boolean]
|
|
171
186
|
def event_based?
|
|
172
|
-
!property_based?
|
|
187
|
+
!property_based? && !config_based?
|
|
173
188
|
end
|
|
174
189
|
|
|
175
190
|
# Rules which are property based must have a property to be sent to TeamServer. Eventually, each rule may own
|
|
@@ -181,13 +196,31 @@ module Contrast
|
|
|
181
196
|
PROPERTIES_RULES.include?(@rule_id)
|
|
182
197
|
end
|
|
183
198
|
|
|
199
|
+
# Rules which are config based must have a configuration to be sent to TeamServer. Eventually, each rule may own
|
|
200
|
+
# its own validation, as the properties each needs are different; however, that's a refactor for after we've
|
|
201
|
+
# translated all rules from the Service and have had time to build proper child structure.
|
|
202
|
+
#
|
|
203
|
+
# @return [Boolean]
|
|
204
|
+
def config_based?
|
|
205
|
+
CONFIGURATION_RULES.include?(@rule_id)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Rules which are hardcode based send properties to TeamServer. Eventually, each rule may own its own
|
|
209
|
+
# validation, as the properties each needs are different; however, that's a refactor for after we've
|
|
210
|
+
# translated all rules from the Service and have had time to build proper child structure.
|
|
211
|
+
#
|
|
212
|
+
# @return [Boolean]
|
|
213
|
+
def hardcoded?
|
|
214
|
+
HARDCODED_RULES.include?(@rule_id)
|
|
215
|
+
end
|
|
216
|
+
|
|
184
217
|
# Rules which are request based must have a request to be sent to TeamServer. Most rules fit this category, so
|
|
185
218
|
# we'll default to true for now. Eventually, this will be split out for those rules, like Hardcoded, which do
|
|
186
219
|
# not need requests.
|
|
187
220
|
#
|
|
188
221
|
# @return [Boolean]
|
|
189
222
|
def request_based?
|
|
190
|
-
|
|
223
|
+
!config_based? && !hardcoded?
|
|
191
224
|
end
|
|
192
225
|
end
|
|
193
226
|
end
|
|
@@ -1,44 +1,63 @@
|
|
|
1
1
|
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_object'
|
|
5
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_parent_object'
|
|
6
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_property'
|
|
7
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_signature'
|
|
4
8
|
require 'contrast/agent/reporting/reporting_events/finding_event_source'
|
|
5
|
-
require 'contrast/agent/reporting/reporting_events/
|
|
6
|
-
require 'contrast/agent/reporting/reporting_events/
|
|
7
|
-
require 'contrast/agent/reporting/reporting_events/finding_taint_range'
|
|
9
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_stack'
|
|
10
|
+
require 'contrast/agent/reporting/reporting_events/finding_event_taint_range'
|
|
8
11
|
|
|
9
12
|
module Contrast
|
|
10
13
|
module Agent
|
|
11
14
|
module Reporting
|
|
12
|
-
# This is the new
|
|
13
|
-
# relay this information in the Finding/Trace messages. These
|
|
14
|
-
# vulnerability information for the assess feature. They represent
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# @attr_reader action [String] what the event did; CREATION, A2O, A2P, A2A, A2R, O2A, O2O, O2P, O2R, P2A, P2O,
|
|
18
|
-
# P2P, P2R, TAG, TRIGGER.
|
|
19
|
-
# @attr_reader args [Array<Contrast::Agent::Reporting::FindingObject>] the arguments passed to the method.
|
|
20
|
-
# @attr_reader code [nil] unused.
|
|
21
|
-
# @attr_reader event_id [Integer] the id of this event.
|
|
22
|
-
# @attr_reader event_sources [Array<Contrast::Agent::Reporting::FindingEventSource>] the source of taint
|
|
23
|
-
# @attr_reader field_name [nil] unused.
|
|
24
|
-
# @attr_reader object [Contrast::Agent::Reporting::FindingEventSource] the object this method was invoked on.
|
|
25
|
-
# @attr_reader parent_object_ids [Array<Integer>]
|
|
26
|
-
# @attr_reader properties [Hash<String,String]
|
|
27
|
-
# @attr_reader ret [Contrast::Agent::Reporting::FindingObject] the return of the method.
|
|
28
|
-
# @attr_reader signature [Contrast::Agent::Reporting::FindingSignature] the signature of the method.
|
|
29
|
-
# @attr_reader source [String] the source of the taint from the method; ^(O|R|P\d+)$
|
|
30
|
-
# @attr_reader stack [Array<Contrast::Agent::Reporting::FindingStack>]
|
|
31
|
-
# @attr_reader tags [Array<String>] description of what's happened to the data
|
|
32
|
-
# @attr_reader taint_ranges [Array<Contrast::Agent::Reporting::FindingTaintRange>] the tags and spans of the
|
|
33
|
-
# source that are tracked
|
|
34
|
-
# @attr_reader target [String] the target of the taint from the method; ^(O|R|P\d+)$
|
|
35
|
-
# @attr_reader thread [String] the id of the thread on which the method was invoked
|
|
36
|
-
# @attr_reader time [Integer] the time, in ms, when the event was generated
|
|
37
|
-
# @attr_reader type [String] the type of event; METHOD, PROPAGATION, TAG
|
|
15
|
+
# This is the new FindingEvent class which will include all the needed information for the new reporting system
|
|
16
|
+
# to relay this information in the Finding/Trace messages. These FindingEvents are used by TeamServer to
|
|
17
|
+
# construct the vulnerability information for the assess feature. They represent the operation the application
|
|
18
|
+
# underwent that transformed data during the dataflow.
|
|
38
19
|
class FindingEvent
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
20
|
+
# @return [Symbol] what the event did; CREATION, A2O, A2P, A2A, A2R, O2A, O2O, O2P, O2R, P2A, P2O, P2P, P2R,
|
|
21
|
+
# TAG, TRIGGER.
|
|
22
|
+
attr_reader :action
|
|
23
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventObject>] the arguments passed to the method.
|
|
24
|
+
attr_reader :args
|
|
25
|
+
# @return [nil] unused.
|
|
26
|
+
attr_reader :code
|
|
27
|
+
# @return [Integer] the id of this event.
|
|
28
|
+
attr_reader :event_id
|
|
29
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventSource>] the source of taint
|
|
30
|
+
attr_reader :event_sources
|
|
31
|
+
# @return [nil] unused.
|
|
32
|
+
attr_reader :field_name
|
|
33
|
+
# @return [Contrast::Agent::Reporting::FindingEventObject] the object this method was invoked on.
|
|
34
|
+
attr_reader :object
|
|
35
|
+
# @@return [Array<Contrast::Agent::Reporting::FindingEventParentObject>] the ids of all the events directly
|
|
36
|
+
# preceding this
|
|
37
|
+
attr_reader :parent_object_ids
|
|
38
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventProperty>]
|
|
39
|
+
attr_reader :properties
|
|
40
|
+
# @return [Contrast::Agent::Reporting::FindingEventObject] the return of the method.
|
|
41
|
+
attr_reader :ret
|
|
42
|
+
# @return [Contrast::Agent::Reporting::FindingEventSignature] the signature of the method.
|
|
43
|
+
attr_reader :signature
|
|
44
|
+
# @return [String] the source of the taint from the method; ^(O|R|P\d+)$
|
|
45
|
+
attr_reader :source
|
|
46
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventStack>]
|
|
47
|
+
attr_reader :stack
|
|
48
|
+
# @return [String] comma separated list of descriptions of what's happened to the data
|
|
49
|
+
attr_reader :tags
|
|
50
|
+
# @return [Array<Contrast::Agent::Reporting::FindingEventTaintRange>] the tags and spans of the source that are
|
|
51
|
+
# tracked
|
|
52
|
+
attr_reader :taint_ranges
|
|
53
|
+
# @return [String] the target of the taint from the method; ^(O|R|P\d+)$
|
|
54
|
+
attr_reader :target
|
|
55
|
+
# @return [String] the id of the thread on which the method was invoked
|
|
56
|
+
attr_reader :thread
|
|
57
|
+
# @return [Integer] the time, in ms, when the event was generated
|
|
58
|
+
attr_reader :time
|
|
59
|
+
# @return [String] the type of event; METHOD, PROPAGATION, TAG
|
|
60
|
+
attr_reader :type
|
|
42
61
|
|
|
43
62
|
class << self
|
|
44
63
|
# Find all the events leading up to the given source and return an array of FindingEvents
|
|
@@ -48,7 +67,7 @@ module Contrast
|
|
|
48
67
|
def from_source source
|
|
49
68
|
return unless source && (props = Contrast::Agent::Assess::Tracker.properties(source))
|
|
50
69
|
|
|
51
|
-
build_events([], props.event) if props
|
|
70
|
+
build_events([], props.event) if props.event
|
|
52
71
|
end
|
|
53
72
|
|
|
54
73
|
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
@@ -71,7 +90,7 @@ module Contrast
|
|
|
71
90
|
return unless event
|
|
72
91
|
|
|
73
92
|
event.parent_events&.each do |parent_event|
|
|
74
|
-
build_events(
|
|
93
|
+
build_events(events, parent_event)
|
|
75
94
|
end
|
|
76
95
|
events << convert(event)
|
|
77
96
|
events
|
|
@@ -85,11 +104,11 @@ module Contrast
|
|
|
85
104
|
def attach_data event
|
|
86
105
|
@event_id = event.event_id
|
|
87
106
|
@time = event.time.to_i
|
|
88
|
-
@thread = event.thread
|
|
107
|
+
@thread = event.thread.to_s
|
|
89
108
|
display_params!(event)
|
|
90
109
|
dataflow!(event)
|
|
91
110
|
event_sources!(event)
|
|
92
|
-
@signature = Contrast::Agent::Reporting::
|
|
111
|
+
@signature = Contrast::Agent::Reporting::FindingEventSignature.convert(event)
|
|
93
112
|
stack!(event)
|
|
94
113
|
parent_ids!(event)
|
|
95
114
|
properties!(event)
|
|
@@ -100,25 +119,25 @@ module Contrast
|
|
|
100
119
|
#
|
|
101
120
|
# @return [Hash]
|
|
102
121
|
# @raise [ArgumentError]
|
|
103
|
-
def to_controlled_hash
|
|
122
|
+
def to_controlled_hash # rubocop:disable Metrics/AbcSize
|
|
104
123
|
validate
|
|
105
124
|
{
|
|
106
125
|
action: action,
|
|
107
|
-
args: args,
|
|
126
|
+
args: args.map(&:to_controlled_hash),
|
|
108
127
|
# code: code, # Unused by our agent
|
|
109
|
-
|
|
110
|
-
eventSources: event_sources,
|
|
128
|
+
objectId: event_id,
|
|
129
|
+
eventSources: event_sources.map(&:to_controlled_hash),
|
|
111
130
|
# fieldName: field_name, # Unused by our agent
|
|
112
|
-
object: object,
|
|
113
|
-
parentObjectIds: parent_object_ids,
|
|
114
|
-
properties: properties,
|
|
115
|
-
ret: ret,
|
|
116
|
-
signature: signature,
|
|
117
|
-
source: source,
|
|
118
|
-
stack: stack,
|
|
119
|
-
tags: tags,
|
|
120
|
-
taintRanges: taint_ranges,
|
|
121
|
-
target: target,
|
|
131
|
+
object: object.to_controlled_hash,
|
|
132
|
+
parentObjectIds: parent_object_ids.map(&:to_controlled_hash),
|
|
133
|
+
properties: properties.map(&:to_controlled_hash),
|
|
134
|
+
ret: ret&.to_controlled_hash,
|
|
135
|
+
signature: signature.to_controlled_hash,
|
|
136
|
+
source: source || '',
|
|
137
|
+
stack: stack.map(&:to_controlled_hash),
|
|
138
|
+
tags: tags.join(','),
|
|
139
|
+
taintRanges: taint_ranges.map(&:to_controlled_hash),
|
|
140
|
+
target: target || '',
|
|
122
141
|
thread: thread,
|
|
123
142
|
time: time,
|
|
124
143
|
type: type
|
|
@@ -139,7 +158,14 @@ module Contrast
|
|
|
139
158
|
# @param event [Contrast::Agent::Assess::ContrastEvent]
|
|
140
159
|
def display_params! event
|
|
141
160
|
@action = event.policy_node.build_action
|
|
142
|
-
@type = event.policy_node.node_type
|
|
161
|
+
@type = case event.policy_node.node_type
|
|
162
|
+
when :TYPE_TAG
|
|
163
|
+
'TAG'
|
|
164
|
+
when :TYPE_PROPAGATION
|
|
165
|
+
'PROPAGATION'
|
|
166
|
+
else # :TYPE_METHOD
|
|
167
|
+
'METHOD'
|
|
168
|
+
end
|
|
143
169
|
end
|
|
144
170
|
|
|
145
171
|
# Build the dataflow components of this FindingEvent.
|
|
@@ -148,9 +174,9 @@ module Contrast
|
|
|
148
174
|
def dataflow! event
|
|
149
175
|
taint_target = taint_target!(event)
|
|
150
176
|
truncate_obj = Contrast::Utils::ObjectShare::OBJECT_KEY != taint_target
|
|
151
|
-
@object = Contrast::Agent::Reporting::
|
|
177
|
+
@object = Contrast::Agent::Reporting::FindingEventObject.convert(event.object, truncate_obj)
|
|
152
178
|
truncate_ret = Contrast::Utils::ObjectShare::RETURN_KEY != taint_target
|
|
153
|
-
@ret = Contrast::Agent::Reporting::
|
|
179
|
+
@ret = Contrast::Agent::Reporting::FindingEventObject.convert(event.ret, truncate_ret)
|
|
154
180
|
event_args!(event, taint_target)
|
|
155
181
|
taint_ranges!(event)
|
|
156
182
|
end
|
|
@@ -164,7 +190,7 @@ module Contrast
|
|
|
164
190
|
@args = []
|
|
165
191
|
idx = 0
|
|
166
192
|
while idx < event.args.length
|
|
167
|
-
@args << Contrast::Agent::Reporting::
|
|
193
|
+
@args << Contrast::Agent::Reporting::FindingEventObject.convert(event.args[idx], taint_target != idx)
|
|
168
194
|
idx += 1
|
|
169
195
|
end
|
|
170
196
|
end
|
|
@@ -176,7 +202,8 @@ module Contrast
|
|
|
176
202
|
@event_sources = []
|
|
177
203
|
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
|
178
204
|
|
|
179
|
-
|
|
205
|
+
source = Contrast::Agent::Reporting::FindingEventSource.convert(event)
|
|
206
|
+
event_sources << source if source
|
|
180
207
|
end
|
|
181
208
|
|
|
182
209
|
# Convert the parent id's of the given ContrastEvent to the reportable form for this FindingEvent.
|
|
@@ -185,7 +212,7 @@ module Contrast
|
|
|
185
212
|
def parent_ids! event
|
|
186
213
|
@parent_object_ids = []
|
|
187
214
|
event.parent_events&.each do |parent_event|
|
|
188
|
-
parent_object_ids << parent_event.event_id.to_i
|
|
215
|
+
parent_object_ids << Contrast::Agent::Reporting::FindingEventParentObject.new(parent_event.event_id.to_i)
|
|
189
216
|
end
|
|
190
217
|
end
|
|
191
218
|
|
|
@@ -194,7 +221,7 @@ module Contrast
|
|
|
194
221
|
#
|
|
195
222
|
# @param _event [Contrast::Agent::Assess::ContrastEvent]
|
|
196
223
|
def properties! _event
|
|
197
|
-
@properties =
|
|
224
|
+
@properties = []
|
|
198
225
|
end
|
|
199
226
|
|
|
200
227
|
# Convert the stack of the given ContrastEvent to the reportable form for this FindingEvent.
|
|
@@ -203,7 +230,7 @@ module Contrast
|
|
|
203
230
|
def stack! event
|
|
204
231
|
@stack = []
|
|
205
232
|
event.stack_trace.each do |stack_event|
|
|
206
|
-
if (report = Contrast::Agent::Reporting::
|
|
233
|
+
if (report = Contrast::Agent::Reporting::FindingEventStack.convert(stack_event))
|
|
207
234
|
stack << report
|
|
208
235
|
end
|
|
209
236
|
end
|
|
@@ -217,7 +244,9 @@ module Contrast
|
|
|
217
244
|
@taint_ranges = []
|
|
218
245
|
event&.tags&.each_pair do |tag_key, tag_ranges|
|
|
219
246
|
tags << tag_key
|
|
220
|
-
tag_ranges.each
|
|
247
|
+
tag_ranges.each do |range|
|
|
248
|
+
taint_ranges << Contrast::Agent::Reporting::FindingEventTaintRange.convert(range)
|
|
249
|
+
end
|
|
221
250
|
end
|
|
222
251
|
end
|
|
223
252
|
|
data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb}
RENAMED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'base64'
|
|
5
|
+
require 'contrast/agent/assess/contrast_object'
|
|
5
6
|
|
|
6
7
|
module Contrast
|
|
7
8
|
module Agent
|
|
8
9
|
module Reporting
|
|
9
|
-
# This is the new
|
|
10
|
-
# to relay this information in the Finding/Trace messages. These
|
|
11
|
-
# construct the vulnerability information for the assess feature. They represent those parts of the
|
|
12
|
-
# were acted on in a Dataflow Finding.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
# This is the new FindingEventObject class which will include all the needed information for the new reporting
|
|
11
|
+
# system to relay this information in the Finding/Trace messages. These FindingEventObjects are used by
|
|
12
|
+
# TeamServer to construct the vulnerability information for the assess feature. They represent those parts of the
|
|
13
|
+
# objects that were acted on in a Dataflow Finding.
|
|
14
|
+
class FindingEventObject
|
|
15
|
+
# @return [Integer] the id of the Object this represents.
|
|
16
|
+
attr_reader :hash
|
|
17
|
+
# @return [Boolean] if the Object is tracked or not
|
|
18
|
+
attr_reader :tracked
|
|
19
|
+
# @return [String] the base64 of the human readable representation of the Object this represents.
|
|
20
|
+
attr_reader :value
|
|
19
21
|
|
|
20
22
|
# We'll truncate any object that isn't important to the taint ranges of this event, so that we don't murder
|
|
21
23
|
# TeamServer by, for instance, hypothetically sending the entire rendered HTML page >_> <_< >_>
|
|
@@ -25,8 +27,8 @@ module Contrast
|
|
|
25
27
|
|
|
26
28
|
class << self
|
|
27
29
|
# @param object [Contrast::Agent::Assess::ContrastObject] the object to translate
|
|
28
|
-
# @param truncate [Boolean] if the value of this
|
|
29
|
-
# @return [Contrast::Agent::Reporting::
|
|
30
|
+
# @param truncate [Boolean] if the value of this FindingEventObject should be truncated or not
|
|
31
|
+
# @return [Contrast::Agent::Reporting::FindingEventObject]
|
|
30
32
|
def convert object, truncate
|
|
31
33
|
report = new
|
|
32
34
|
report.attach_data(object, truncate)
|
|
@@ -35,13 +37,13 @@ module Contrast
|
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Parse the data from a Contrast::Agent::Assess::ContrastObject to attach what is required for reporting to
|
|
38
|
-
# TeamServer to this Contrast::Agent::Reporting::
|
|
40
|
+
# TeamServer to this Contrast::Agent::Reporting::FindingEventObject
|
|
39
41
|
#
|
|
40
|
-
# @param object [Contrast::Agent::Assess::ContrastObject]
|
|
42
|
+
# @param object [Contrast::Agent::Assess::ContrastObject, nil]
|
|
41
43
|
def attach_data object, truncate
|
|
42
|
-
@hash = object.__id__
|
|
43
|
-
@tracked = object
|
|
44
|
-
@value = reportable_value(object
|
|
44
|
+
@hash = object&.object.__id__
|
|
45
|
+
@tracked = !!object&.tracked?
|
|
46
|
+
@value = reportable_value(object&.object, truncate)
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
@@ -59,9 +61,9 @@ module Contrast
|
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
def validate
|
|
62
|
-
raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash
|
|
64
|
+
raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash
|
|
63
65
|
raise(ArgumentError, "#{ self } did not have a proper tracked. Unable to continue.") if tracked.nil?
|
|
64
|
-
return
|
|
66
|
+
return if value
|
|
65
67
|
|
|
66
68
|
raise(ArgumentError, "#{ self } did not have a proper value. Unable to continue.")
|
|
67
69
|
end
|
|
@@ -71,10 +73,12 @@ module Contrast
|
|
|
71
73
|
# Parse, truncate, and translate the given value to be reported to TeamServer. The field is expected to be
|
|
72
74
|
# base64 encoded.
|
|
73
75
|
#
|
|
74
|
-
# @param value [String] the contrast_string of the object this represents.
|
|
76
|
+
# @param value [String, nil] the contrast_string of the object this represents.
|
|
75
77
|
# @param truncate [Boolean] if the string should be truncated or not.
|
|
76
78
|
# @return [String]
|
|
77
79
|
def reportable_value value, truncate
|
|
80
|
+
return Contrast::Utils::ObjectShare::NIL_STRING unless value
|
|
81
|
+
|
|
78
82
|
if truncate && value.length > TRUNCATION_LENGTH
|
|
79
83
|
tmp = []
|
|
80
84
|
tmp << value[0, UNTRUNCATED_PORTION_LENGTH]
|
|
@@ -0,0 +1,39 @@
|
|
|
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 'base64'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
module Reporting
|
|
9
|
+
# This is the new FindingEventParentObject class which will include all the needed information for the new
|
|
10
|
+
# reporting system to relay this information in the Finding/Trace messages. These FindingEventParentObject are
|
|
11
|
+
# used by TeamServer to relate this event to those that came previously. They represent the events that directly
|
|
12
|
+
# preceding the FindingEvent generated.
|
|
13
|
+
class FindingEventParentObject
|
|
14
|
+
# @return [Integer] the Id of the parent event
|
|
15
|
+
attr_reader :id
|
|
16
|
+
|
|
17
|
+
def initialize id
|
|
18
|
+
@id = id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
22
|
+
# TeamServer to process the JSON form of this message.
|
|
23
|
+
#
|
|
24
|
+
# @return [Hash]
|
|
25
|
+
# @raise [ArgumentError]
|
|
26
|
+
def to_controlled_hash
|
|
27
|
+
validate
|
|
28
|
+
{
|
|
29
|
+
id: id
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def validate
|
|
34
|
+
raise(ArgumentError, "#{ self } did not have a proper id. Unable to continue.") unless id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
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
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Reporting
|
|
7
|
+
# This is the new FindingEventProperty class which will include all the needed information for the new reporting
|
|
8
|
+
# system to relay this information in the Finding/Trace messages. Events have properties on them which are held
|
|
9
|
+
# as an array of key-value pairs.
|
|
10
|
+
class FindingEventProperty
|
|
11
|
+
# @return [String] the key of the property
|
|
12
|
+
attr_reader :key
|
|
13
|
+
# @return [String] the value of the source
|
|
14
|
+
attr_reader :value
|
|
15
|
+
|
|
16
|
+
def initialize key, value
|
|
17
|
+
@key = key
|
|
18
|
+
@value = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Convert the instance variables on the class, and other information, into the identifiers required for
|
|
22
|
+
# TeamServer to process the JSON form of this message.
|
|
23
|
+
#
|
|
24
|
+
# @return [Hash]
|
|
25
|
+
# @raise [ArgumentError]
|
|
26
|
+
def to_controlled_hash
|
|
27
|
+
validate
|
|
28
|
+
{
|
|
29
|
+
key: key,
|
|
30
|
+
value: value
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate
|
|
35
|
+
raise(ArgumentError, "#{ self } did not have a proper key. Unable to continue.") unless key && !key.empty?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|