contrast-agent 7.6.0 → 7.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +35 -7
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +7 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +1 -6
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +4 -6
- data/lib/contrast/agent/request/request.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/utils/hash_digest.rb +0 -14
- data/lib/contrast/utils/hash_digest_extend.rb +16 -5
- data/lib/contrast/utils/json.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8363c48e9a12500ea45f3d40389d6bf29d7931e01599a577dfb4026c9cedc260
|
4
|
+
data.tar.gz: 1c21202db2612d8715f1658fa9818442e16a5a51f613143a3c3c6b69c0a0fabf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5fe5203e78e7ea7c6e996c32367c340704f3d1544890b64ed856d3554ef53f3006cc948fab0f3d36108e1577d19196249c10ee8f0fcee95c8677115b7a4a64
|
7
|
+
data.tar.gz: 7a1d876e9c49940daf97589adaaf5d2550fdbd51bd2216f44a28bf4f97ab9431a5b5db200035089a76bfbe66bf8849aed58f382ee99e7e681e555b8da5097af5
|
@@ -73,7 +73,8 @@ module Contrast
|
|
73
73
|
finding.routes << context.discovered_route if context&.discovered_route
|
74
74
|
build_evidence(evidence, finding)
|
75
75
|
finding.request = Contrast::Agent::Reporting::FindingRequest.convert(context.request) if context&.request
|
76
|
-
|
76
|
+
# Hash must be built last so that finding has full context.
|
77
|
+
hash = Contrast::Utils::HashDigest.generate_response_hash(finding, context&.request)
|
77
78
|
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
|
78
79
|
finding
|
79
80
|
end
|
@@ -69,21 +69,46 @@ module Contrast
|
|
69
69
|
# @param event [Contrast::Agent::Reporting::ReportingEvent] The event to send to TeamServer. Really a
|
70
70
|
# child of the ReportingEvent rather than a literal one.
|
71
71
|
# @param connection [Net::HTTP] open connection
|
72
|
-
# @return response [Net::HTTPResponse, nil] response from TS if no response
|
73
72
|
def send_event event, connection
|
74
73
|
return unless connection
|
75
74
|
return unless event.valid?
|
76
75
|
|
77
76
|
logger.debug('[Reporter] Preparing to send reporting event', event_class: event.cs__class)
|
78
77
|
request = build_request(event)
|
79
|
-
|
78
|
+
begin
|
79
|
+
response = connection.request(request)
|
80
|
+
rescue StandardError => e
|
81
|
+
handle_response_error(event, connection, e)
|
82
|
+
return
|
83
|
+
end
|
80
84
|
audit.audit_event(event, response) if ::Contrast::API.request_audit_enable
|
81
|
-
|
82
|
-
process_preflight_response(event, response, connection)
|
83
|
-
report_configuration(response, event)
|
85
|
+
process_event_response(event, response, connection)
|
84
86
|
response
|
85
87
|
rescue StandardError => e
|
86
|
-
|
88
|
+
logger.error('[Reporter] Error while processing event sent to TeamServer',
|
89
|
+
error: e,
|
90
|
+
event_type: event&.cs__class)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Only two types of events require processing when returned from TeamServer; preflight and settings.
|
94
|
+
# For Settings, we want to update internally and then report the resulting configuration.
|
95
|
+
# For preflight, we want to process the response and send findings if requested.
|
96
|
+
#
|
97
|
+
# @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer
|
98
|
+
# @param response [Net::HTTPResponse] the response from TeamServer
|
99
|
+
# @param connection [Net::HTTP] open connection
|
100
|
+
def process_event_response event, response, connection
|
101
|
+
case event
|
102
|
+
when Contrast::Agent::Reporting::Preflight
|
103
|
+
process_preflight_response(event, response, connection)
|
104
|
+
when Contrast::Agent::Reporting::AgentStartup,
|
105
|
+
Contrast::Agent::Reporting::ApplicationStartup,
|
106
|
+
Contrast::Agent::Reporting::ApplicationSettings,
|
107
|
+
Contrast::Agent::Reporting::ServerSettings
|
108
|
+
|
109
|
+
process_settings_response(response, event)
|
110
|
+
report_configuration(response, event)
|
111
|
+
end
|
87
112
|
end
|
88
113
|
|
89
114
|
# Write effective config to file:
|
@@ -91,7 +116,7 @@ module Contrast
|
|
91
116
|
# 200 or 304. In case of 304 there will be no new settings and we can write current ones.
|
92
117
|
# This is done on every settings request.
|
93
118
|
#
|
94
|
-
# @param response [
|
119
|
+
# @param response [Net::HTTPResponse, nil]
|
95
120
|
# @param event [Contrast::Agent::Reporting::ReportingEvent]
|
96
121
|
def report_configuration response, event
|
97
122
|
return unless response
|
@@ -109,14 +134,17 @@ module Contrast
|
|
109
134
|
logger.error('[Reporter] Error while reporting configuration', error: e, event_type: event&.cs__class)
|
110
135
|
end
|
111
136
|
|
137
|
+
# @return [Contrast::Agent::Reporting::ConnectionStatus]
|
112
138
|
def status
|
113
139
|
@_status ||= Contrast::Agent::Reporting::ConnectionStatus.new
|
114
140
|
end
|
115
141
|
|
142
|
+
# @return [Contrast::Agent::Reporting::ResponseHandler]
|
116
143
|
def response_handler
|
117
144
|
@_response_handler ||= Contrast::Agent::Reporting::ResponseHandler.new
|
118
145
|
end
|
119
146
|
|
147
|
+
# @return [Contrast::Config::Diagnostics::Monitor]
|
120
148
|
def diagnostics
|
121
149
|
@_diagnostics ||= Contrast::Config::Diagnostics::Monitor.new(Contrast::LOGGER.path)
|
122
150
|
end
|
@@ -91,6 +91,7 @@ module Contrast
|
|
91
91
|
#
|
92
92
|
# @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
|
93
93
|
# @param response [Net::HTTPResponse]
|
94
|
+
# @return [Contrast::Agent::Reporting::Response]
|
94
95
|
def process_settings_response response, event
|
95
96
|
res = response_handler.process(response, event)
|
96
97
|
if res
|
@@ -115,7 +116,6 @@ module Contrast
|
|
115
116
|
return unless response&.body && connection
|
116
117
|
|
117
118
|
findings_to_return = response.body.split(',').delete_if { |el| el.include?('*') }
|
118
|
-
mode.resend.reset_rescue_attempts
|
119
119
|
findings_to_return.each do |index|
|
120
120
|
preflight_message = event.messages[index.to_i]
|
121
121
|
preflight_data = preflight_message&.data
|
@@ -127,6 +127,12 @@ module Contrast
|
|
127
127
|
|
128
128
|
send_event(corresponding_finding, connection)
|
129
129
|
end
|
130
|
+
|
131
|
+
# Event rejected traces should be removed from the set.
|
132
|
+
# We could clean this up to know if the message was already deleted by the loop above.
|
133
|
+
event.messages&.each do |preflight_message|
|
134
|
+
Contrast::Agent::Reporting::ReportingStorage.delete(preflight_message&.data)
|
135
|
+
end
|
130
136
|
rescue StandardError => e
|
131
137
|
logger.error('[Reporter] Unable to handle preflight response', e)
|
132
138
|
end
|
@@ -44,12 +44,7 @@ module Contrast
|
|
44
44
|
def delete key
|
45
45
|
return unless key
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
deleted_value = collection.delete(key)
|
50
|
-
logger.debug('Key wasn\'t found') unless deleted_value
|
51
|
-
|
52
|
-
deleted_value
|
47
|
+
collection.delete(key)
|
53
48
|
end
|
54
49
|
|
55
50
|
# @param rule_id [String] the rule_id
|
@@ -25,6 +25,7 @@ module Contrast
|
|
25
25
|
# @return response [Net::HTTPResponse, nil]
|
26
26
|
def process response, event
|
27
27
|
logger.debug('[Reporter] Received a response')
|
28
|
+
return if event&.cs__is_a?(Contrast::Agent::Reporting::Finding)
|
28
29
|
return unless analyze_response?(response)
|
29
30
|
|
30
31
|
# Handle the response body and obtain server_features or app_settings
|
@@ -14,7 +14,6 @@ module Contrast
|
|
14
14
|
include Contrast::Agent::Reporting::NgResponseExtractor
|
15
15
|
include Contrast::Agent::Reporting::ResponseExtractor
|
16
16
|
|
17
|
-
ANALYZE_WHEN = %w[200 204].cs__freeze
|
18
17
|
ERROR_CODES = {
|
19
18
|
message_not_sent: '400',
|
20
19
|
access_forbidden: '401',
|
@@ -112,11 +111,10 @@ module Contrast
|
|
112
111
|
# used for in observed routes message.
|
113
112
|
return false unless response && (response_code = response&.code)
|
114
113
|
|
115
|
-
# We still need to check the response code even if we are not analyzing it, since the 304 code does not
|
116
|
-
# contain settings to be extracted but we still need to know for the diagnostics. Do not move this bellow
|
117
|
-
# the ANALYZE_WHEN check.
|
118
114
|
@_last_response_code = response_code
|
119
|
-
return true if
|
115
|
+
return true if response_code == '200'
|
116
|
+
return false if response_code == '204'
|
117
|
+
return false if response_code == '304'
|
120
118
|
|
121
119
|
handle_error(response) if ERROR_CODES.value?(response_code) && response&.body
|
122
120
|
# There was error, so analyze the Error and nothing more.
|
@@ -267,7 +265,7 @@ module Contrast
|
|
267
265
|
# @return response [Contrast::Agent::Reporting::Response]
|
268
266
|
def convert_response response, event
|
269
267
|
response_body = response&.body
|
270
|
-
return unless response_body
|
268
|
+
return unless response_body && !response_body.blank?
|
271
269
|
|
272
270
|
response_data = Contrast::Utils::Json.parse(response_body, deep_symbolize: true)
|
273
271
|
return unless response_data.cs__is_a?(Hash)
|
@@ -180,7 +180,7 @@ module Contrast
|
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
183
|
-
# returns or
|
183
|
+
# returns or generates the hash checksum for the request
|
184
184
|
#
|
185
185
|
# @return @_hash_id [String] Contrast::Utils::HashDigest generated string checksum
|
186
186
|
def hash_id
|
@@ -15,7 +15,6 @@ module Contrast
|
|
15
15
|
class HashDigest < Digest::Class
|
16
16
|
include Digest::Instance
|
17
17
|
extend Contrast::Utils::HashDigestExtend
|
18
|
-
CONTENT_LENGTH_HEADER = 'Content-Length'
|
19
18
|
CHARS = %w[a b c d e f g].cs__freeze
|
20
19
|
CRYPTO_RULES = %w[crypto-bad-ciphers crypto-bad-mac].cs__freeze
|
21
20
|
CONFIG_PATH_KEY = 'path'
|
@@ -34,8 +33,6 @@ module Contrast
|
|
34
33
|
#
|
35
34
|
# @param finding [Contrast::Agent::Reporting::Finding] finding to be reported
|
36
35
|
# @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
|
37
|
-
# @return checksum [Integer, nil] returns nil if there is no request context or tracking
|
38
|
-
# is disabled.
|
39
36
|
def update_on_request finding, request
|
40
37
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
41
38
|
return unless context || ::Contrast::ASSESS.non_request_tracking?
|
@@ -58,7 +55,6 @@ module Contrast
|
|
58
55
|
# Update to CRC checksum the event source name and source type.
|
59
56
|
#
|
60
57
|
# @param events [Array<Contrast::Agent::Reporting::FindingEvent>]
|
61
|
-
# @return checksum [Integer, nil] returns nil if there is no events
|
62
58
|
def update_on_sources events
|
63
59
|
events.each do |event|
|
64
60
|
event.event_sources.each do |source|
|
@@ -68,22 +64,12 @@ module Contrast
|
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
71
|
-
# This method converts and integer value for length into a string value
|
72
|
-
# that we can hash on, based on the logarithmic value of the length, and
|
73
|
-
# updates the current hash with that value.
|
74
|
-
# @param chr [Numeric] the length to translate
|
75
|
-
def update_on_content_length chr
|
76
|
-
update(CHARS[Math.log10(chr.to_s.length).to_i] || CHARS[-1])
|
77
|
-
end
|
78
|
-
|
79
67
|
# Converts given string to CRC checksum. CRC32 checksum ensures that If error
|
80
68
|
# of a single bit occurs, the CRC checksum will fail, regardless of any other
|
81
69
|
# property of the transmitted data, including its length. Called several times
|
82
70
|
# with previous CRC to recalculate the new output.
|
83
71
|
#
|
84
72
|
# @param str [String]
|
85
|
-
# @return @crc32 [Integer, nil] updated value of crc 32 bit integer checksum or
|
86
|
-
# nil if passed string is nil or empty
|
87
73
|
def update str
|
88
74
|
return unless str
|
89
75
|
|
@@ -17,7 +17,7 @@ module Contrast
|
|
17
17
|
# param names and content length to CRC checksum and returns string representation
|
18
18
|
#
|
19
19
|
# @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
|
20
|
-
# @return
|
20
|
+
# @return [String] String representation of CRC32 checksum
|
21
21
|
def generate_request_hash request
|
22
22
|
hash = new
|
23
23
|
hash.update(request.request_method)
|
@@ -25,8 +25,6 @@ module Contrast
|
|
25
25
|
request.parameters.each_key do |name|
|
26
26
|
hash.update(name)
|
27
27
|
end
|
28
|
-
cl = request.headers[Contrast::Utils::HashDigest::CONTENT_LENGTH_HEADER]
|
29
|
-
hash.update_on_content_length(cl) if cl
|
30
28
|
hash.finish
|
31
29
|
end
|
32
30
|
|
@@ -37,7 +35,7 @@ module Contrast
|
|
37
35
|
# @param finding [Contrast::Agent::Reporting::Finding] to be reported
|
38
36
|
# @param source [Object] the source of the Trigger Event
|
39
37
|
# @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
|
40
|
-
# @return
|
38
|
+
# @return [String] String representation of CRC32 checksum
|
41
39
|
def generate_event_hash finding, source, request
|
42
40
|
return generate_dataflow_hash(finding, request) if finding.events.length.to_i > 1
|
43
41
|
|
@@ -51,7 +49,7 @@ module Contrast
|
|
51
49
|
# to CRC32 checksum and returns string representation to be appended to Contrast::Api::Dtm::Finding
|
52
50
|
#
|
53
51
|
# @param finding [Contrast::Agent::Reporting::Finding] to be reported
|
54
|
-
# @return
|
52
|
+
# @return [String] String representation of CRC32 checksum.
|
55
53
|
def generate_config_hash finding
|
56
54
|
hash = new
|
57
55
|
hash.update(finding.rule_id)
|
@@ -80,6 +78,19 @@ module Contrast
|
|
80
78
|
hash.finish
|
81
79
|
end
|
82
80
|
|
81
|
+
# Generates the hash checksum for response scanning. Converts the rule_id and request to CRC32 checksum and
|
82
|
+
# returns string representation.
|
83
|
+
#
|
84
|
+
# @param finding [Contrast::Agent::Reporting::Finding] to be reported
|
85
|
+
# # @param request [Contrast::Agent::Request]
|
86
|
+
# @return [String] String representation of CRC32 checksum.
|
87
|
+
def generate_response_hash finding, request
|
88
|
+
hash = new
|
89
|
+
hash.update(finding.rule_id)
|
90
|
+
hash.update_on_request(finding, request)
|
91
|
+
hash.finish
|
92
|
+
end
|
93
|
+
|
83
94
|
private
|
84
95
|
|
85
96
|
# Generates the hash checksum for crypto(crypto-bad-ciphers, crypto-bad-mac) rules.
|
data/lib/contrast/utils/json.rb
CHANGED
@@ -14,7 +14,7 @@ module Contrast
|
|
14
14
|
|
15
15
|
# Add any known cases where parsing error might arise from older json parser:
|
16
16
|
# @return [Array<String>]
|
17
|
-
SPECIAL_CASES = ["\"\"", "\"0\""].cs__freeze # rubocop:disable Style/StringLiterals
|
17
|
+
SPECIAL_CASES = [nil, "", "\"\"", "\"0\""].cs__freeze # rubocop:disable Style/StringLiterals
|
18
18
|
|
19
19
|
# Parses a string using JSON.parser. This method is used instead of standard JSON.parse to
|
20
20
|
# support older versions of json gem => not supporting key-value second parameter, which is
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contrast-agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.6.
|
4
|
+
version: 7.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ruby@contrastsecurity.com
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -1395,7 +1395,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1395
1395
|
- !ruby/object:Gem::Version
|
1396
1396
|
version: '0'
|
1397
1397
|
requirements: []
|
1398
|
-
rubygems_version: 3.3.
|
1398
|
+
rubygems_version: 3.3.27
|
1399
1399
|
signing_key:
|
1400
1400
|
specification_version: 4
|
1401
1401
|
summary: Contrast Security's agent for rack-based applications.
|