contrast-agent 7.1.0 → 7.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/ext/extconf_common.rb +88 -14
  3. data/lib/contrast/agent/assess/policy/source_method.rb +13 -4
  4. data/lib/contrast/agent/assess/policy/trigger_method.rb +12 -18
  5. data/lib/contrast/agent/excluder/excluder.rb +64 -31
  6. data/lib/contrast/agent/protect/rule/base.rb +4 -6
  7. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +1 -1
  8. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +2 -2
  9. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +1 -1
  10. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +1 -1
  11. data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +2 -2
  12. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +1 -1
  13. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +1 -1
  14. data/lib/contrast/agent/protect/rule/utils/filters.rb +6 -6
  15. data/lib/contrast/agent/protect/rule/xxe/xxe.rb +1 -1
  16. data/lib/contrast/agent/reporting/client/interface.rb +132 -0
  17. data/lib/contrast/agent/reporting/client/interface_base.rb +27 -0
  18. data/lib/contrast/agent/reporting/connection_status.rb +0 -1
  19. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +6 -4
  20. data/lib/contrast/agent/reporting/reporter.rb +11 -26
  21. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +1 -1
  22. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  23. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +47 -6
  24. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +40 -31
  25. data/lib/contrast/agent/reporting/reporting_utilities/resend.rb +144 -0
  26. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +35 -13
  27. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_mode.rb +14 -1
  28. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +11 -11
  29. data/lib/contrast/agent/request/request.rb +27 -12
  30. data/lib/contrast/agent/telemetry/base.rb +18 -19
  31. data/lib/contrast/agent/telemetry/exception/obfuscate.rb +97 -0
  32. data/lib/contrast/agent/telemetry/exception.rb +1 -0
  33. data/lib/contrast/agent/version.rb +1 -1
  34. data/lib/contrast/components/config/sources.rb +6 -5
  35. data/lib/contrast/components/settings.rb +9 -0
  36. data/lib/contrast/config/diagnostics/source_config_value.rb +5 -1
  37. data/lib/contrast/config/diagnostics/tools.rb +4 -4
  38. data/lib/contrast/config/validate.rb +2 -2
  39. data/lib/contrast/configuration.rb +11 -19
  40. data/lib/contrast/framework/grape/support.rb +1 -2
  41. data/lib/contrast/framework/manager.rb +17 -8
  42. data/lib/contrast/framework/rack/support.rb +99 -1
  43. data/lib/contrast/framework/rails/support.rb +1 -2
  44. data/lib/contrast/framework/sinatra/support.rb +1 -2
  45. data/lib/contrast/logger/aliased_logging.rb +18 -9
  46. data/lib/contrast/utils/hash_utils.rb +21 -2
  47. data/lib/contrast/utils/request_utils.rb +14 -0
  48. data/resources/assess/policy.json +11 -0
  49. metadata +6 -3
  50. data/lib/contrast/agent/reporting/input_analysis/details/bot_blocker_details.rb +0 -27
@@ -0,0 +1,144 @@
1
+ # Copyright (c) 2023 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
+ # Module to house the Resend logic, when there is no response from TS.
8
+ module Resend
9
+ # How manny times the client will try to rescue from above error before raising an
10
+ # error to reflect the events.
11
+ #
12
+ RESCUE_ATTEMPTS = 3
13
+ TIMEOUT = 5
14
+ RETRY_ERRORS = [
15
+ Net::OpenTimeout, Net::ReadTimeout, ArgumentError, EOFError, OpenSSL::SSL::SSLError, Resolv::ResolvError,
16
+ Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::ESHUTDOWN, Errno::EHOSTDOWN, NameError,
17
+ Errno::EHOSTUNREACH, Errno::EISCONN, Errno::ECONNABORTED, Errno::ENETRESET, Errno::ENETUNREACH, SocketError,
18
+ NameError, NoMethodError, Timeout::Error, RuntimeError
19
+ ].cs__freeze
20
+ RESENDING_MESSAGE = '[Reporter] Resending message...'
21
+ END_RESENDING_MESSAGE = '[Reporter] Reporter tried to resend event and received error:'
22
+
23
+ # This method is mainly used in inside error handling and startup mesasges sending.
24
+ #
25
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] event to resend
26
+ # @param connection [Net::HTTP] open connection
27
+ # @param error [StandardError] error that was raised
28
+ # @return [Net::HTTPResponse, nil] response from TS
29
+ def try_resend event, connection, error
30
+ # The timeout for agent to sleep is set here, we don't need to raise an error,
31
+ # but also 15 min is a long time to wait for example if event is startup message.
32
+ # Override timeout
33
+ if Contrast::Agent::Reporting::ReporterClientUtils::STARTUP_EVENTS.include?(event.cs__class)
34
+ response_handler.suspend_reporting(RESENDING_MESSAGE,
35
+ TIMEOUT,
36
+ error,
37
+ event: event,
38
+ log_error: false,
39
+ startup: true)
40
+ end
41
+ mode.enter_resend_mode
42
+ handle_resend(event, connection)
43
+ end
44
+
45
+ # This method will handle the error and will decide if we need to resend the event
46
+ #
47
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
48
+ # @param connection [Net::HTTP] open connection
49
+ # @return [Net::HTTPResponse, nil] response from TS
50
+ def handle_resend event, connection
51
+ if sleep?
52
+ logger.debug("[Reporter] sleeping for #{ TIMEOUT } seconds before resending...")
53
+ Thread.current.send(:sleep, timeout)
54
+ wake_up
55
+ end
56
+ response = mode.status == mode.resending ? send_event(event, connection) : nil
57
+ response_success! if response
58
+ response
59
+ end
60
+
61
+ # Handles errors that occurs before the ResponseHandler do not have a response, or response
62
+ # code to hande. This Errors requires different handling, because most of errors here
63
+ # occurs when the response cannot be read, there is open ssl error, or a certain Timeout
64
+ # is reached.
65
+ #
66
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
67
+ # One of the DTMs valid for the event field of Contrast::Api::Dtm::Message
68
+ # @param error [StandardError]
69
+ # @param connection [Net::HTTP] open connection
70
+ # @return nil [NilClass] to be passed as response
71
+ def handle_response_error event, connection, error
72
+ return end_of_rescue(event, error) if mode.resend.rescue_attempts >= RESCUE_ATTEMPTS
73
+
74
+ if RETRY_ERRORS.include?(error.cs__class)
75
+ mode.resend.increase_rescue_attempts
76
+ try_resend(event, connection, error)
77
+ else
78
+ end_of_rescue(event, error)
79
+ end
80
+ end
81
+
82
+ # End of rescue attempts.
83
+ #
84
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
85
+ # @param error [StandardError]
86
+ # @return [NilClass]
87
+ def end_of_rescue event, error
88
+ if Contrast::Agent::Reporting::ReporterClientUtils::STARTUP_EVENTS.include?(event.cs__class)
89
+ # Agent didn't send it's startup events, There will be reporting errors, disable reporting.
90
+ disable_reporting(event, error)
91
+ else
92
+ # There is an error in one of the reported messages, log that error and continue.
93
+ logger.error(END_RESENDING_MESSAGE,
94
+ resend_attempts: Contrast::Agent::Reporting::Resend::RESCUE_ATTEMPTS,
95
+ connection_error: error,
96
+ client: Contrast::Agent::Reporting::ReporterClient::SERVICE_NAME,
97
+ event_id: event&.__id__,
98
+ event_type: event&.cs__class&.cs__name)
99
+ end
100
+ end
101
+
102
+ # Disable reporting when there is an error that cannot be handled.
103
+ #
104
+ # @param event [Contrast::Agent::Reporting::ReportingEvent]
105
+ # @param error [StandardError]
106
+ # @return [NilClass]
107
+ def disable_reporting event, error
108
+ logger.error('[Agent] Disabling Reporting...', error: error.message, event_type: event.cs__class&.cs__name)
109
+ Contrast::AGENT.disable!
110
+ end
111
+
112
+ # Methods that will initialize the resending state.
113
+ class Status
114
+ # RESEND_STARTUP_EVENTS = 4
115
+
116
+ def initialize
117
+ @_rescue_attempts = 0
118
+ end
119
+
120
+ # return how many times the client has tried to rescue from error.
121
+ #
122
+ # @return [Integer]
123
+ def rescue_attempts
124
+ @_rescue_attempts ||= 0
125
+ end
126
+
127
+ # Reset Error rescues attempts.
128
+ #
129
+ # @return [Integer]
130
+ def reset_rescue_attempts
131
+ @_rescue_attempts = 0
132
+ end
133
+
134
+ # Increase the Error rescues attempts.
135
+ #
136
+ # @return [Integer]
137
+ def increase_rescue_attempts
138
+ @_rescue_attempts += 1
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -40,7 +40,7 @@ module Contrast
40
40
  assess_on: ::Contrast::ASSESS.enabled?)
41
41
  response
42
42
  rescue StandardError => e
43
- logger.error('Unable to process response from TeamServer', e)
43
+ logger.error('[Reporter] Unable to process response from TeamServer', e)
44
44
  nil
45
45
  end
46
46
 
@@ -63,11 +63,45 @@ module Contrast
63
63
  @_mode ||= Contrast::Agent::Reporting::ResponseHandlerMode.new
64
64
  end
65
65
 
66
+ # Puts the reporting service to sleep
67
+ def put_to_sleep
68
+ @_sleep = true
69
+ end
70
+
66
71
  # Wakes the reporting service
67
72
  def wake_up
68
73
  @_sleep = false
69
74
  end
70
75
 
76
+ # Suspend the reporter and try again in time. If not set
77
+ # the timeout is set to 15 min As default:
78
+ #
79
+ # @param message [String] Message to log.
80
+ # @param timeout [Integer,nil] The timeout to wait and retry after.
81
+ # @param error_message [String, nil] Error message if any received.
82
+ # @param event [Contrast::Agent::Reporting::ReportingEvent, nil] The event sent to TeamServer if set
83
+ # @param log_error [Boolean] Whether to log the error or not.
84
+ # @param startup [Boolean] Whether this is a startup error or not.
85
+ def suspend_reporting message, timeout, error_message, event: nil, log_error: true, startup: false
86
+ @_timeout = timeout || Contrast::Agent::Reporting::ResponseHandler::TIMEOUT
87
+ if log_error
88
+ log_error_msg(message,
89
+ timeout: @_timeout,
90
+ error_message: error_message || 'none',
91
+ event_id: event.nil? ? 'none' : event&.__id__,
92
+ event_type: event.nil? ? 'none' : event&.cs__class&.cs__name)
93
+ end
94
+ if startup
95
+ logger.info(message,
96
+ connection_error: error_message,
97
+ client: Contrast::Agent::Reporting::ReporterClient::SERVICE_NAME,
98
+ event_id: event&.__id__,
99
+ timeout: timeout,
100
+ event_type: event&.cs__class&.cs__name)
101
+ end
102
+ put_to_sleep
103
+ end
104
+
71
105
  private
72
106
 
73
107
  # Handles the errors code received from TS and takes appropriate action.
@@ -93,18 +127,6 @@ module Contrast
93
127
  end
94
128
  end
95
129
 
96
- # Suspend the reporter and try again in time. If not set
97
- # the timeout is set to 15 min As default:
98
- #
99
- # @param message [String] Message to log.
100
- # @param timeout [Integer,nil] The timeout to wait and retry after.
101
- # @param error_message [String, nil] Error message if any received.
102
- def suspend_reporting message, timeout, error_message
103
- @_timeout = timeout || Contrast::Agent::Reporting::ResponseHandler::TIMEOUT
104
- log_error_msg(message, timeout: @_timeout, error_message: error_message || 'none')
105
- @_sleep = true
106
- end
107
-
108
130
  # Log what we've received.
109
131
  #
110
132
  # @param message [String] Message to log
@@ -1,6 +1,8 @@
1
1
  # Copyright (c) 2023 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_utilities/resend'
5
+
4
6
  module Contrast
5
7
  module Agent
6
8
  module Reporting
@@ -44,6 +46,17 @@ module Contrast
44
46
  @_status ||= running
45
47
  end
46
48
 
49
+ # Resend Status tracker
50
+ #
51
+ # @return [Contrast::Agent::Reporting::Resend::Status]
52
+ def resend
53
+ @_resend ||= Contrast::Agent::Reporting::Resend::Status.new
54
+ end
55
+
56
+ def enter_resend_mode
57
+ @_status = resending
58
+ end
59
+
47
60
  # Set current mode.
48
61
  #
49
62
  # @return [Symbol] Reporter's client and Response Handler
@@ -54,7 +67,7 @@ module Contrast
54
67
 
55
68
  # Reset mode
56
69
  #
57
- def reset_mode
70
+ def enter_run_mode
58
71
  @_status = running
59
72
  end
60
73
  end
@@ -54,6 +54,16 @@ module Contrast
54
54
  @_last_application_modified
55
55
  end
56
56
 
57
+ # Cease reporting about this application
58
+ #
59
+ # @param message [String] Message to log
60
+ # @param info_hash [Hash] information about the context to log.
61
+ def stop_reporting message, info_hash
62
+ Contrast::Agent.reporter&.stop!
63
+ log_error_msg(message, info_hash)
64
+ ::Contrast::AGENT.disable!
65
+ end
66
+
57
67
  private
58
68
 
59
69
  # check if response code is valid before analyze it
@@ -127,7 +137,7 @@ module Contrast
127
137
  # log, suspend, disable:
128
138
  if mode == @_mode.running
129
139
  log_error_msg(message,
130
- response: response.__id__,
140
+ response_id: response.__id__,
131
141
  request: Contrast::Agent::REQUEST_TRACKER.current&.request&.type,
132
142
  error_message: error_message || 'none',
133
143
  auth_error: auth_error || 'none')
@@ -176,16 +186,6 @@ module Contrast
176
186
  end
177
187
  end
178
188
 
179
- # Cease reporting about this application
180
- #
181
- # @param message [String] Message to log
182
- # @param info_hash [Hash] information about the context to log.
183
- def stop_reporting message, info_hash
184
- Contrast::Agent.reporter&.stop!
185
- log_error_msg(message, info_hash)
186
- ::Contrast::AGENT.disable!
187
- end
188
-
189
189
  # Applies the settings from the TS response
190
190
  #
191
191
  # @param response [Contrast::Agent::Reporting::Response]
@@ -10,6 +10,7 @@ require 'contrast/utils/hash_digest'
10
10
  require 'contrast/components/logger'
11
11
  require 'contrast/components/scope'
12
12
  require 'contrast/utils/request_utils'
13
+ require 'contrast/utils/duck_utils'
13
14
 
14
15
  module Contrast
15
16
  module Agent
@@ -24,12 +25,7 @@ module Contrast
24
25
 
25
26
  extend Forwardable
26
27
 
27
- INNER_REST_TOKEN = %r{/\d+/}.cs__freeze
28
- LAST_REST_TOKEN = %r{/\d+$}.cs__freeze
29
- INNER_NUMBER_MARKER = '/{n}/'
30
- LAST_NUMBER_MARKER = '/{n}'
31
- STATIC_SUFFIXES = /\.(?:js|css|jpeg|jpg|gif|png|ico|woff|svg|pdf|eot|ttf|jar)$/i.cs__freeze
32
- MEDIA_TYPE_MARKERS = %w[image/ text/css text/javascript].cs__freeze
28
+ EMPTY_PATH = '/'
33
29
 
34
30
  # @return [Rack::Request] The passed to the Agent RackRequest to be wrapped.
35
31
  attr_reader :rack_request
@@ -75,12 +71,31 @@ module Contrast
75
71
  def normalized_uri
76
72
  @_normalized_uri ||= begin
77
73
  path = rack_request.path_info || rack_request.path.to_s
78
- path = '/' if path.empty?
79
-
80
- uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0] # remove ;jsessionid
81
- uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0] # remove ?query_string=
82
- uri.gsub(INNER_REST_TOKEN, INNER_NUMBER_MARKER) # replace interior tokens
83
- uri.gsub(LAST_REST_TOKEN, LAST_NUMBER_MARKER) # replace last token
74
+ path = EMPTY_PATH if Contrast::Utils::DuckUtils.empty_duck?(path)
75
+
76
+ # /foo/bar;jsessionid=123 => /foo/bar
77
+ uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0]
78
+ # /foo/bar?query_string=123 => /foo/bar
79
+ uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0]
80
+
81
+ # Replace with tokens:
82
+ # NUM_ => '/{n}/'
83
+ # ID_ => '{ID}'
84
+ #
85
+ # replace UUIDs: /123e4567-e89b-42d3-a456-556642440000/ => /{ID}/
86
+ uri.gsub!(UUID_PATTERN, ID_)
87
+ # replace hash patterns: /6f1ed002ab5595859014ebf0951522d9/ => /{ID}/
88
+ uri.gsub!(HASH_PATTERN, ID_)
89
+ # replace windows SID: /S-1-5-21-1843332746-572796286-2118856591-1000/ => /{ID}/
90
+ uri.gsub!(WIN_PATTERN, ID_)
91
+ # replace interior number tokens: /123/ => /{n}/
92
+ uri.gsub!(NUM_PATTERN, NUM_)
93
+ # replace last number tokens: /123 => /{n}
94
+ uri.gsub!(END_PATTERN, NUM_[0..-2])
95
+ uri
96
+ rescue StandardError => e
97
+ logger.error('error normalizing uri', error: e, backtrace: e.backtrace)
98
+ EMPTY_PATH
84
99
  end
85
100
  end
86
101
 
@@ -8,6 +8,7 @@ require 'contrast/agent/thread/worker_thread'
8
8
  require 'contrast/agent/telemetry/telemetry'
9
9
  require 'contrast/agent/telemetry/exception'
10
10
  require 'contrast/utils/job_servers_running'
11
+ require 'contrast/agent/reporting/client/interface'
11
12
 
12
13
  module Contrast
13
14
  module Agent
@@ -96,11 +97,7 @@ module Contrast
96
97
  end
97
98
 
98
99
  def client
99
- @_client ||= Contrast::Agent::Telemetry::Client.new
100
- end
101
-
102
- def connection
103
- @_connection ||= client.initialize_connection(URL)
100
+ @_client ||= Contrast::Agent::Reporting::Telemetry::Interface.new
104
101
  end
105
102
 
106
103
  def attempt_to_start?
@@ -148,16 +145,24 @@ module Contrast
148
145
  Contrast::TELEMETRY_EXCEPTIONS&.clear
149
146
  end
150
147
 
151
- def request_with_response event
152
- client.handle_response(client.send_request(event, connection))
153
- end
154
-
155
148
  private
156
149
 
157
150
  def queue
158
151
  @_queue ||= Queue.new
159
152
  end
160
153
 
154
+ # Starts sending Telemetry messages.
155
+ def process_event event
156
+ logger.debug('[Telemetry] This is the current processed event', event)
157
+ if (sleep_time = client.request_with_response(event))
158
+ return sleep(sleep_time)
159
+ end
160
+
161
+ logger.debug('[Telemetry] Retrying to process event', event)
162
+ retry_sleep_time = client.request_with_response(event)
163
+ sleep(retry_sleep_time) unless retry_sleep_time.nil?
164
+ end
165
+
161
166
  # It is recommended that implementations send a single payload of general metrics every 3 hours, starting from
162
167
  # implementation startup. This returns a thread configured to do so.
163
168
  #
@@ -165,22 +170,16 @@ module Contrast
165
170
  def create_thread
166
171
  Contrast::Agent::Thread.new do
167
172
  loop do
168
- next unless client && connection
173
+ next unless client.connected?
169
174
  break unless attempt_to_start?
170
175
 
171
176
  # Start pushing exceptions to queue for reporting.
172
- Contrast::TELEMETRY_EXCEPTIONS.each_value { |value| queue << value }
173
- Contrast::TELEMETRY_EXCEPTIONS.clear
177
+ Contrast::TELEMETRY_EXCEPTIONS&.each_value { |value| queue << value }
178
+ Contrast::TELEMETRY_EXCEPTIONS&.clear
174
179
  until queue.empty?
175
180
  event = queue.pop
176
181
  begin
177
- logger.debug('[Telemetry] This is the current processed event', event)
178
- if (sleep_time = request_with_response(event))
179
- sleep(sleep_time)
180
- logger.debug('[Telemetry] Retrying to process event', event)
181
- retry_sleep_time = request_with_response(event)
182
- sleep(retry_sleep_time) unless retry_sleep_time.nil?
183
- end
182
+ process_event(event)
184
183
  rescue StandardError => e
185
184
  logger.error('[Telemetry] Could not send message to service from telemetry queue.', e)
186
185
  stop!
@@ -0,0 +1,97 @@
1
+ # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/duck_utils'
5
+ require 'contrast/utils/object_share'
6
+
7
+ module Contrast
8
+ module Agent
9
+ module Telemetry
10
+ module Exception
11
+ # Obfuscate sensitive user data before building exception.
12
+ module Obfuscate
13
+ CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.cs__freeze
14
+ # This will generate different chars for each agent startup but will be constant
15
+ # for current run and will make identical obfuscation to be compared if given type
16
+ # is the same.
17
+ CYPHER = CHARS.chars.shuffle.join.cs__freeze
18
+ VERSION_MATCH = '[^0-9].-'
19
+
20
+ # List of known places after witch a user name might appear:
21
+ KNOWN_DIRS = %w[app application project projects git github users home user].cs__freeze
22
+
23
+ class << self
24
+ # Returns paths for known gems.
25
+ #
26
+ # @return [Array<Regexp>] known paths
27
+ def known_paths
28
+ @_known_paths ||= KNOWN_DIRS.map do |name|
29
+ to_regexp(name)
30
+ end
31
+ end
32
+
33
+ # Obfuscate a type and replace it with random characters.
34
+ #
35
+ # @param path [String] the StackFrame type to obfuscate
36
+ # @return [String] obfuscated type
37
+ def obfuscate_path path
38
+ return path if Contrast::Utils::DuckUtils.empty_duck?(path)
39
+
40
+ cypher(path)
41
+ end
42
+
43
+ private
44
+
45
+ # Transforms string to regexp
46
+ #
47
+ # @param string [String]
48
+ def to_regexp string
49
+ /#{ string }/
50
+ end
51
+
52
+ # Add cypher to path to make it obscure, but unique enough for
53
+ # comparisons. Mutates original or duplicate if frozen string.
54
+ #
55
+ # @param string [String] string to be transformed.
56
+ # @return [String]
57
+ def cypher string
58
+ cypher = string.cs__frozen? ? string.dup : string
59
+ dirs = cypher.split(Contrast::Utils::ObjectShare::SLASH)
60
+ return string unless dirs.cs__is_a?(Array)
61
+
62
+ dirs.each_with_index do |name, idx|
63
+ next if Contrast::Utils::DuckUtils.empty_duck?(name)
64
+ next unless match_known(known_paths,
65
+ name.tr(VERSION_MATCH, Contrast::Utils::ObjectShare::EMPTY_STRING).downcase)
66
+
67
+ obscure(name)
68
+ # obscure username (next dir in line)
69
+ obscure(dirs[idx + 1]) if dirs[idx + 1]
70
+ end
71
+ cypher = dirs.join(Contrast::Utils::ObjectShare::SLASH)
72
+ return cypher if cypher
73
+
74
+ Contrast::Utils::ObjectShare::EMPTY_STRING
75
+ rescue StandardError
76
+ Contrast::Utils::ObjectShare::EMPTY_STRING
77
+ end
78
+
79
+ # @param known [Array<Regexp>] Array of regexp to match against
80
+ # @param type [String] type to check
81
+ def match_known known, type
82
+ known.any? { |regexp| type =~ regexp }
83
+ end
84
+
85
+ # Replaces chars in name.
86
+ #
87
+ # @param [string] name
88
+ # @return [String, nil]
89
+ def obscure name
90
+ name.to_s.tr!(CHARS, CYPHER)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -17,3 +17,4 @@ require 'contrast/agent/telemetry/exception/stack_frame'
17
17
  require 'contrast/agent/telemetry/exception/message_exception'
18
18
  require 'contrast/agent/telemetry/exception/message'
19
19
  require 'contrast/agent/telemetry/exception/event'
20
+ require 'contrast/agent/telemetry/exception/obfuscate'
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '7.1.0'
6
+ VERSION = '7.2.0'
7
7
  end
8
8
  end
@@ -11,14 +11,15 @@ module Contrast
11
11
  # This component encapsulates storing the source for each entry in the config,
12
12
  # so that we can report on where the value was set from.
13
13
  class Sources
14
- ENVIRONMENT_VARIABLE = 'ENV'
15
- COMMAND_LINE = 'CLI'
16
- CONTRAST_UI = 'ContrastUI'
17
- DEFAULT_VALUE = 'Default'
14
+ ENVIRONMENT_VARIABLE = 'ENVIRONMENT_VARIABLE'
15
+ COMMAND_LINE = 'COMMAND_LINE'
16
+ CONTRAST_UI = 'CONTRAST_UI'
17
+ DEFAULT_VALUE = 'DEFAULT_VALUE'
18
+ APP_CONFIGURATION_FILE = 'USER_CONFIGURATION_FILE'
18
19
  # Order matters for the Configurations files. This is read when Agent starts up and will always go
19
20
  # through the YAML as priority.
20
21
  # Do not change the order!
21
- APP_CONFIGURATION_FILE = %w[YAML YML].cs__freeze
22
+ APP_CONFIGURATION_EXTENSIONS = %w[yaml yml].cs__freeze
22
23
 
23
24
  # @return [Hash]
24
25
  attr_reader :data
@@ -5,6 +5,7 @@ require 'contrast/agent/excluder/excluder'
5
5
  require 'contrast/agent/reporting/settings/sensitive_data_masking'
6
6
  require 'contrast/components/config'
7
7
  require 'contrast/components/logger'
8
+ require 'contrast/utils/duck_utils'
8
9
 
9
10
  module Contrast
10
11
  module Components
@@ -219,6 +220,14 @@ module Contrast
219
220
  exclusions.input_exclusions.each do |exclusion|
220
221
  matchers << Contrast::Agent::ExclusionMatcher.new(exclusion)
221
222
  end
223
+ # Do not populate the matchers unless we have any. There are certain checks in
224
+ # SourceMethod that will safe-guard return if there are no exclusions received.
225
+ # The matching operation is expensive, and the excluder calls are made for each
226
+ # source, and we do not want to check for exclusions if they are empty. This is
227
+ # probably redundant as all exclusions default to empty, but will save useless
228
+ # new object creation at very least.
229
+ return if Contrast::Utils::DuckUtils.empty_duck?(matchers)
230
+
222
231
  @excluder = Contrast::Agent::Excluder.new(matchers)
223
232
  end
224
233
 
@@ -41,7 +41,11 @@ module Contrast
41
41
  # @param source [String] name of the source file yaml | yml
42
42
  # @return [Array<String>, nil]
43
43
  def assign_filename source
44
- Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE.each do |type|
44
+ Contrast::Components::Config::Sources::APP_CONFIGURATION_EXTENSIONS.each do |type|
45
+ # We use the source to transfer the file's name from the mapping of the extensions.
46
+ # This is done b/c the user_configuration file has a second field to be filled,
47
+ # and other sources don't. Transfer the source as filename and set the default value
48
+ # for it later when we find the sources.
45
49
  instance_variable_set(:@filename, source) if source.include?(".#{ type.downcase }")
46
50
  end
47
51
  end
@@ -164,10 +164,10 @@ module Contrast
164
164
  # For files we keep the whole path as source.
165
165
  source = Contrast::CONFIG.sources.get(new_effective_value.canonical_name)
166
166
  new_effective_value.assign_filename(source)
167
- new_source = if source.include?(Contrast::Config::LocalSourceValue::YAML_EXT)
168
- Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE[0]
169
- elsif source.include?(Contrast::Config::LocalSourceValue::YML_EXT)
170
- Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE[1]
167
+ new_source = if source.include?(Contrast::Config::LocalSourceValue::YAML_EXT) ||
168
+ source.include?(Contrast::Config::LocalSourceValue::YML_EXT)
169
+
170
+ Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE
171
171
  else
172
172
  Contrast::Components::Config::Sources::DEFAULT_VALUE
173
173
  end
@@ -108,9 +108,9 @@ module Contrast
108
108
 
109
109
  def test_connection reporter
110
110
  puts(" Connection failed #{ FAIL }") unless reporter
111
- connection = reporter.connection
111
+ connection = reporter.client.send(:reporting_connection)
112
112
  abort("Failed to Initialize Connection please check error logs for details #{ FAIL } ") unless connection
113
- abort('Failed to Start Client please check error logs for details') unless reporter.client.startup!(connection)
113
+ abort('Failed to Start Client please check error logs for details') unless reporter.client.startup
114
114
  last_response = reporter.client.response_handler.last_response_code
115
115
  if last_response.include?('40')
116
116
  puts(" Last response code: #{ last_response } #{ FAIL }")