contrast-agent 6.10.0 → 6.11.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/ext/build_funchook.rb +1 -1
  3. data/lib/contrast/agent/assess/policy/propagator/split.rb +1 -4
  4. data/lib/contrast/agent/middleware.rb +1 -1
  5. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +6 -2
  6. data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
  7. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +2 -2
  8. data/lib/contrast/agent/protect/rule/base_service.rb +1 -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/cmdi/cmdi_chained_command.rb +1 -1
  12. data/lib/contrast/agent/protect/rule/cmdi/cmdi_dangerous_path.rb +1 -1
  13. data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +0 -1
  14. data/lib/contrast/agent/reporting/reporter.rb +4 -8
  15. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +9 -5
  16. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +8 -5
  17. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +7 -7
  18. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +4 -4
  19. data/lib/contrast/agent/telemetry/base.rb +2 -5
  20. data/lib/contrast/agent/version.rb +1 -1
  21. data/lib/contrast/components/config.rb +1 -1
  22. data/lib/contrast/framework/rack/patch/session_cookie.rb +2 -1
  23. data/lib/contrast/utils/duck_utils.rb +18 -0
  24. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  25. data/lib/contrast/utils/log_utils.rb +1 -1
  26. data/lib/contrast/utils/middleware_utils.rb +1 -1
  27. data/lib/contrast/utils/patching/policy/patch_utils.rb +2 -2
  28. data/lib/contrast/utils/routes_sent.rb +4 -0
  29. data/lib/contrast/utils/telemetry.rb +1 -1
  30. data/ruby-agent.gemspec +5 -5
  31. metadata +14 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 624b29e40ff797608bb6fef0ab00377cc1bc3e6756af01063d4768fad53bfbfa
4
- data.tar.gz: 7380498d855e3f6b8a7387ee98351d118b6b70b7bc332536bf1124a870c1a48c
3
+ metadata.gz: fddcf57afedf79d84cacef08c959a190f29f6db9248d34d1a01c3abe1b13553b
4
+ data.tar.gz: 599882cd812552c2cc12c86f6ecd526e7a7d1e458cf8ea4d961d17ef5179c9ad
5
5
  SHA512:
6
- metadata.gz: 44d0b0cc41d92f58cf048212295fdef8367a4aa4475e3d035c9ae09c80bbcad9de08ba2a63f321381de4af41fd90b581dca2710ef3641d8eb486e8664a3d18cf
7
- data.tar.gz: cb2f9e2799f8ef7fff7da2ba6714e94022ad5495ddc57c448244c34f649cb70eb756d4f806c1f1274676cbdd37749bf0177cd8539b175ba7b8086857bd1f86a3
6
+ metadata.gz: e91dd42c9aa9f9cb934bfe878e0f6be951f9397978c874dd2a4b38c0a78f1cfe6b7e957ec6abaa3bb97a194f6494950f9ec93ebad408cf2767df06b1475bd373
7
+ data.tar.gz: ad17804c1abf24410ba2bbe7798ce142eba284cb0911830e40f6f92917a47961d4cfaefa79ed8affb8d1d821e27a85f57ace126a64417f6fe672f965916c3dba
@@ -17,7 +17,7 @@ unless find_header('funchook.h', ext_path)
17
17
  extension_paths = Dir[contrast_gem_dir_search]
18
18
  extension_paths.map! do |extension_path|
19
19
  target_path = File.join(extension_path, 'shared_libraries')
20
- FileUtils.mkdir_p(target_path) unless File.exist?(target_path)
20
+ FileUtils.mkdir_p(target_path)
21
21
  target_path
22
22
  end
23
23
  bundler_install_target_paths += extension_paths
@@ -17,12 +17,11 @@ module Contrast
17
17
  module Propagator
18
18
  # This class is specifically for String#split & String#grapheme_clusters propagation
19
19
  # it propagates tag ranges from a string to elements within an untracked array
20
- class Split < Contrast::Agent::Assess::Policy::Propagator::Base
20
+ class Split < Contrast::Agent::Assess::Policy::Propagator::Base # rubocop:disable Metrics/ClassLength
21
21
  extend Contrast::Components::Scope::InstanceMethods
22
22
  extend Contrast::Components::Logger::InstanceMethods
23
23
  extend Contrast::Utils::Assess::SplitUtils
24
24
  extend Contrast::Utils::Assess::EventLimitUtils
25
-
26
25
  SPLIT_TRACKER = Contrast::Utils::ThreadTracker.new
27
26
 
28
27
  class << self
@@ -61,7 +60,6 @@ module Contrast
61
60
  rescue Exception => e # rubocop:disable Lint/RescueException
62
61
  logger.warn('Unable to record split context', e)
63
62
  end
64
-
65
63
  yield
66
64
  ensure
67
65
  # String#split exit. Remove propagation context.
@@ -158,7 +156,6 @@ module Contrast
158
156
  # Get tags for element from source by element range.
159
157
  range = current_index...(current_index + target_elem.length)
160
158
  tags = source_properties.tags_at_range(range)
161
-
162
159
  # Set element properties accordingly.
163
160
  elem_properties.clear_tags
164
161
  tags.each_pair { |key, value| elem_properties.set_tags(key, value) }
@@ -44,7 +44,7 @@ module Contrast
44
44
  @app = app # THIS MUST BE FIRST AND ALWAYS SET!
45
45
  setup_agent # THIS MUST BE SECOND AND ALWAYS CALLED!
46
46
  unless ::Contrast::AGENT.enabled?
47
- logger.error('The Agent was unable to initialize before the application middleware was initialized. '\
47
+ logger.error('The Agent was unable to initialize before the application middleware was initialized. ' \
48
48
  'Disabling permanently.')
49
49
  ::Contrast::AGENT.disable! # ensure the agent is disabled (probably redundant)
50
50
  return
@@ -57,8 +57,12 @@ module Contrast
57
57
  nodes.find { |node| node }&.method_visibility
58
58
  end
59
59
 
60
- def check_method_policy_nodes_empty?(source_node, propagation_node, trigger_node, protect_node,
61
- inventory_node, deadzone_node)
60
+ def check_method_policy_nodes_empty?(source_node,
61
+ propagation_node,
62
+ trigger_node,
63
+ protect_node,
64
+ inventory_node,
65
+ deadzone_node)
62
66
  return false unless source_node.nil? && propagation_node.nil? && trigger_node.nil? && protect_node.nil? &&
63
67
  inventory_node.nil? && deadzone_node.nil?
64
68
 
@@ -46,7 +46,7 @@ module Contrast
46
46
  super
47
47
  unless applicator.public_methods(false).any?(applicator_method)
48
48
  raise(ArgumentError,
49
- "#{ id } did not have a proper applicator method: "\
49
+ "#{ id } did not have a proper applicator method: " \
50
50
  "#{ applicator } does not respond to #{ applicator_method }. Unable to create.")
51
51
  end
52
52
  validate_properties
@@ -101,10 +101,10 @@ module Contrast
101
101
  CS__SAFER_REL_PATHS = %w[public app log tmp].cs__freeze
102
102
  def safer_abs_paths
103
103
  @_safer_abs_paths ||= begin
104
- pwd = ENV['PWD']
104
+ pwd = ENV.fetch('PWD', nil)
105
105
  if pwd
106
106
  tmp = CS__SAFER_REL_PATHS.map { |r| "#{ pwd }/#{ r }" }
107
- gems = ENV['GEM_PATH']
107
+ gems = ENV.fetch('GEM_PATH', nil)
108
108
  tmp += gems.split(Contrast::Utils::ObjectShare::COLON) if gems
109
109
  tmp.map!(&:downcase)
110
110
  tmp
@@ -91,8 +91,7 @@ module Contrast
91
91
  return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless context&.agent_input_analysis&.results
92
92
 
93
93
  context.agent_input_analysis.results.select do |ia_result|
94
- ia_result.rule_id == rule_name &&
95
- ia_result.score_level != Contrast::Agent::Reporting::ScoreLevel::IGNORE
94
+ ia_result.rule_id == rule_name && ia_result.score_level != Contrast::Agent::Reporting::ScoreLevel::IGNORE
96
95
  end
97
96
  end
98
97
 
@@ -70,7 +70,7 @@ module Contrast
70
70
  # @raise [Contrast::SecurityException]
71
71
  def raise_error classname, method
72
72
  raise(Contrast::SecurityException.new(self,
73
- 'Command Injection Command Backdoor rule triggered. '\
73
+ 'Command Injection Command Backdoor rule triggered. ' \
74
74
  "Call to #{ classname }.#{ method } blocked."))
75
75
  end
76
76
 
@@ -78,7 +78,7 @@ module Contrast
78
78
  # @raise [Contrast::SecurityException]
79
79
  def raise_error classname, method
80
80
  raise(Contrast::SecurityException.new(self,
81
- 'Command Injection Rule triggered. '\
81
+ 'Command Injection Rule triggered. ' \
82
82
  "Call to #{ classname }.#{ method } blocked."))
83
83
  end
84
84
 
@@ -32,7 +32,7 @@ module Contrast
32
32
  # @raise [Contrast::SecurityException]
33
33
  def raise_error classname, method
34
34
  raise(Contrast::SecurityException.new(self,
35
- 'Command Injection Semantic Chained Commands rule triggered. '\
35
+ 'Command Injection Semantic Chained Commands rule triggered. ' \
36
36
  "Call to #{ classname }.#{ method } blocked."))
37
37
  end
38
38
 
@@ -32,7 +32,7 @@ module Contrast
32
32
  # @raise [Contrast::SecurityException]
33
33
  def raise_error classname, method
34
34
  raise(Contrast::SecurityException.new(self,
35
- 'Command Injection Dangerous Path rule triggered. '\
35
+ 'Command Injection Dangerous Path rule triggered. ' \
36
36
  "Call to #{ classname }.#{ method } blocked."))
37
37
  end
38
38
 
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
5
- # frozen_string_literal: true
6
5
 
7
6
  module Contrast
8
7
  module Agent
@@ -73,13 +73,11 @@ module Contrast
73
73
  return unless event
74
74
 
75
75
  if queue.size >= MAX_QUEUE_SIZE
76
- if Contrast::Agent::Telemetry::Base.enabled?
77
- Contrast::Agent.thread_watcher.telemetry_queue.send_event(queue_limit_telemetry_event)
78
- end
76
+ Contrast::Agent::Telemetry::Base.enabled? && Contrast::Agent.thread_watcher.telemetry_queue.
77
+ send_event(queue_limit_telemetry_event)
79
78
 
80
79
  return
81
80
  end
82
-
83
81
  queue << event
84
82
  end
85
83
 
@@ -89,9 +87,8 @@ module Contrast
89
87
  # @return [Net::HTTPResponse, nil]
90
88
  def send_event_immediately event
91
89
  if ::Contrast::AGENT.disabled?
92
- logger.warn('[Reporter] attempted to send event immediately with Agent disabled',
93
- caller: caller,
94
- event: event)
90
+ logger.warn('[Reporter] attempted to send event immediately with Agent disabled', caller: caller,
91
+ event: event)
95
92
  return
96
93
  end
97
94
  return unless event
@@ -157,7 +154,6 @@ module Contrast
157
154
  else
158
155
  stack_trace[1].path.delete_prefix(Dir.pwd)
159
156
  end
160
-
161
157
  stack_frame_function = stack_trace.nil? || stack_trace[1].nil? ? 'none' : stack_trace[1].label
162
158
  Contrast::Agent::Telemetry::TelemetryException::StackFrame.build(stack_frame_function, stack_frame_type, nil)
163
159
  end
@@ -5,6 +5,7 @@ require 'json'
5
5
  require 'contrast/components/logger'
6
6
  require 'contrast/agent/reporting/reporting_events/reporting_event'
7
7
  require 'contrast/utils/object_share'
8
+ require 'contrast/utils/duck_utils'
8
9
 
9
10
  module Contrast
10
11
  module Agent
@@ -16,9 +17,9 @@ module Contrast
16
17
  # includes the literal URL and HTTP Verb used to invoke them, as they must have been called at this point to be
17
18
  # recorded.
18
19
  class DiscoveredRoute < Contrast::Agent::Reporting::ObservedRoute
19
- include Contrast::Components::Logger::InstanceMethods
20
-
21
20
  class << self
21
+ include Contrast::Components::Logger::InstanceMethods
22
+
22
23
  # @param obj [Regexp, Object]
23
24
  # @return [String]
24
25
  def source_or_string obj
@@ -100,9 +101,7 @@ module Contrast
100
101
  def initialize
101
102
  super
102
103
  @event_type = :discovered_route
103
- @signature = Contrast::Utils::ObjectShare::EMPTY_STRING
104
104
  @verb = Contrast::Utils::ObjectShare::EMPTY_STRING
105
- @url = Contrast::Utils::ObjectShare::EMPTY_STRING
106
105
  end
107
106
 
108
107
  def to_controlled_hash
@@ -116,7 +115,12 @@ module Contrast
116
115
  end
117
116
 
118
117
  def validate
119
- raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.") unless signature
118
+ if Contrast::Utils::DuckUtils.empty_duck?(signature)
119
+ raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.")
120
+ end
121
+ if Contrast::Utils::DuckUtils.empty_duck?(url)
122
+ raise(ArgumentError, "#{ self } did not have a proper url. Unable to continue.")
123
+ end
120
124
 
121
125
  nil
122
126
  end
@@ -6,6 +6,7 @@ require 'json'
6
6
  require 'contrast/components/logger'
7
7
  require 'contrast/agent/reporting/reporting_events/application_reporting_event'
8
8
  require 'contrast/utils/object_share'
9
+ require 'contrast/utils/duck_utils'
9
10
 
10
11
  module Contrast
11
12
  module Agent
@@ -32,9 +33,7 @@ module Contrast
32
33
  def initialize
33
34
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.observed_route
34
35
  @sources = []
35
- @signature = Contrast::Utils::ObjectShare::EMPTY_STRING
36
36
  @verb = Contrast::Utils::ObjectShare::EMPTY_STRING
37
- @url = Contrast::Utils::ObjectShare::EMPTY_STRING
38
37
  super()
39
38
  end
40
39
 
@@ -69,7 +68,7 @@ module Contrast
69
68
  # @return [String]
70
69
  #
71
70
  def hash_id
72
- hashable_data = to_controlled_hash.except(:session_id)
71
+ hashable_data = to_controlled_hash.reject { |key, _value| key == :session_id } # rubocop:disable Style/HashExcept
73
72
  hashable_data[:sources] = hashable_data[:sources].sort_by { |s| s[:name] }
74
73
 
75
74
  Digest::SHA2.new(256).hexdigest(hashable_data.to_s)
@@ -78,8 +77,12 @@ module Contrast
78
77
  # @raise [ArgumentError]
79
78
  def validate
80
79
  raise(ArgumentError, "#{ self } did not have a proper sources. Unable to continue.") if @sources.nil?
81
- raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.") unless signature
82
- raise(ArgumentError, "#{ self } did not have a proper url. Unable to continue.") unless url
80
+ if Contrast::Utils::DuckUtils.empty_duck?(signature)
81
+ raise(ArgumentError, "#{ self } did not have a proper signature. Unable to continue.")
82
+ end
83
+ if Contrast::Utils::DuckUtils.empty_duck?(url)
84
+ raise(ArgumentError, "#{ self } did not have a proper url. Unable to continue.")
85
+ end
83
86
 
84
87
  nil
85
88
  end
@@ -89,7 +89,7 @@ module Contrast
89
89
  #
90
90
  # @return [String]
91
91
  def application_endpoint
92
- @_application_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/applications"\
92
+ @_application_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/applications" \
93
93
  "#{ server_path_segment }#{ application_path_segment }"
94
94
  end
95
95
 
@@ -97,7 +97,7 @@ module Contrast
97
97
  #
98
98
  # @return [String]
99
99
  def route_endpoint
100
- @_route_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/routes"\
100
+ @_route_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/routes" \
101
101
  "#{ server_path_segment }#{ application_path_segment }"
102
102
  end
103
103
 
@@ -105,7 +105,7 @@ module Contrast
105
105
  #
106
106
  # @return [String]
107
107
  def server_endpoint
108
- @_server_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/servers"\
108
+ @_server_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/servers" \
109
109
  "#{ server_path_segment }"
110
110
  end
111
111
 
@@ -113,7 +113,7 @@ module Contrast
113
113
  #
114
114
  # @return [String]
115
115
  def trace_endpoint
116
- @_trace_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/traces"\
116
+ @_trace_endpoint ||= "#{ Contrast::API.api_url }/agents#{ ENDPOINT_VERSION }/traces" \
117
117
  "#{ server_path_segment }#{ application_path_segment }"
118
118
  end
119
119
 
@@ -121,8 +121,8 @@ module Contrast
121
121
  #
122
122
  # @return [String]
123
123
  def server_path_segment
124
- @_server_path_segment ||= "/#{ Base64.urlsafe_encode64(server_host_name, padding: false) }"\
125
- "/#{ Base64.urlsafe_encode64(server_path, padding: false) }"\
124
+ @_server_path_segment ||= "/#{ Base64.urlsafe_encode64(server_host_name, padding: false) }" \
125
+ "/#{ Base64.urlsafe_encode64(server_path, padding: false) }" \
126
126
  "/#{ Base64.urlsafe_encode64(server_type, padding: false) }"
127
127
  end
128
128
 
@@ -130,7 +130,7 @@ module Contrast
130
130
  #
131
131
  # @return [String]
132
132
  def application_path_segment
133
- @_application_path_segment ||= "/#{ Base64.urlsafe_encode64(APP_LANGUAGE, padding: false) }"\
133
+ @_application_path_segment ||= "/#{ Base64.urlsafe_encode64(APP_LANGUAGE, padding: false) }" \
134
134
  "/#{ Base64.urlsafe_encode64(app_name, padding: false) }"
135
135
  end
136
136
 
@@ -22,14 +22,14 @@ module Contrast
22
22
  unprocessable_entity: '422',
23
23
  too_many_requests: '429'
24
24
  }.cs__freeze
25
- APP_NON_EXISTENT_MSG = 'Application does not exist! Either it has not been created or has '\
26
- 'been deleted or archived. '\
25
+ APP_NON_EXISTENT_MSG = 'Application does not exist! Either it has not been created or has ' \
26
+ 'been deleted or archived. ' \
27
27
  'Disabling permanently.'
28
28
  SUSPEND_MSG = 'Reporter is temporarily suspended.'
29
29
  UNSUCCESSFULLY_RECEIVED_MSG = 'The Reporter was unable to send message.'
30
- FORBIDDEN_MSG = 'Access was forbidden for current Report because the request authentication '\
30
+ FORBIDDEN_MSG = 'Access was forbidden for current Report because the request authentication ' \
31
31
  'information was not provided'
32
- FORBIDDEN_NO_ACTION_MSG = 'Report access was forbidden because the supplied credentials failed '\
32
+ FORBIDDEN_NO_ACTION_MSG = 'Report access was forbidden because the supplied credentials failed ' \
33
33
  'to authenticate the Agent'
34
34
  UNPROCESSABLE_ENTITY_MSG = 'Reporter received Unprocessable Entity response. Disabling permanently.'
35
35
  RETRY_AFTER_MSG = "There are too many requests of this type being sent by this Agent. #{ SUSPEND_MSG }"
@@ -14,7 +14,6 @@ module Contrast
14
14
  # This class will initialize and hold everything needed for the telemetry
15
15
  class Base < WorkerThread
16
16
  include Contrast::Components::Logger::InstanceMethods
17
-
18
17
  # this is where we will send the data from the agents
19
18
  URL = 'https://telemetry.ruby.contrastsecurity.com/'
20
19
  # Suggested timeout after each send is to be 3 hours (10800 seconds)
@@ -108,8 +107,7 @@ module Contrast
108
107
  end
109
108
 
110
109
  def request_with_response event
111
- res = client.send_request(event, connection)
112
- client.handle_response(res)
110
+ client.handle_response(client.send_request(event, connection))
113
111
  end
114
112
 
115
113
  private
@@ -134,8 +132,7 @@ module Contrast
134
132
  event = queue.pop
135
133
  begin
136
134
  logger.debug('[Telemetry] This is the current processed event', event)
137
- sleep_time = request_with_response(event)
138
- if sleep_time
135
+ if (sleep_time = request_with_response(event))
139
136
  sleep(sleep_time)
140
137
  logger.debug('[Telemetry] Retrying to process event', event)
141
138
  retry_sleep_time = request_with_response(event)
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.10.0'
6
+ VERSION = '6.11.0'
7
7
  end
8
8
  end
@@ -30,7 +30,7 @@ module Contrast
30
30
  DATE_TIME = '%Y-%m-%dT%H:%M:%S.%L%z'
31
31
 
32
32
  class Interface # :nodoc: # rubocop:disable Metrics/ClassLength
33
- SESSION_VARIABLES = 'Invalid configuration. '\
33
+ SESSION_VARIABLES = 'Invalid configuration. ' \
34
34
  "Setting both application.session_id and application.session_metadata is not allowed.\n"
35
35
  API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
36
36
  API_KEY = "Invalid configuration. Missing a required connection value 'api_key' is not set."
@@ -48,7 +48,8 @@ module Contrast
48
48
 
49
49
  def vulnerable_setting?(setting_key,
50
50
  safe_settings_value,
51
- options, safe_default: true,
51
+ options,
52
+ safe_default: true,
52
53
  comparison_type: nil)
53
54
  # In most cases, Rack is pretty nice and the default value is safe
54
55
  return !safe_default unless options&.key?(setting_key)
@@ -63,6 +63,24 @@ module Contrast
63
63
  # otherwise, don't risk it
64
64
  false
65
65
  end
66
+
67
+ # Every duck quacks to nil, we need to check if it is empty?
68
+ # Utils method to check for blank ( nil or empty object ).
69
+ #
70
+ # @param [Object] object to test.
71
+ # @return [Boolean]
72
+ def empty_duck? object
73
+ # If not quacking to empty it is True/False class, Integer, Regexp or Time instance.
74
+ return false if object.instance_of?(TrueClass) || object.instance_of?(FalseClass)
75
+
76
+ if object.cs__respond_to?(:empty?)
77
+ return true if object.empty?
78
+ elsif object.nil?
79
+ return true
80
+ end
81
+
82
+ false
83
+ end
66
84
  end
67
85
  end
68
86
  end
@@ -35,7 +35,7 @@ module Contrast
35
35
  control = Contrast::Utils::HeapDumpUtil.control
36
36
  log_enabled_warning
37
37
  dir = control[:path]
38
- Dir.mkdir(dir) unless Dir.exist?(dir)
38
+ FileUtils.mkdir_p(dir)
39
39
  return unless File.writable?(dir)
40
40
 
41
41
  delay = control[:delay]
@@ -26,7 +26,7 @@ module Contrast
26
26
  return File.writable?(path) if File.exist?(path)
27
27
 
28
28
  dir_name = File.dirname(File.absolute_path(path))
29
- FileUtils.mkdir_p(dir_name) unless Dir.exist?(dir_name)
29
+ FileUtils.mkdir_p(dir_name)
30
30
  File.writable?(dir_name)
31
31
  end
32
32
 
@@ -12,7 +12,7 @@ module Contrast
12
12
  LANGUAGE_DEPRECATION_VERSION = '2.7'
13
13
  LANGUAGE_DEPRECATION_YEAR = '2023'
14
14
  LANGUAGE_DEPRECATION_WARNING =
15
- "[Contrast Security] [DEPRECATION] Support for Ruby #{ LANGUAGE_DEPRECATION_VERSION } will be removed in "\
15
+ "[Contrast Security] [DEPRECATION] Support for Ruby #{ LANGUAGE_DEPRECATION_VERSION } will be removed in " \
16
16
  "April #{ LANGUAGE_DEPRECATION_YEAR }. Please contact Customer Support prior if you require continued support."
17
17
 
18
18
  def setup_agent
@@ -23,8 +23,8 @@ module Contrast
23
23
  # Given a method, return a symbol in the format
24
24
  # <method_start>_unbound_<method_name>
25
25
  def build_unbound_method_name patcher_method
26
- "#{ Contrast::Utils::ObjectShare::CONTRAST_PATCHED_METHOD_START }unbound"\
27
- "#{ Contrast::Utils::ObjectShare::UNDERSCORE }"\
26
+ "#{ Contrast::Utils::ObjectShare::CONTRAST_PATCHED_METHOD_START }unbound" \
27
+ "#{ Contrast::Utils::ObjectShare::UNDERSCORE }" \
28
28
  "#{ patcher_method }".to_sym
29
29
  end
30
30
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  # require 'contrast/components/logger'
5
5
  # require 'contrast/agent/telemetry/events/exceptions/telemetry_exception_event'
6
+ require 'contrast/utils/duck_utils'
6
7
 
7
8
  module Contrast
8
9
  module Utils
@@ -25,6 +26,9 @@ module Contrast
25
26
  # @param route [Contrast::Agent::Reporting::ObservedRoute] the route
26
27
  # @return [boolean]
27
28
  def sendable? route
29
+ return false if Contrast::Utils::DuckUtils.empty_duck?(route.signature)
30
+ return false if Contrast::Utils::DuckUtils.empty_duck?(route.url)
31
+
28
32
  route_hash = route.hash_id
29
33
 
30
34
  # If hash doesn't exist in @cache...
@@ -81,7 +81,7 @@ module Contrast
81
81
  #
82
82
  # @return[Boolean] true if success, false if fails
83
83
  def touch_marker dir, file
84
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
84
+ FileUtils.mkdir_p(dir)
85
85
  FileUtils.touch(dir + file)
86
86
  File.file?(dir + file)
87
87
  rescue StandardError => _e
data/ruby-agent.gemspec CHANGED
@@ -75,7 +75,7 @@ def self.add_specs spec
75
75
  spec.add_development_dependency 'factory_bot'
76
76
  spec.add_development_dependency 'fake_ftp'
77
77
  spec.add_development_dependency 'openssl'
78
- spec.add_development_dependency 'parallel_tests'
78
+ spec.add_development_dependency 'parallel_tests', '~> 3.0'
79
79
  spec.add_development_dependency 'rspec', '~> 3.0'
80
80
  spec.add_development_dependency 'rspec-benchmark'
81
81
  spec.add_development_dependency 'rspec_junit_formatter', '0.3.0'
@@ -92,11 +92,11 @@ end
92
92
 
93
93
  # Dependencies used to run all of our Rubocop during the linting phase.
94
94
  def self.add_rubocop spec
95
- spec.add_development_dependency 'rubocop', '1.26.1'
96
- spec.add_development_dependency 'rubocop-performance', '1.13.3'
97
- spec.add_development_dependency 'rubocop-rails', '2.14.2'
95
+ spec.add_development_dependency 'rubocop', '1.37.1'
96
+ spec.add_development_dependency 'rubocop-performance', '1.15.0'
97
+ spec.add_development_dependency 'rubocop-rails', '2.17.2'
98
98
  spec.add_development_dependency 'rubocop-rake', '0.6.0'
99
- spec.add_development_dependency 'rubocop-rspec', '2.9.0'
99
+ spec.add_development_dependency 'rubocop-rspec', '2.14.2'
100
100
  end
101
101
 
102
102
  # Dependencies not mocked out during RSpec that we test real code of, beyond just frameworks.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrast-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.10.0
4
+ version: 6.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - galen.palmer@contrastsecurity.com
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: exe
15
15
  cert_chain: []
16
- date: 2022-11-02 00:00:00.000000000 Z
16
+ date: 2022-11-09 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: bundler
@@ -161,42 +161,42 @@ dependencies:
161
161
  requirements:
162
162
  - - '='
163
163
  - !ruby/object:Gem::Version
164
- version: 1.26.1
164
+ version: 1.37.1
165
165
  type: :development
166
166
  prerelease: false
167
167
  version_requirements: !ruby/object:Gem::Requirement
168
168
  requirements:
169
169
  - - '='
170
170
  - !ruby/object:Gem::Version
171
- version: 1.26.1
171
+ version: 1.37.1
172
172
  - !ruby/object:Gem::Dependency
173
173
  name: rubocop-performance
174
174
  requirement: !ruby/object:Gem::Requirement
175
175
  requirements:
176
176
  - - '='
177
177
  - !ruby/object:Gem::Version
178
- version: 1.13.3
178
+ version: 1.15.0
179
179
  type: :development
180
180
  prerelease: false
181
181
  version_requirements: !ruby/object:Gem::Requirement
182
182
  requirements:
183
183
  - - '='
184
184
  - !ruby/object:Gem::Version
185
- version: 1.13.3
185
+ version: 1.15.0
186
186
  - !ruby/object:Gem::Dependency
187
187
  name: rubocop-rails
188
188
  requirement: !ruby/object:Gem::Requirement
189
189
  requirements:
190
190
  - - '='
191
191
  - !ruby/object:Gem::Version
192
- version: 2.14.2
192
+ version: 2.17.2
193
193
  type: :development
194
194
  prerelease: false
195
195
  version_requirements: !ruby/object:Gem::Requirement
196
196
  requirements:
197
197
  - - '='
198
198
  - !ruby/object:Gem::Version
199
- version: 2.14.2
199
+ version: 2.17.2
200
200
  - !ruby/object:Gem::Dependency
201
201
  name: rubocop-rake
202
202
  requirement: !ruby/object:Gem::Requirement
@@ -217,14 +217,14 @@ dependencies:
217
217
  requirements:
218
218
  - - '='
219
219
  - !ruby/object:Gem::Version
220
- version: 2.9.0
220
+ version: 2.14.2
221
221
  type: :development
222
222
  prerelease: false
223
223
  version_requirements: !ruby/object:Gem::Requirement
224
224
  requirements:
225
225
  - - '='
226
226
  - !ruby/object:Gem::Version
227
- version: 2.9.0
227
+ version: 2.14.2
228
228
  - !ruby/object:Gem::Dependency
229
229
  name: simplecov
230
230
  requirement: !ruby/object:Gem::Requirement
@@ -473,16 +473,16 @@ dependencies:
473
473
  name: parallel_tests
474
474
  requirement: !ruby/object:Gem::Requirement
475
475
  requirements:
476
- - - ">="
476
+ - - "~>"
477
477
  - !ruby/object:Gem::Version
478
- version: '0'
478
+ version: '3.0'
479
479
  type: :development
480
480
  prerelease: false
481
481
  version_requirements: !ruby/object:Gem::Requirement
482
482
  requirements:
483
- - - ">="
483
+ - - "~>"
484
484
  - !ruby/object:Gem::Version
485
- version: '0'
485
+ version: '3.0'
486
486
  - !ruby/object:Gem::Dependency
487
487
  name: rspec
488
488
  requirement: !ruby/object:Gem::Requirement