contrast-agent 6.10.0 → 6.11.0

Sign up to get free protection for your applications and to get access to all the features.
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