contrast-agent 6.15.3 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +1 -1
  3. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.h +1 -1
  4. data/ext/cs__assess_module/cs__assess_module.c +0 -19
  5. data/ext/cs__assess_test/cs__assess_tests.c +1 -1
  6. data/ext/cs__common/cs__common.c +17 -18
  7. data/ext/cs__common/cs__common.h +7 -11
  8. data/ext/cs__contrast_patch/cs__contrast_patch.c +16 -24
  9. data/ext/extconf_common.rb +79 -0
  10. data/lib/contrast/agent/assess/policy/policy.rb +1 -1
  11. data/lib/contrast/agent/assess/policy/source_method.rb +1 -0
  12. data/lib/contrast/agent/deadzone/policy/policy.rb +1 -1
  13. data/lib/contrast/agent/patching/policy/policy.rb +2 -2
  14. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +3 -0
  15. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +1 -1
  16. data/lib/contrast/agent/reporting/reporter.rb +19 -4
  17. data/lib/contrast/agent/reporting/reporting_events/agent_effective_config.rb +32 -0
  18. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +7 -0
  19. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +3 -1
  20. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +11 -7
  21. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +15 -7
  22. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +4 -2
  23. data/lib/contrast/agent/reporting/reporting_workers/application_server_worker.rb +3 -0
  24. data/lib/contrast/agent/reporting/reporting_workers/reporter_heartbeat.rb +3 -0
  25. data/lib/contrast/agent/reporting/reporting_workers/server_settings_worker.rb +3 -0
  26. data/lib/contrast/agent/telemetry/base.rb +37 -12
  27. data/lib/contrast/agent/telemetry/client.rb +1 -3
  28. data/lib/contrast/agent/telemetry/telemetry.rb +0 -7
  29. data/lib/contrast/agent/thread/thread_watcher.rb +2 -2
  30. data/lib/contrast/agent/version.rb +1 -1
  31. data/lib/contrast/components/agent.rb +1 -1
  32. data/lib/contrast/components/api.rb +3 -3
  33. data/lib/contrast/components/app_context.rb +1 -1
  34. data/lib/contrast/components/assess.rb +1 -1
  35. data/lib/contrast/components/assess_rules.rb +2 -2
  36. data/lib/contrast/components/base.rb +3 -3
  37. data/lib/contrast/components/config/sources.rb +12 -9
  38. data/lib/contrast/components/config.rb +2 -2
  39. data/lib/contrast/components/protect.rb +2 -2
  40. data/lib/contrast/components/sampling.rb +7 -5
  41. data/lib/contrast/components/settings.rb +1 -1
  42. data/lib/contrast/config/certification_configuration.rb +1 -1
  43. data/lib/contrast/config/configuration_files.rb +47 -0
  44. data/lib/contrast/config/diagnostics/command_line.rb +24 -0
  45. data/lib/contrast/config/{config.rb → diagnostics/config.rb} +21 -6
  46. data/lib/contrast/config/diagnostics/contrast_ui.rb +24 -0
  47. data/lib/contrast/config/diagnostics/effective_config.rb +28 -0
  48. data/lib/contrast/config/diagnostics/effective_config_value.rb +14 -0
  49. data/lib/contrast/config/diagnostics/environment_variables.rb +51 -0
  50. data/lib/contrast/config/{diagnostics.rb → diagnostics/monitor.rb} +10 -10
  51. data/lib/contrast/config/diagnostics/source_config_value.rb +51 -0
  52. data/lib/contrast/config/diagnostics/tools.rb +188 -0
  53. data/lib/contrast/config/diagnostics/user_configuration_file.rb +44 -0
  54. data/lib/contrast/config/request_audit_configuration.rb +1 -1
  55. data/lib/contrast/config/server_configuration.rb +1 -1
  56. data/lib/contrast/configuration.rb +90 -57
  57. data/lib/contrast/utils/hash_utils.rb +43 -0
  58. data/lib/contrast/utils/json.rb +46 -0
  59. data/lib/contrast/utils/middleware_utils.rb +4 -4
  60. data/lib/contrast/utils/net_http_base.rb +75 -26
  61. data/lib/contrast/utils/object_share.rb +3 -3
  62. data/lib/contrast.rb +0 -16
  63. data/ruby-agent.gemspec +4 -8
  64. metadata +40 -25
  65. data/lib/contrast/config/diagnostics_tools.rb +0 -99
  66. data/lib/contrast/config/effective_config.rb +0 -131
  67. data/lib/contrast/config/effective_config_value.rb +0 -32
@@ -15,6 +15,8 @@ require 'contrast/components/protect'
15
15
  require 'contrast/components/assess'
16
16
  require 'contrast/components/config/sources'
17
17
  require 'contrast/config/server_configuration'
18
+ require 'contrast/config/configuration_files'
19
+ require 'contrast/utils/hash_utils'
18
20
 
19
21
  module Contrast
20
22
  # This is how we read in the local settings for the Agent, both ENV/ CMD line
@@ -57,7 +59,8 @@ module Contrast
57
59
  DEFAULT_YAML_PATH = 'contrast_security.yaml'
58
60
  MILLISECOND_MARKER = '_ms'
59
61
  CONVERSION = {}.cs__freeze
60
- CONFIG_BASE_PATHS = ['', 'config/', '/etc/contrast/ruby/', '/etc/contrast/', '/etc/'].cs__freeze
62
+ # Precedence of paths, shift if config file values needs to go up the chain.
63
+ CONFIG_BASE_PATHS = %w[./ config/ /etc/contrast/ruby/ /etc/contrast/ /etc/].cs__freeze
61
64
  KEYS_TO_REDACT = %i[api_key url service_key user_name].cs__freeze
62
65
  REDACTED = '**REDACTED**'
63
66
 
@@ -65,9 +68,7 @@ module Contrast
65
68
  @default_name = default_name
66
69
 
67
70
  # Load config_kv from file
68
- config_kv = deep_symbolize_all_keys(load_config)
69
- config_sources = assign_source_to(config_kv, Contrast::Components::Config::Sources::YAML)
70
-
71
+ config_kv = Contrast::Utils::HashUtils.deep_symbolize_all_keys(load_config)
71
72
  unless cli_options
72
73
  cli_options = {}
73
74
  ENV.each do |key, value|
@@ -76,19 +77,22 @@ module Contrast
76
77
  cli_options[key] = value
77
78
  end
78
79
  end
80
+
79
81
  # Overlay CLI options - they take precedence over config file
80
- cli_options = deep_symbolize_all_keys(cli_options)
82
+ cli_options = Contrast::Utils::HashUtils.deep_symbolize_all_keys(cli_options)
81
83
  if cli_options
82
- config_kv = deep_merge(cli_options, config_kv)
83
- config_sources = deep_merge(assign_source_to(cli_options, Contrast::Components::Config::Sources::CLI),
84
- config_sources)
84
+ config_kv = Contrast::Utils::HashUtils.deep_merge(cli_options, config_kv)
85
+ @_source_file_extensions = Contrast::Utils::HashUtils.
86
+ deep_merge(assign_source_to(cli_options,
87
+ Contrast::Components::Config::Sources::COMMAND_LINE),
88
+ @_source_file_extensions)
85
89
  end
86
90
 
87
91
  # Some in-flight rewrites to maintain backwards compatibility
88
92
  config_kv = update_prop_keys(config_kv)
89
93
  @loaded_config = config_kv
90
94
 
91
- @sources = Contrast::Components::Config::Sources.new(config_sources)
95
+ @sources = Contrast::Components::Config::Sources.new(@_source_file_extensions)
92
96
 
93
97
  @api = Contrast::Components::Api::Interface.new(config_kv[:api])
94
98
  @enable = config_kv[:enable]
@@ -107,6 +111,11 @@ module Contrast
107
111
  convert_to_hash.to_yaml
108
112
  end
109
113
 
114
+ # @return [Hash] map of all extensions for each config key.
115
+ def source_file_extensions
116
+ @_source_file_extensions ||= {}
117
+ end
118
+
110
119
  # @return [Contrast::Components::Api::Interface]
111
120
  def api
112
121
  @api ||= Contrast::Components::Api::Interface.new # rubocop:disable Naming/MemoizedInstanceVariableName
@@ -142,27 +151,36 @@ module Contrast
142
151
  @protect ||= Contrast::Components::Protect::Interface.new # rubocop:disable Naming/MemoizedInstanceVariableName
143
152
  end
144
153
 
145
- protected
146
-
147
- # TODO: RUBY-546 move utility methods to auxiliary classes
154
+ # Base paths to check for the contrast configuration file, sorted by
155
+ # reverse order of precedence (first is most important).
156
+ def configuration_paths
157
+ @_configuration_paths ||= begin
158
+ basename = default_name.split('.').first
159
+ # Order of extensions comes from here:
160
+ extensions = Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE.map(&:downcase)
148
161
 
149
- def load_config
150
- config = {}
151
- configuration_paths.find do |path|
152
- next unless File.exist?(path)
162
+ paths = []
163
+ # Environment paths takes precedence here. Look first through them.
164
+ paths << ENV['CONTRAST_CONFIG_PATH'] if ENV['CONTRAST_CONFIG_PATH']
165
+ paths << ENV['CONTRAST_SECURITY_CONFIG'] if ENV['CONTRAST_SECURITY_CONFIG']
153
166
 
154
- unless File.readable?(path)
155
- log_file_read_error(path)
156
- next
167
+ extensions.each do |ext|
168
+ places = CONFIG_BASE_PATHS.product(["#{ basename }.#{ ext }"])
169
+ paths += places.map!(&:join)
157
170
  end
158
- config = yaml_to_hash(path) || {}
159
- @config_file = path
160
- break
171
+ paths
161
172
  end
173
+ end
162
174
 
163
- config
175
+ # List of all read configuration files.
176
+ #
177
+ # @return [Contrast::Config::ConfigurationFiles] of paths
178
+ def origin
179
+ @_origin ||= Contrast::Config::ConfigurationFiles.new
164
180
  end
165
181
 
182
+ protected
183
+
166
184
  def yaml_to_hash path
167
185
  if path && File.readable?(path)
168
186
  begin
@@ -179,6 +197,45 @@ module Contrast
179
197
  {}
180
198
  end
181
199
 
200
+ # TODO: RUBY-546 move utility methods to auxiliary classes
201
+
202
+ # Read through all the paths we know config may live. Merge all values from all files found.
203
+ # Priority is given to yaml over yml. If same keys are found on two configs with different extensions,
204
+ # the Agent will read first from the yaml file and ignore the values for same key on the yml file.
205
+ #
206
+ # @return config [Hash] All source configurations from files.
207
+ def load_config
208
+ config = {}
209
+ @_source_file_extensions = {}
210
+ configuration_paths.each do |path|
211
+ next unless File.exist?(path)
212
+
213
+ unless File.readable?(path)
214
+ log_file_read_error(path)
215
+ next
216
+ end
217
+ origin.add_source_file(path, (yaml_to_hash(path) || {}))
218
+ end
219
+
220
+ # Legacy usage: Assign main configuration file for reference.
221
+ @config_file = origin.main_file
222
+ # merge all settings keeping the top yaml files values as priority.
223
+ # If in top file a key exists it's value won't be changed if same key has different value in one
224
+ # of the other config files. Only unique values will be taken in consideration.w
225
+ # precedence of paths: see Contrast::Configuration::CONFIG_BASE_PATHS
226
+ extensions_maps = []
227
+ origin.source_files.each do |file|
228
+ # config.merge!(file.values) { |_key, oldval, _newval| oldval = oldval }
229
+ precedence_merge!(config, file.values)
230
+ # assign source values extentions:
231
+ extensions_maps << assign_source_to(Contrast::Utils::HashUtils.deep_symbolize_all_keys(file.values), file.path)
232
+ end
233
+ # merge all origin paths to be used as extension classification to preserve the precedence of config files:
234
+ extensions_maps.each { |path| @_source_file_extensions = precedence_merge!(@_source_file_extensions, path) }
235
+
236
+ config
237
+ end
238
+
182
239
  # We're updating properties loaded from the configuration files to match the new agreed upon standard configuration
183
240
  # names, so that one file works for all agents
184
241
  def update_prop_keys config
@@ -203,39 +260,6 @@ module Contrast
203
260
  config
204
261
  end
205
262
 
206
- # Base paths to check for the contrast configuration file, sorted by
207
- # reverse order of precedence (first is most important).
208
- def configuration_paths
209
- @_configuration_paths ||= begin
210
- basename = default_name.split('.').first
211
- names = %w[yml yaml].map { |suffix| "#{ basename }.#{ suffix }" }
212
-
213
- paths = []
214
- paths << ENV['CONTRAST_CONFIG_PATH'] if ENV['CONTRAST_CONFIG_PATH']
215
- paths << ENV['CONTRAST_SECURITY_CONFIG'] if ENV['CONTRAST_SECURITY_CONFIG']
216
-
217
- tmp = CONFIG_BASE_PATHS.product(names)
218
- paths += tmp.map!(&:join)
219
- paths
220
- end
221
- end
222
-
223
- def deep_merge cli_config, file_config
224
- cli_config.merge(file_config) do |_key, cli_value, file_value|
225
- cli_value.is_a?(Hash) && file_value.is_a?(Hash) ? deep_merge(cli_value, file_value) : cli_value
226
- end
227
- end
228
-
229
- def deep_symbolize_all_keys hash
230
- return if hash.nil?
231
-
232
- new_hash = {}
233
- hash.each do |key, value|
234
- new_hash[key.to_sym] = value.is_a?(Hash) ? deep_symbolize_all_keys(value) : value
235
- end
236
- new_hash
237
- end
238
-
239
263
  private
240
264
 
241
265
  # We cannot use all access components at this point, unfortunately, as they
@@ -333,7 +357,7 @@ module Contrast
333
357
  KEYS_TO_REDACT.include?(key.to_sym)
334
358
  end
335
359
 
336
- def assign_source_to hash, source = Contrast::Components::Config::Sources::YAML
360
+ def assign_source_to hash, source = Contrast::Components::Config::Sources::APP_CONFIGURATION_FILE[0]
337
361
  hash.transform_values do |value|
338
362
  if value.is_a?(Hash)
339
363
  assign_source_to(value, source)
@@ -342,5 +366,14 @@ module Contrast
342
366
  end
343
367
  end
344
368
  end
369
+
370
+ # Merges two hashes, first hash will preserve it's values and will only add unique values.
371
+ #
372
+ # @param hsh [Hash]
373
+ # @param other_hsh [Hash]
374
+ # @return [Hash]
375
+ def precedence_merge! hsh, other_hsh
376
+ hsh.merge!(other_hsh) { |_key, old_val, _new_val| old_val }
377
+ end
345
378
  end
346
379
  end
@@ -0,0 +1,43 @@
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 Utils
6
+ # Module to hold various hash utils methods
7
+ module HashUtils
8
+ class << self
9
+ # Merges two hashes, first hash will preserve it's values and will only add unique values.
10
+ #
11
+ # @param hsh [Hash]
12
+ # @param other_hsh [Hash]
13
+ def deep_merge hsh, other_hsh
14
+ hsh.merge(other_hsh) do |_key, old_value, new_value|
15
+ old_value.is_a?(Hash) || new_value.is_a?(Hash) ? deep_merge(old_value, new_value) : old_value
16
+ end
17
+ end
18
+
19
+ # Deep symbolizes all keys
20
+ # @param value [Hash]
21
+ # @return [Hash]
22
+ def deep_symbolize_all_keys value
23
+ new_hash = {}
24
+ value.each { |key, v| new_hash[key.to_sym] = map_value(v) }
25
+ new_hash
26
+ end
27
+
28
+ private
29
+
30
+ def map_value value
31
+ case value
32
+ when Hash
33
+ deep_symbolize_all_keys(value)
34
+ when Array
35
+ value.map { |v| map_value(v) }
36
+ else
37
+ value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,46 @@
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 'json'
5
+ require 'contrast/utils/hash_utils'
6
+ require 'contrast/components/logger'
7
+
8
+ module Contrast
9
+ module Utils
10
+ # Module to hold Agent's custom JSON.parse logic.
11
+ module Json
12
+ class << self
13
+ include Contrast::Components::Logger::InstanceMethods
14
+
15
+ # Add any known cases where parsing error might arise from older json parser:
16
+ # @return [Array<String>]
17
+ SPECIAL_CASES = ["\"\""].cs__freeze # rubocop:disable Style/StringLiterals
18
+
19
+ # Parses a string using JSON.parser. This method is used instead of standard JSON.parse to
20
+ # support older versions of json gem => not supporting key-value second parameter, which is
21
+ # supported after json 2.3.0.
22
+ #
23
+ # @param string [String]
24
+ # @param deep_symbolize [Boolean] flag to set if keys needs to be deep symbolized.
25
+ # @return [Hash]
26
+ def parse string, deep_symbolize: false
27
+ # The Agent receives empty responses from TS sometimes.
28
+ # There is a special case to handle this.
29
+ return {} if SPECIAL_CASES.include?(string)
30
+
31
+ symbolized_hash = {}
32
+ hash = JSON::Parser.new(string).parse
33
+ symbolized_hash = Contrast::Utils::HashUtils.deep_symbolize_all_keys(hash) if deep_symbolize
34
+ return hash unless deep_symbolize
35
+
36
+ symbolized_hash
37
+ rescue JSON::ParserError => e
38
+ # Any parsing error will produce empty {}. Since older JSON might pose support issues with newer Ruby,
39
+ # We might just log any miss-parsings and allow Agent to continue.
40
+ logger.warn("[JSON] parse error: #{ e }")
41
+ {}
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -11,12 +11,12 @@ module Contrast
11
11
  module MiddlewareUtils
12
12
  private
13
13
 
14
- # TODO: RUBY-1609 update this part of the method for Ruby Version and Year
15
- LANGUAGE_DEPRECATION_VERSION = '2.7'
16
- LANGUAGE_DEPRECATION_YEAR = '2023'
14
+ LANGUAGE_DEPRECATION_VERSION = '3.0'
15
+ LANGUAGE_DEPRECATION_YEAR = '2024'
17
16
  LANGUAGE_DEPRECATION_WARNING =
18
17
  "[Contrast Security] [DEPRECATION] Support for Ruby #{ LANGUAGE_DEPRECATION_VERSION } will be removed in " \
19
- "April #{ LANGUAGE_DEPRECATION_YEAR }. Please contact Customer Support prior if you require continued support."
18
+ "April #{ LANGUAGE_DEPRECATION_YEAR }. Please contact Customer Support prior if you require continued" \
19
+ 'support.'.cs__freeze
20
20
 
21
21
  def setup_agent
22
22
  # Generate new config file if one is not already created:
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'net/http'
5
+ require 'resolv'
5
6
  require 'contrast/components/logger'
6
7
  require 'contrast/utils/object_share'
7
8
  require 'socket'
@@ -10,8 +11,25 @@ module Contrast
10
11
  module Utils
11
12
  # This module creates a Net::HTTP client base to be used by different services
12
13
  # All HTTP clients reporting to Telemetry or TS should inherit from this
13
- class NetHttpBase
14
+ class NetHttpBase # rubocop:disable Metrics/ClassLength
14
15
  include Contrast::Components::Logger::InstanceMethods
16
+
17
+ class << self
18
+ # Last recorded error
19
+ # @return [StandardError]
20
+ def last_error
21
+ @_last_error
22
+ end
23
+
24
+ # @param [StandardError]
25
+ def last_error= error
26
+ @_last_error = error
27
+ end
28
+ end
29
+
30
+ # @return [String]
31
+ attr_reader :client_name
32
+
15
33
  # This method initializes the Net::HTTP client we'll need. it will validate
16
34
  # the connection and make the first request. If connection is valid and response
17
35
  # is available then the open connection is returned.
@@ -23,29 +41,22 @@ module Contrast
23
41
  # self signed certificates provided by config [default = false]
24
42
  # @return [Net::HTTP, nil] Return open connection or nil
25
43
  def initialize_connection service_name, url, use_proxy: false, use_custom_cert: false
26
- return unless url
27
-
28
- addr = URI(url)
29
- return if addr.host.nil? || addr.port.nil?
30
- return if addr.scheme != 'https' && !addr.host.to_s.include?('localhost')
31
-
32
- # the proxy is enabled only if there is provided url even if the enable is set to true
33
- proxy_addr = URI(Contrast::API.proxy_url) if proxy_enabled?
34
- net_http_client = initialize_client(addr, proxy_addr, use_proxy, use_custom_cert)
35
- return if net_http_client.nil?
36
-
37
- net_http_client.start
38
- return unless net_http_client.started?
44
+ Contrast::Utils::NetHttpBase.last_error = nil
45
+ @client_name = service_name
46
+ return unless (addr = retrieve_address(url))
47
+ return unless (net_http_client = configure_new_client(addr, use_proxy, use_custom_cert))
48
+ return unless client_started?(net_http_client)
39
49
 
40
- logger.debug("Starting #{ service_name } connection test")
50
+ logger.debug("Starting #{ client_name } connection test")
41
51
  return unless connection_verified?(net_http_client, url)
42
52
 
43
- logger.debug('Client verified', service: service_name, url: url)
53
+ logger.debug('Client verified', service: client_name, url: url)
44
54
  net_http_client
45
55
  rescue StandardError => e
46
- # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
47
- # an infinite loop w/ telemetry
48
- logger.debug('Connection failed', e, service: service_name, url: url)
56
+ Contrast::Utils::NetHttpBase.last_error = e
57
+ return if client_name == Contrast::Agent::Telemetry::Client::SERVICE_NAME
58
+
59
+ logger.error('Connection failed', e, service: client_name, url: url)
49
60
  nil
50
61
  end
51
62
 
@@ -74,14 +85,51 @@ module Contrast
74
85
  Errno::ETIMEDOUT, Errno::ESHUTDOWN, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::EISCONN,
75
86
  Errno::ECONNABORTED, Errno::ENETRESET, Errno::ENETUNREACH => e
76
87
 
77
- # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
78
- # an infinite loop w/ telemetry
79
- logger.debug("#{ service_name } connection failed", e.message)
80
- false
88
+ Contrast::Utils::NetHttpBase.last_error = e
89
+ unless client_name == Contrast::Agent::Telemetry::Client::SERVICE_NAME
90
+ logger.error("#{ client_name } connection failed", e.message)
91
+ end
92
+ @_connection_verified = false
81
93
  end
82
94
 
83
95
  private
84
96
 
97
+ # Starts connection and return started status.
98
+ #
99
+ # @param client [Net::HTTP] client instance.
100
+ # @return [Boolean] status indicates whether connection has started.
101
+ def client_started? client
102
+ return false unless client
103
+
104
+ client.start
105
+ client.started?
106
+ end
107
+
108
+ # @param url
109
+ # @return [URI::Generic, nil]
110
+ def retrieve_address url
111
+ return unless (addr = URI(url))
112
+ return if addr.host.nil? || addr.port.nil?
113
+ return if addr.scheme != 'https' && !addr.host.to_s.include?('localhost')
114
+
115
+ addr
116
+ end
117
+
118
+ # Assigns proxy and custom certificates if enabled, and initializes new client.
119
+ #
120
+ # @param addr [URI::Generic, nil]
121
+ # @param use_proxy [Boolean] flag used to indicate proxy connections [default = false]
122
+ # @param use_custom_cert [Boolean] flag used to indicate whether the client is to use
123
+ # @return client [Net::HTTP, nil] initialized client.
124
+ def configure_new_client addr, use_proxy, use_custom_cert
125
+ # the proxy is enabled only if there is provided url even if the enable is set to true
126
+ proxy_addr = URI(Contrast::API.proxy_url) if proxy_enabled?
127
+ net_http_client = initialize_client(addr, proxy_addr, use_proxy, use_custom_cert)
128
+ return if net_http_client.nil?
129
+
130
+ net_http_client
131
+ end
132
+
85
133
  # Resolves the address of the assigned domain to array of corresponding IPs (if more than one)
86
134
  # and runs a matcher to see if current connection IP is in the list.
87
135
  # This is called within #verify_connection, if called on it's own there will be no
@@ -114,9 +162,10 @@ module Contrast
114
162
  client.key = OpenSSL::PKey::RSA.new(File.read(Contrast::API.certification_key_file)).to_s
115
163
  end
116
164
  rescue Errno::ENOENT => e
117
- # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
118
- # an infinite loop w/ telemetry
119
- logger.debug('Custom certificates failed', e.message)
165
+ Contrast::Utils::NetHttpBase.last_error = e
166
+ unless client_name == Contrast::Agent::Telemetry::Client::SERVICE_NAME
167
+ logger.error('Custom certificates failed', e.message)
168
+ end
120
169
  end
121
170
 
122
171
  # sets default setting for client validation of certificates and
@@ -55,9 +55,9 @@ module Contrast
55
55
  EMPTY_HASH = {}.freeze
56
56
 
57
57
  # RegExps
58
- DIGIT_REGEXP = /[[:digit:]]/.freeze
59
- WHITE_SPACE_REGEXP = /\s/.freeze
60
- NOT_WHITE_SPACE_REGEXP = /[^\s]/.freeze
58
+ DIGIT_REGEXP = /[[:digit:]]/
59
+ WHITE_SPACE_REGEXP = /\s/
60
+ NOT_WHITE_SPACE_REGEXP = /[^\s]/
61
61
 
62
62
  # Messages
63
63
  OVERRIDE_MESSAGE = 'A security filter prevented original response from being returned.'
data/lib/contrast.rb CHANGED
@@ -102,19 +102,3 @@ if RUBY_VERSION >= '3.0.0' && RUBY_VERSION < '3.1.0'
102
102
  Class.alias_method(:prepend, :cs__orig_prepend)
103
103
  Class.remove_method(:cs__orig_prepend)
104
104
  end
105
-
106
- if RUBY_VERSION < '3.0.0'
107
- # Better handles ancestors for older ruby versions.
108
- # This is called from C, tread lightly.
109
- class Module
110
- @_included_in = []
111
- # Returns array with modules including this instance
112
- def included_in
113
- @_included_in ||= [] unless cs__frozen?
114
- end
115
-
116
- def self.included_in
117
- @_included_in ||= [] unless cs__frozen?
118
- end
119
- end
120
- end
data/ruby-agent.gemspec CHANGED
@@ -51,7 +51,7 @@ end
51
51
  def self.add_frameworks spec
52
52
  spec.add_development_dependency 'grape', '~> 1.5', '>= 1.5.2'
53
53
  spec.add_development_dependency 'rack-protection', '>= 2'
54
- spec.add_development_dependency 'rails', '~> 7'
54
+ spec.add_development_dependency 'rails', '>= 6', '~> 7'
55
55
  spec.add_development_dependency 'sinatra', '>= 2'
56
56
  end
57
57
 
@@ -80,7 +80,7 @@ def self.add_specs spec
80
80
  spec.add_development_dependency 'rspec', '~> 3.0'
81
81
  spec.add_development_dependency 'rspec-benchmark'
82
82
  spec.add_development_dependency 'rspec_junit_formatter', '0.3.0'
83
- spec.add_development_dependency 'rspec-rails', '5.0'
83
+ spec.add_development_dependency 'rspec-rails', '6.0'
84
84
  spec.add_development_dependency 'tzinfo-data' # Alpine rspec-rails requirement.
85
85
  spec.add_development_dependency 'warning'
86
86
  spec.add_development_dependency 'typhoeus', '~> 1.4'
@@ -102,11 +102,7 @@ end
102
102
 
103
103
  # Dependencies not mocked out during RSpec that we test real code of, beyond just frameworks.
104
104
  def self.add_tested_gems spec
105
- if RUBY_VERSION < '3.0.0'
106
- spec.add_development_dependency 'async', '~> 1.30.3'
107
- else
108
- spec.add_development_dependency 'async'
109
- end
105
+ spec.add_development_dependency 'async'
110
106
  spec.add_development_dependency 'execjs'
111
107
  spec.add_development_dependency 'rhino'
112
108
  spec.add_development_dependency 'sqlite3'
@@ -181,7 +177,7 @@ Gem::Specification.new do |spec|
181
177
  'Testing and Protection.'
182
178
  spec.homepage = 'https://www.contrastsecurity.com'
183
179
  spec.license = 'CONTRAST SECURITY (see license file)'
184
- spec.required_ruby_version = ['>= 2.7.0', '< 3.3.0']
180
+ spec.required_ruby_version = ['>= 3.0.0', '< 3.3.0']
185
181
 
186
182
  spec.bindir = 'exe'
187
183
  # Keep cs__common first, it handles funchook.h right now.