contrast-agent 6.2.0 → 6.3.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +7 -5
  3. data/ext/cs__assess_kernel/cs__assess_kernel.c +14 -3
  4. data/ext/cs__assess_kernel/cs__assess_kernel.h +2 -0
  5. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +10 -3
  6. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +2 -1
  7. data/ext/cs__assess_regexp/cs__assess_regexp.c +9 -7
  8. data/ext/{cs__assess_string_interpolation26/cs__assess_string_interpolation26.c → cs__assess_string_interpolation/cs__assess_string_interpolation.c} +14 -3
  9. data/ext/{cs__assess_string_interpolation26/cs__assess_string_interpolation26.h → cs__assess_string_interpolation/cs__assess_string_interpolation.h} +1 -1
  10. data/ext/{cs__assess_string_interpolation26 → cs__assess_string_interpolation}/extconf.rb +0 -0
  11. data/ext/cs__common/cs__common.c +5 -4
  12. data/ext/cs__contrast_patch/cs__contrast_patch.c +3 -10
  13. data/lib/contrast/agent/assess/events/source_event.rb +16 -12
  14. data/lib/contrast/agent/assess/policy/policy_node.rb +6 -0
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -39
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +8 -0
  17. data/lib/contrast/agent/assess/policy/propagator/base.rb +2 -0
  18. data/lib/contrast/agent/assess/policy/source_method.rb +2 -47
  19. data/lib/contrast/agent/assess/policy/source_node.rb +1 -0
  20. data/lib/contrast/agent/assess/policy/trigger_node.rb +8 -0
  21. data/lib/contrast/agent/assess/property/evented.rb +4 -18
  22. data/lib/contrast/agent/assess/tag.rb +19 -0
  23. data/lib/contrast/agent/at_exit_hook.rb +8 -8
  24. data/lib/contrast/agent/inventory/database_config.rb +6 -3
  25. data/lib/contrast/agent/inventory/dependency_analysis.rb +3 -2
  26. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +10 -10
  27. data/lib/contrast/agent/middleware.rb +4 -0
  28. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +27 -2
  29. data/lib/contrast/agent/patching/policy/policy.rb +5 -0
  30. data/lib/contrast/agent/patching/policy/policy_node.rb +6 -0
  31. data/lib/contrast/agent/patching/policy/trigger_node.rb +3 -0
  32. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +3 -4
  33. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -0
  34. data/lib/contrast/agent/protect/policy/rule_applicator.rb +2 -2
  35. data/lib/contrast/agent/protect/rule/base.rb +1 -0
  36. data/lib/contrast/agent/protect/rule/no_sqli.rb +2 -0
  37. data/lib/contrast/agent/reporting/reporter.rb +32 -7
  38. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +21 -15
  39. data/lib/contrast/agent/reporting/reporting_events/application_update.rb +5 -24
  40. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +8 -1
  41. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +8 -1
  42. data/lib/contrast/agent/reporting/reporting_events/finding.rb +7 -1
  43. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +10 -1
  44. data/lib/contrast/agent/reporting/reporting_events/finding_event_object.rb +11 -1
  45. data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +11 -1
  46. data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +12 -1
  47. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +10 -1
  48. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +11 -1
  49. data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +11 -1
  50. data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +11 -1
  51. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +11 -1
  52. data/lib/contrast/agent/reporting/reporting_events/library_discovery.rb +29 -32
  53. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +13 -1
  54. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +11 -8
  55. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +12 -5
  56. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +8 -1
  57. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +9 -1
  58. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +10 -1
  59. data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +11 -4
  60. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +0 -8
  61. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -4
  62. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -22
  63. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -3
  64. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +1 -11
  65. data/lib/contrast/agent/request.rb +5 -7
  66. data/lib/contrast/agent/request_context.rb +8 -17
  67. data/lib/contrast/agent/request_context_extend.rb +8 -9
  68. data/lib/contrast/agent/request_handler.rb +9 -38
  69. data/lib/contrast/agent/rule_set.rb +4 -0
  70. data/lib/contrast/agent/service_heartbeat.rb +1 -1
  71. data/lib/contrast/agent/static_analysis.rb +6 -11
  72. data/lib/contrast/agent/telemetry/base.rb +35 -35
  73. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -0
  74. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +2 -0
  75. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message.rb +5 -2
  76. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +3 -0
  77. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +3 -0
  78. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions.rb +0 -1
  79. data/lib/contrast/agent/thread_watcher.rb +1 -4
  80. data/lib/contrast/agent/version.rb +1 -1
  81. data/lib/contrast/api/communication/socket.rb +1 -0
  82. data/lib/contrast/api/decorators/message.rb +0 -6
  83. data/lib/contrast/api/decorators.rb +0 -2
  84. data/lib/contrast/components/assess.rb +0 -6
  85. data/lib/contrast/components/config.rb +18 -2
  86. data/lib/contrast/config/base_configuration.rb +0 -13
  87. data/lib/contrast/config/root_configuration.rb +1 -0
  88. data/lib/contrast/config/ruby_configuration.rb +2 -9
  89. data/lib/contrast/configuration.rb +0 -2
  90. data/lib/contrast/extension/assess/eval_trigger.rb +0 -4
  91. data/lib/contrast/extension/assess/hash.rb +3 -2
  92. data/lib/contrast/extension/assess/kernel.rb +22 -0
  93. data/lib/contrast/extension/assess/marshal.rb +16 -0
  94. data/lib/contrast/extension/assess/string.rb +21 -20
  95. data/lib/contrast/framework/base_support.rb +8 -0
  96. data/lib/contrast/framework/manager.rb +6 -20
  97. data/lib/contrast/framework/manager_extend.rb +0 -1
  98. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +11 -16
  99. data/lib/contrast/logger/aliased_logging.rb +2 -0
  100. data/lib/contrast/utils/assess/source_method_utils.rb +0 -9
  101. data/lib/contrast/utils/lru_cache.rb +3 -0
  102. data/lib/contrast/utils/middleware_utils.rb +2 -0
  103. data/lib/contrast/utils/telemetry_client.rb +7 -7
  104. data/resources/assess/policy.json +2 -11
  105. data/ruby-agent.gemspec +1 -1
  106. metadata +22 -20
  107. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +0 -30
  108. data/lib/contrast/api/decorators/application_update.rb +0 -44
  109. data/lib/contrast/api/decorators/library.rb +0 -56
  110. data/lib/contrast/framework/platform_version.rb +0 -22
@@ -33,7 +33,7 @@ module Contrast
33
33
  @messaging_queue = Contrast::Api::Communication::MessagingQueue.new
34
34
  end
35
35
  @reporter = Contrast::Agent::Reporter.new
36
- @reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new if Contrast::Agent::Reporter.enabled?
36
+ @reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
37
37
  @telemetry = Contrast::Agent::Telemetry::Base.new if Contrast::Agent::Telemetry::Base.enabled?
38
38
  end
39
39
 
@@ -50,9 +50,6 @@ module Contrast
50
50
  @pids[Process.pid] = @pids[Process.pid] && telemetry_status
51
51
  end
52
52
  reporter_status = init_thread(reporter)
53
-
54
- return @pids unless Contrast::Agent::Reporter.enabled?
55
-
56
53
  reporter_heartbeat_status = init_thread(reporter_heartbeat)
57
54
  @pids[Process.pid] = @pids[Process.pid] && reporter_status && reporter_heartbeat_status
58
55
  @pids
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.2.0'
6
+ VERSION = '6.3.0'
7
7
  end
8
8
  end
@@ -34,6 +34,7 @@ module Contrast
34
34
  end
35
35
 
36
36
  # Override this method to return a socket. Should be interface compatible with TCPSocket, UNIXSocket, etc.
37
+ # @raise[NoMethodError] abstract method, needs to be implemented
37
38
  def new_socket
38
39
  raise(NoMethodError, 'This is abstract, override it.')
39
40
  end
@@ -19,14 +19,10 @@ module Contrast
19
19
 
20
20
  def append_event event
21
21
  case event
22
- when Contrast::Api::Dtm::ServerActivity
23
- self.server_activity = event
24
22
  when Contrast::Api::Dtm::AgentStartup
25
23
  self.agent_startup = event
26
24
  when Contrast::Api::Dtm::ApplicationCreate
27
25
  self.application_create = event
28
- when Contrast::Api::Dtm::ApplicationUpdate
29
- self.application_update = event
30
26
  when Contrast::Api::Dtm::Activity
31
27
  self.activity = event
32
28
  when Contrast::Api::Dtm::HttpRequest
@@ -35,8 +31,6 @@ module Contrast
35
31
  self.postfilter = event
36
32
  when Contrast::Api::Dtm::Poll
37
33
  self.poll = event
38
- when Contrast::Api::Dtm::ObservedRoute
39
- self.observed_route = event
40
34
  else
41
35
  logger.error('Unknown event type received. Unsure how to send.', event_type: event.cs__class.cs__name)
42
36
  return
@@ -12,12 +12,10 @@ end
12
12
  require 'contrast/api/decorators/message'
13
13
  require 'contrast/api/decorators/agent_startup'
14
14
  require 'contrast/api/decorators/application_startup'
15
- require 'contrast/api/decorators/application_update'
16
15
  require 'contrast/api/decorators/architecture_component'
17
16
  require 'contrast/api/decorators/input_analysis'
18
17
  require 'contrast/api/decorators/application_settings'
19
18
  require 'contrast/api/decorators/server_features'
20
- require 'contrast/api/decorators/library'
21
19
  require 'contrast/api/decorators/route_coverage'
22
20
  require 'contrast/api/decorators/trace_event_object'
23
21
  require 'contrast/api/decorators/trace_event_signature'
@@ -76,12 +76,6 @@ module Contrast
76
76
  @_scan_response
77
77
  end
78
78
 
79
- def track_frozen_sources?
80
- @_track_frozen_sources = !false?(::Contrast::CONFIG.root.agent.ruby.track_frozen_sources) if
81
- @_track_frozen_sources.nil?
82
- @_track_frozen_sources
83
- end
84
-
85
79
  def require_scan?
86
80
  @_require_scan = !false?(::Contrast::CONFIG.root.agent.ruby.require_scan) if @_require_scan.nil?
87
81
  @_require_scan
@@ -27,7 +27,7 @@ module Contrast
27
27
  CONTRAST_LOG = 'contrast_agent.log'
28
28
  CONTRAST_NAME = 'Contrast Agent'
29
29
 
30
- class Interface # :nodoc:
30
+ class Interface # :nodoc: # rubocop:disable Metrics/ClassLength
31
31
  SESSION_VARIABLES = 'Invalid configuration. '\
32
32
  "Setting both application.session_id and application.session_metadata is not allowed.\n"
33
33
  API_URL = "Invalid configuration. Missing a required connection value 'url' is not set."
@@ -57,6 +57,8 @@ module Contrast
57
57
  @config = Contrast::Configuration.new
58
58
  env_overrides
59
59
  validate
60
+ rescue ArgumentError => e
61
+ proto_logger.error('Configuration failed with error: ', e)
60
62
  end
61
63
  alias_method :rebuild, :build
62
64
 
@@ -136,7 +138,7 @@ module Contrast
136
138
  next unless env_key.to_s.start_with?(CONTRAST_ENV_MARKER)
137
139
 
138
140
  config_item = Contrast::Utils::EnvConfigurationItem.new(env_key, env_value)
139
- @config.assign_value_to_path_array(config_item.dot_path_array, config_item.value)
141
+ assign_value_to_path_array(root, config_item.dot_path_array, config_item.value)
140
142
  end
141
143
  end
142
144
 
@@ -193,6 +195,20 @@ module Contrast
193
195
  def logger_path
194
196
  root.agent.logger.path
195
197
  end
198
+
199
+ # Assign the value from an ENV variable to the Contrast::Config::RootConfiguration object, when
200
+ # appropriate.
201
+ #
202
+ # @return nil
203
+ def assign_value_to_path_array current_level, dot_path_array, value
204
+ dot_path_array[0...-1].each do |segment|
205
+ segment = segment.tr('-', '_')
206
+ current_level = current_level.send(segment) if current_level.cs__respond_to?(segment)
207
+ end
208
+ return unless current_level.nil? == false && current_level.cs__respond_to?(dot_path_array[-1])
209
+
210
+ current_level.send("#{ dot_path_array[-1] }=", value)
211
+ end
196
212
  end
197
213
  end
198
214
  end
@@ -20,19 +20,6 @@ module Contrast
20
20
  end
21
21
  hsh
22
22
  end
23
-
24
- def assign_value_to_path_array dot_path_array, value
25
- current_level = self
26
- dot_path_array[0...-1].each do |segment|
27
- segment = segment.tr('-', '_')
28
- current_level = current_level.send(segment) if current_level.cs__respond_to?(segment)
29
- end
30
- last_entry = dot_path_array[-1]
31
- if current_level.nil? == false && current_level.cs__respond_to?(last_entry)
32
- current_level.send("#{ last_entry }=", value)
33
- end
34
- nil
35
- end
36
23
  end
37
24
  end
38
25
  end
@@ -26,6 +26,7 @@ module Contrast
26
26
  # @return [Boolean, nil]
27
27
  attr_accessor :enable
28
28
 
29
+ # @raise[ArgumentError]
29
30
  def initialize hsh = {}
30
31
  raise(ArgumentError, 'Expected a hash') unless hsh.is_a?(Hash)
31
32
 
@@ -21,7 +21,7 @@ module Contrast
21
21
  DEFAULT_UNINSTRUMENTED_NAMESPACES = %w[FactoryGirl FactoryBot].cs__freeze
22
22
 
23
23
  attr_writer :disabled_agent_rake_tasks, :exceptions, :interpolate, :propagate_yield, :require_scan,
24
- :track_frozen_sources, :non_request_tracking, :uninstrument_namespace
24
+ :non_request_tracking, :uninstrument_namespace
25
25
 
26
26
  def initialize hsh = {}
27
27
  return unless hsh
@@ -31,7 +31,6 @@ module Contrast
31
31
  @interpolate = hsh[:interpolate]
32
32
  @propagate_yield = hsh[:propagate_yield]
33
33
  @require_scan = hsh[:require_scan]
34
- @track_frozen_sources = hsh[:track_frozen_sources]
35
34
  @non_request_tracking = hsh[:non_request_tracking]
36
35
  @uninstrument_namespace = hsh[:uninstrument_namespace]
37
36
  end
@@ -67,16 +66,10 @@ module Contrast
67
66
  @require_scan.nil? ? Contrast::Utils::ObjectShare::TRUE : @require_scan
68
67
  end
69
68
 
70
- # controls whether or not we track frozen Strings by replacing them
71
- # @return [Boolean, Contrast::Utils::ObjectShare::TRUE]
72
- def track_frozen_sources
73
- @track_frozen_sources.nil? ? Contrast::Utils::ObjectShare::TRUE : @track_frozen_sources
74
- end
75
-
76
69
  # controls tracking outside of request
77
70
  # @return [Boolean, Contrast::Utils::ObjectShare::FALSE]
78
71
  def non_request_tracking
79
- @non_request_tracking.nil? ? Contrast::Utils::ObjectShare::FALSE : @non_request_tracking
72
+ @non_request_tracking.nil? ? Contrast::Utils::ObjectShare::FALSE : @non_request_tracking
80
73
  end
81
74
 
82
75
  # @return [Array, DEFAULT_UNINSTRUMENTED_NAMESPACES]
@@ -18,8 +18,6 @@ module Contrast
18
18
  include Contrast::Components::Scope::InstanceMethods
19
19
  extend Contrast::Components::Scope::InstanceMethods
20
20
 
21
- def_delegator :root, :assign_value_to_path_array
22
-
23
21
  attr_reader :default_name, :root
24
22
 
25
23
  DEFAULT_YAML_PATH = 'contrast_security.yaml'
@@ -25,10 +25,6 @@ module Contrast
25
25
  def apply_trigger obj, source, ret, clazz, method
26
26
  return unless ::Contrast::ASSESS.non_request_tracking? || Contrast::Agent::REQUEST_TRACKER.current
27
27
 
28
- # Since we know this is the source of the trigger, we can do some
29
- # optimization here and return when it is not tracked
30
- return unless Contrast::Utils::Assess::TrackingUtil.tracked?(source)
31
-
32
28
  # source might not be all the args passed in, but it is the one we care
33
29
  # about. we could pass in all the args in the last param here if it
34
30
  # becomes an issue in rendering on TS
@@ -15,11 +15,12 @@ module Contrast
15
15
  class << self
16
16
  def cs__duplicate_and_freeze object
17
17
  return object unless object.is_a?(String) && !object.cs__frozen?
18
- return object unless Contrast::Agent::Assess::Tracker.tracked?(object)
19
18
 
20
19
  # Copy the object, then freeze it, so that it looks the same
21
20
  # externally, but will have our finalizer on it.
22
- object.dup&.cs__freeze
21
+ copy = object.dup
22
+ Contrast::Agent::Assess::Tracker.pre_freeze(copy)
23
+ copy&.cs__freeze
23
24
  rescue StandardError
24
25
  # we'll rescue this error, but we can't log it here as that will
25
26
  # result in a seg fault
@@ -4,6 +4,7 @@
4
4
  require 'contrast/extension/assess/exec_trigger'
5
5
  require 'contrast/components/logger'
6
6
  require 'contrast/agent/assess/events/event_data'
7
+ require 'contrast/agent/patching/policy/patch'
7
8
 
8
9
  module Contrast
9
10
  module Extension
@@ -97,6 +98,27 @@ module Contrast
97
98
  end
98
99
  end
99
100
  end
101
+
102
+ # Used for Kernel exec aliasing since we have no other way of accessing the
103
+ # Kernel#exec under C if we alias it there.
104
+ module ContrastKernel
105
+ # Method to replace the call to Kernel#exec when applying alias patch.
106
+ # It will invoke Contrast::Extension::Assess::KernelPropagator before
107
+ # calling the original method.
108
+ #
109
+ # @param source [String, Proc] Potentially untrusted shell command to execute.
110
+ # @return nil - This method will invoke the Kernel#exec which will
111
+ def cs__kernel_exec source
112
+ # Check if in contrast scope and we have source.
113
+ unless Contrast::Agent::Patching::Policy::Patch.in_contrast_scope? || source.nil?
114
+ Contrast::Agent::Patching::Policy::Patch.with_contrast_scope do
115
+ Contrast::Extension::Assess::KernelPropagator.apply_trigger(source)
116
+ end
117
+ end
118
+ # Call this in the end else any code bellow this call won't be executed.
119
+ Kernel.exec(source)
120
+ end
121
+ end
100
122
  end
101
123
  end
102
124
  end
@@ -58,6 +58,22 @@ module Contrast
58
58
  end
59
59
  end
60
60
  end
61
+
62
+ # Used for aliasing
63
+ module ContrastMarshal
64
+ def cs__marshal_load source
65
+ # Do the protect
66
+ Contrast::Extension::Assess::MarshalPropagator.cs__load_protect(source) if source
67
+ # call the original
68
+ result = Marshal.load(source) # rubocop:disable Security/MarshalLoad
69
+ # Do the assess
70
+ tracked = Contrast::Agent::Assess::Tracker::PROPERTIES_HASH.tracked?(source) if source
71
+ skip = Contrast::Agent::Patching::Policy::Patch.skip_assess_analysis? if tracked
72
+ Contrast::Extension::Assess::MarshalPropagator.cs__load_assess(source, result) if skip
73
+ # return original
74
+ result
75
+ end
76
+ end
61
77
  end
62
78
  end
63
79
  end
@@ -31,31 +31,32 @@ module Contrast
31
31
  INTERPOLATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
32
32
 
33
33
  class << self
34
+ # We call this method from C, and the Scope check is happening there. If we are in
35
+ # Contrast Scope the method won't be invoked.
36
+ #
37
+ # @param inputs [Array<String>] Inputs for interpolation.
38
+ # @param result [String] The result from the interpolation.
34
39
  def track_interpolation inputs, result
35
40
  return unless ::Contrast::AGENT.interpolation_enabled?
36
- return if in_contrast_scope?
37
41
  return unless inputs.any? { |input| Contrast::Agent::Assess::Tracker.tracked?(input) }
42
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties!(result))
38
43
 
39
- with_contrast_scope do
40
- return unless (properties = Contrast::Agent::Assess::Tracker.properties!(result))
41
-
42
- parent_events = []
43
- offset = 0
44
- inputs.each do |input|
45
- properties.copy_from(input, result, offset)
46
- add_dynamic_sources_info(input, result)
47
- offset += input.length
48
- parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event
49
- parent_events << parent_event if parent_event
50
- end
51
- event_data = Contrast::Agent::Assess::Events::EventData.new(INTERPOLATION_NODE,
52
- result,
53
- inputs,
54
- result,
55
- inputs)
56
- properties.build_event(event_data)
57
- properties.event.instance_variable_set(:@_parent_events, parent_events)
44
+ parent_events = []
45
+ offset = 0
46
+ inputs.each do |input|
47
+ properties.copy_from(input, result, offset)
48
+ add_dynamic_sources_info(input, result)
49
+ offset += input.length
50
+ parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event
51
+ parent_events << parent_event if parent_event
58
52
  end
53
+ event_data = Contrast::Agent::Assess::Events::EventData.new(INTERPOLATION_NODE,
54
+ result,
55
+ inputs,
56
+ result,
57
+ inputs)
58
+ properties.build_event(event_data)
59
+ properties.event.instance_variable_set(:@_parent_events, parent_events)
59
60
  rescue StandardError => e
60
61
  logger.error('Unable to track interpolation', e)
61
62
  end
@@ -8,18 +8,22 @@ module Contrast
8
8
  # The API for all subclasses to implement to correctly support a given framework
9
9
  module BaseSupport
10
10
  # The top level module name used by the framework
11
+ # @raise [NoMethodError] raises error if subclass does not implement this method
11
12
  def detection_class
12
13
  raise(NoMethodError('Subclasses of BaseSupport should implement this method'))
13
14
  end
14
15
 
16
+ # @raise [NoMethodError] raises error if subclass does not implement this method
15
17
  def version
16
18
  raise(NoMethodError('Subclasses of BaseSupport should implement this method'))
17
19
  end
18
20
 
21
+ # @raise [NoMethodError] raises error if subclass does not implement this method
19
22
  def application_name
20
23
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
21
24
  end
22
25
 
26
+ # @raise [NoMethodError] raises error if subclass does not implement this method
23
27
  def server_type
24
28
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
25
29
  end
@@ -27,18 +31,22 @@ module Contrast
27
31
  # Find all the predefined routes for this application
28
32
  #
29
33
  # @return [Array<Contrast::Agent::Reporting::DiscoveredRoute>]
34
+ # @raise [NoMethodError] raises error if subclass does not implement this method
30
35
  def collect_routes
31
36
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
32
37
  end
33
38
 
39
+ # @raise [NoMethodError] raises error if subclass does not implement this method
34
40
  def current_route_coverage
35
41
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
36
42
  end
37
43
 
44
+ # @raise [NoMethodError] raises error if subclass does not implement this method
38
45
  def current_route
39
46
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
40
47
  end
41
48
 
49
+ # @raise [NoMethodError] raises error if subclass does not implement this method
42
50
  def retrieve_request _env
43
51
  raise(NoMethodError, 'Subclasses of BaseSupport should implement this method')
44
52
  end
@@ -7,7 +7,6 @@ require 'contrast/components/logger'
7
7
  require 'contrast/extension/module'
8
8
  require 'contrast/framework/grape/support'
9
9
  require 'contrast/framework/manager_extend'
10
- require 'contrast/framework/platform_version'
11
10
  require 'contrast/framework/rack/support'
12
11
  require 'contrast/framework/rails/support'
13
12
  require 'contrast/framework/sinatra/support'
@@ -64,16 +63,6 @@ module Contrast
64
63
  routes_for_all_frameworks
65
64
  end
66
65
 
67
- def platform_version
68
- framework_version = first_framework_result(:version, '')
69
-
70
- Contrast::Framework::PlatformVersion.from_string(framework_version)
71
- end
72
-
73
- def platform_version_string
74
- first_framework_result(:version, '')
75
- end
76
-
77
66
  def server_type
78
67
  first_framework_result(:server_type, 'rack')
79
68
  end
@@ -157,16 +146,13 @@ module Contrast
157
146
  next unless module_name == framework.detection_class
158
147
 
159
148
  @_frameworks << framework
160
- # Report the registered routes of that framework now that we know we need to find them
161
- # TODO: RUBY-1438 -- remove and build ReportingEvent directly
162
- app_update_msg = Contrast::Api::Dtm::ApplicationUpdate.build
163
- if Contrast::Agent.reporter
164
- report = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(app_update_msg)
165
- Contrast::Agent.reporter.send_event(report)
166
- else
167
- Contrast::Agent.messaging_queue.send_event_eventually(app_update_msg)
149
+ report = Contrast::Agent::Reporting::ApplicationUpdate.new
150
+ # This convert here is left as it'll be easier to be replaced when the Library is being changed
151
+ report.libraries = Contrast::Agent::Inventory::DependencyAnalysis.instance.library_pb_list
152
+ [report, Contrast::Agent::Reporting::ApplicationInventory.new].each do |e|
153
+ Contrast::Agent.reporter.send_event(e)
168
154
  end
169
- Contrast::Agent.reporter.send_event(Contrast::Agent::Reporting::ApplicationInventory.new)
155
+
170
156
  logger.info('Framework detected after initialization. Enabling support.',
171
157
  framework: framework.detection_class,
172
158
  frameworks: @_frameworks)
@@ -3,7 +3,6 @@
3
3
 
4
4
  require 'contrast/components/logger'
5
5
  require 'contrast/extension/module'
6
- require 'contrast/framework/platform_version'
7
6
  require 'contrast/framework/rack/support'
8
7
  require 'contrast/framework/rails/support'
9
8
  require 'contrast/framework/grape/support'
@@ -1,6 +1,8 @@
1
1
  # Copyright (c) 2022 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/inventory/dependency_usage_analysis'
5
+
4
6
  module Contrast
5
7
  module Framework
6
8
  module Rails
@@ -9,27 +11,20 @@ module Contrast
9
11
  # event on streamed responses.
10
12
  module ActionControllerLiveBuffer
11
13
  class << self
12
- # TODO: RUBY-1353
13
- # TODO: RUBY-1355
14
- # TODO: RUBY-1357
15
- # TODO: RUBY-1357
16
14
  def send_messages
17
15
  return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
18
16
 
17
+ [
18
+ context.observed_route
19
+ ].each do |event|
20
+ Contrast::Agent.reporter&.send_event_immediately(event)
21
+ end
22
+
19
23
  if Contrast::Agent::Reporter.enabled?
20
- [
21
- context.new_observed_route,
22
- context.observed_library_usage,
23
- Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
24
- Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
25
- ].each do |event|
26
- Contrast::Agent.reporter&.send_event_immediately(event)
27
- end
24
+ event = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
25
+ Contrast::Agent.reporter&.send_event_immediately(event)
28
26
  else
29
- Contrast::Agent.reporter.send_event_immediately(context.observed_library_usage)
30
- [context.server_activity, context.activity, context.observed_route].each do |msg|
31
- Contrast::Agent.messaging_queue&.send_event_immediately(msg)
32
- end
27
+ Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
33
28
  end
34
29
  end
35
30
 
@@ -88,6 +88,8 @@ module Contrast
88
88
  end
89
89
  message = Contrast::Agent::Telemetry::TelemetryException::Message.build(tags, [message_exception])
90
90
  Contrast::Agent::Telemetry::TelemetryException::Event.new(message)
91
+ rescue ArgumentError => e
92
+ debug('TelemetryException failed from aliased logging with: ', e)
91
93
  end
92
94
 
93
95
  def get_stack_trace type
@@ -7,15 +7,6 @@ module Contrast
7
7
  # This module will include all methods for some internal validations in the SourceMethod module
8
8
  # and some other module methods from the same place, so we can ease the main module
9
9
  module SourceMethodUtils
10
- # Safely duplicate the target, or return nil
11
- #
12
- # @param target [Object] the thing to check for duplication
13
- def safe_dup target
14
- target.dup
15
- rescue StandardError => _e
16
- nil
17
- end
18
-
19
10
  # Find the name of the source
20
11
  #
21
12
  # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
@@ -5,6 +5,9 @@ module Contrast
5
5
  module Utils
6
6
  # A LRU(Least Recently Used) Cache store.
7
7
  class LRUCache
8
+ # Initializes new Least Recently Used Cache Store
9
+ #
10
+ # @raise [StandardError] raises error if provided capacity is invalid or less or equal to zero
8
11
  def initialize capacity = 500
9
12
  raise(StandardError('Capacity must be bigger than 0')) if capacity <= 0
10
13
 
@@ -34,6 +34,7 @@ module Contrast
34
34
  SECURITY_EXCEPTION_MARKER = 'Contrast::SecurityException'
35
35
  # We're only going to suppress SecurityExceptions indicating a blocked attack. And, only if the
36
36
  # config.agent.ruby.exceptions.capture? is set
37
+ # @raise [Contrast::SecurityException] if exceptions.capture? is allowed
37
38
  def handle_exception exception
38
39
  if security_exception?(exception)
39
40
  exception_control = ::Contrast::AGENT.exception_control
@@ -74,6 +75,7 @@ module Contrast
74
75
  true
75
76
  end
76
77
 
78
+ # @raise [Contrast::SecurityException] raises error to prevent an attack
77
79
  def application_code env
78
80
  logger.trace_with_time('application') do
79
81
  app.call(env)
@@ -34,8 +34,8 @@ module Contrast
34
34
  def build_request event
35
35
  return unless valid_event?(event)
36
36
 
37
- string_body = if Array(event).all?(Contrast::Agent::Telemetry::TelemetryException::Event)
38
- event.map(&:to_controlled_hash).flatten!
37
+ string_body = if event.cs__is_a?(Contrast::Agent::Telemetry::TelemetryException::Event)
38
+ [event.to_controlled_hash]
39
39
  else
40
40
  [event.to_hash]
41
41
  end
@@ -73,14 +73,14 @@ module Contrast
73
73
  ready_after if status_code == 429
74
74
  end
75
75
 
76
- # This method will be responsible for validating the event
77
- # @param event[Contrast::Agent::Telemetry::Event,Contrast::Agent::Telemetry::StartupMetricsEvent,
78
- # array<Contrast::Agent::Telemetry::TelemetryException::Event>]
76
+ # This method will be responsible for validating the event. Valid if event is of a known
77
+ # Contrast::Agent::Telemetry type
78
+ #
79
+ # @param event [Object]
79
80
  def valid_event? event
80
81
  return true if event.cs__is_a?(Contrast::Agent::Telemetry::Event)
81
82
  return true if event.cs__is_a?(Contrast::Agent::Telemetry::StartupMetricsEvent)
82
- # Batch
83
- return true if Array(event).all?(Contrast::Agent::Telemetry::TelemetryException::Event)
83
+ return true if event.cs__is_a?(Contrast::Agent::Telemetry::TelemetryException::Event)
84
84
 
85
85
  false
86
86
  end
@@ -1092,17 +1092,8 @@
1092
1092
  "patch_method": "sprintf_tagger",
1093
1093
  "source": "O,P1",
1094
1094
  "target": "R"
1095
- }, {
1096
- "class_name":"ActiveRecord::ConnectionAdapters::Quoting",
1097
- "instance_method": true,
1098
- "method_visibility": "public",
1099
- "method_name":"quote",
1100
- "source": "P0",
1101
- "target": "R",
1102
- "action": "SPLAT",
1103
- "tags":["SQL_ENCODED"],
1104
- "untags":["SQL_DECODED"]
1105
- }, {
1095
+ },
1096
+ {
1106
1097
  "class_name":"ActiveRecord::ConnectionAdapters::Quoting",
1107
1098
  "instance_method": true,
1108
1099
  "method_visibility": "public",
data/ruby-agent.gemspec CHANGED
@@ -113,7 +113,7 @@ end
113
113
  # dependencies.csv in this directory to indicate that and create a
114
114
  # corresponding update to the fake gem server data in TeamServer.
115
115
  def self.add_dependencies spec
116
- spec.add_dependency 'ougai', '~> 1.8'
116
+ spec.add_dependency 'ougai', '>= 1.8', '< 3.0.0'
117
117
  spec.add_dependency 'protobuf', '~> 3.10'
118
118
  spec.add_dependency 'rack', '~> 2.0'
119
119
  end