contrast-agent 7.6.0 → 7.6.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '089a689fce3dfbc204455c12d29788d316553aa3ff6738fcf515e9f90730a7ae'
4
- data.tar.gz: 5fc2d10ffc084070fc3c268a9cb05d1f479b48cb16974b2501887cb1b762d6f6
3
+ metadata.gz: 8363c48e9a12500ea45f3d40389d6bf29d7931e01599a577dfb4026c9cedc260
4
+ data.tar.gz: 1c21202db2612d8715f1658fa9818442e16a5a51f613143a3c3c6b69c0a0fabf
5
5
  SHA512:
6
- metadata.gz: d13b3205d1e0cb67c8974fdf6d12f78e5a95f2886c59a14439ddc59b9dd8a3b0d0ebd23c5ebfe687d02cf1a12c9b17b127d821ea8131f0876ab284dac62c8b1b
7
- data.tar.gz: 73932d309fafe50ab5bc962655b58306cd5fae6a64729be1a4159274fe39c55c2138027c30525d07f773966476991685ba9b36cd0820527557d417324225af0d
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
- hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
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
- response = connection.request(request)
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
- process_settings_response(response, event)
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
- handle_response_error(event, connection, e)
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 [Contrast::Agent::Reporting::Response, nil]
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
- logger.debug('Starting deleting value for', key: key)
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 ANALYZE_WHEN.include?(response_code)
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 fenerates the hash checksum for the request
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
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '7.6.0'
6
+ VERSION = '7.6.1'
7
7
  end
8
8
  end
@@ -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 checksum [String] String representation of CRC32 checksum
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 checksum [String] String representation of CRC32 checksum
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 checksum [String] String representation of CRC32 checksum.
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.
@@ -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.0
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-04-11 00:00:00.000000000 Z
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.26
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.