datadog 2.29.0 → 2.31.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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +21 -12
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +9 -7
  5. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -24
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +5 -6
  9. data/ext/datadog_profiling_native_extension/http_transport.c +51 -64
  10. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  11. data/ext/datadog_profiling_native_extension/profiling.c +3 -1
  12. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +24 -8
  13. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
  14. data/ext/datadog_profiling_native_extension/stack_recorder.c +29 -43
  15. data/ext/libdatadog_api/crashtracker.c +5 -8
  16. data/ext/libdatadog_api/crashtracker_report_exception.c +34 -144
  17. data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
  18. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  19. data/ext/libdatadog_api/di.c +79 -0
  20. data/ext/libdatadog_api/extconf.rb +5 -20
  21. data/ext/libdatadog_api/init.c +5 -2
  22. data/ext/libdatadog_extconf_helpers.rb +57 -11
  23. data/lib/datadog/ai_guard/component.rb +2 -0
  24. data/lib/datadog/ai_guard/configuration/settings.rb +3 -0
  25. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  26. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  27. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  28. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  29. data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
  30. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  31. data/lib/datadog/ai_guard/evaluation.rb +36 -7
  32. data/lib/datadog/ai_guard.rb +26 -8
  33. data/lib/datadog/appsec/autoload.rb +1 -1
  34. data/lib/datadog/appsec/component.rb +11 -7
  35. data/lib/datadog/appsec/contrib/active_record/patcher.rb +3 -0
  36. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/excon/patcher.rb +2 -0
  38. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +1 -1
  39. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  40. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  41. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  43. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +25 -2
  44. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  45. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +2 -2
  46. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  48. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +2 -0
  49. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
  50. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +3 -3
  51. data/lib/datadog/appsec/event.rb +1 -17
  52. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
  53. data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
  54. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -2
  55. data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
  56. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  57. data/lib/datadog/appsec.rb +5 -9
  58. data/lib/datadog/core/configuration/base.rb +17 -5
  59. data/lib/datadog/core/configuration/components.rb +21 -8
  60. data/lib/datadog/core/configuration/config_helper.rb +9 -0
  61. data/lib/datadog/core/configuration/option.rb +32 -6
  62. data/lib/datadog/core/configuration/option_definition.rb +38 -12
  63. data/lib/datadog/core/configuration/options.rb +41 -7
  64. data/lib/datadog/core/configuration/settings.rb +42 -3
  65. data/lib/datadog/core/configuration/supported_configurations.rb +17 -0
  66. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  67. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  68. data/lib/datadog/core/crashtracking/component.rb +7 -15
  69. data/lib/datadog/core/environment/container.rb +2 -2
  70. data/lib/datadog/core/environment/ext.rb +1 -0
  71. data/lib/datadog/core/environment/identity.rb +25 -3
  72. data/lib/datadog/core/environment/process.rb +12 -0
  73. data/lib/datadog/core/metrics/client.rb +5 -5
  74. data/lib/datadog/core/process_discovery.rb +5 -0
  75. data/lib/datadog/core/remote/component.rb +38 -21
  76. data/lib/datadog/core/runtime/metrics.rb +2 -3
  77. data/lib/datadog/core/telemetry/component.rb +3 -0
  78. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  79. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  80. data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
  81. data/lib/datadog/core/telemetry/event.rb +1 -7
  82. data/lib/datadog/core/telemetry/ext.rb +1 -0
  83. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
  84. data/lib/datadog/core/telemetry/worker.rb +20 -0
  85. data/lib/datadog/core/utils/base64.rb +1 -1
  86. data/lib/datadog/core/utils/only_once.rb +1 -1
  87. data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
  88. data/lib/datadog/core/workers/async.rb +1 -1
  89. data/lib/datadog/core/workers/interval_loop.rb +13 -6
  90. data/lib/datadog/core/workers/queue.rb +0 -4
  91. data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
  92. data/lib/datadog/core.rb +0 -1
  93. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  94. data/lib/datadog/data_streams/processor.rb +1 -0
  95. data/lib/datadog/di/boot.rb +3 -4
  96. data/lib/datadog/di/component.rb +20 -4
  97. data/lib/datadog/di/instrumenter.rb +20 -10
  98. data/lib/datadog/di/probe_manager.rb +79 -62
  99. data/lib/datadog/di/probe_notification_builder.rb +148 -33
  100. data/lib/datadog/di/probe_notifier_worker.rb +52 -6
  101. data/lib/datadog/di/probe_repository.rb +198 -0
  102. data/lib/datadog/di/remote.rb +5 -6
  103. data/lib/datadog/di/serializer.rb +127 -9
  104. data/lib/datadog/di/transport/http.rb +12 -3
  105. data/lib/datadog/di/transport/input.rb +46 -8
  106. data/lib/datadog/di.rb +81 -0
  107. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  108. data/lib/datadog/open_feature/configuration.rb +2 -0
  109. data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
  110. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  111. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  112. data/lib/datadog/open_feature/remote.rb +1 -1
  113. data/lib/datadog/open_feature/transport.rb +1 -1
  114. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
  115. data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
  116. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -1
  117. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  118. data/lib/datadog/profiling/component.rb +31 -1
  119. data/lib/datadog/profiling/http_transport.rb +5 -6
  120. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  121. data/lib/datadog/profiling/profiler.rb +15 -12
  122. data/lib/datadog/profiling/scheduler.rb +2 -2
  123. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  124. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  125. data/lib/datadog/profiling.rb +1 -2
  126. data/lib/datadog/single_step_instrument.rb +1 -1
  127. data/lib/datadog/tracing/buffer.rb +3 -3
  128. data/lib/datadog/tracing/component.rb +11 -0
  129. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  130. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
  132. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  133. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  134. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  135. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  136. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  137. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  138. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  139. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  140. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  141. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  142. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  143. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  144. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  145. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  146. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  147. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  148. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  149. data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
  150. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  151. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
  152. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  153. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -2
  154. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  155. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  156. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -2
  157. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  158. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  159. data/lib/datadog/tracing/contrib/grape/endpoint.rb +7 -7
  160. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
  161. data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
  162. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
  163. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  164. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -2
  165. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  166. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
  167. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -2
  168. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  169. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -2
  170. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  171. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
  172. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
  173. data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
  174. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  175. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
  176. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  177. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +5 -1
  178. data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
  179. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
  180. data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
  181. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  182. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  183. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  184. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  185. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  186. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
  187. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  188. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
  189. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  190. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
  191. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  192. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  193. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  194. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +5 -1
  195. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +1 -0
  196. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  197. data/lib/datadog/tracing/event.rb +1 -1
  198. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  199. data/lib/datadog/tracing/remote.rb +1 -1
  200. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  201. data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
  202. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  203. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  204. data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
  205. data/lib/datadog/tracing/span_operation.rb +1 -1
  206. data/lib/datadog/tracing/sync_writer.rb +0 -1
  207. data/lib/datadog/tracing/trace_operation.rb +50 -6
  208. data/lib/datadog/tracing/tracer.rb +25 -0
  209. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  210. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  211. data/lib/datadog/tracing/writer.rb +0 -1
  212. data/lib/datadog/version.rb +1 -1
  213. metadata +15 -8
  214. data/lib/datadog/tracing/workers/trace_writer.rb +0 -204
@@ -24,7 +24,7 @@ module Datadog
24
24
  end
25
25
 
26
26
  def watch_request(gateway = Instrumentation.gateway)
27
- gateway.watch('rack.request', :appsec) do |stack, gateway_request|
27
+ gateway.watch('rack.request') do |stack, gateway_request|
28
28
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
29
29
 
30
30
  persistent_data = {
@@ -57,7 +57,7 @@ module Datadog
57
57
  end
58
58
 
59
59
  def watch_response(gateway = Instrumentation.gateway)
60
- gateway.watch('rack.response', :appsec) do |stack, gateway_response|
60
+ gateway.watch('rack.response') do |stack, gateway_response|
61
61
  context = gateway_response.context
62
62
 
63
63
  persistent_data = {
@@ -84,7 +84,7 @@ module Datadog
84
84
  end
85
85
 
86
86
  def watch_request_body(gateway = Instrumentation.gateway)
87
- gateway.watch('rack.request.body', :appsec) do |stack, gateway_request|
87
+ gateway.watch('rack.request.body') do |stack, gateway_request|
88
88
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
89
89
 
90
90
  persistent_data = {
@@ -113,20 +113,19 @@ module Datadog
113
113
  # somewhere closer to identity related monitor.
114
114
  # WARNING: The Gateway is a subject of refactoring
115
115
  def watch_request_finish(gateway = Instrumentation.gateway)
116
- gateway.watch('rack.request.finish', :appsec) do |stack, gateway_request|
116
+ gateway.watch('rack.request.finish') do |stack, gateway_request|
117
117
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
118
118
 
119
- if context.span.nil? || !gateway.pushed?('appsec.events.user_lifecycle')
120
- next stack.call(gateway_request.request)
121
- end
119
+ next stack.call(gateway_request.request) if context.span.nil?
122
120
 
123
121
  gateway_request.headers.each do |name, value|
124
- if !Ext::COLLECTABLE_REQUEST_HEADERS.include?(name) &&
125
- !Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
126
- next
122
+ if Ext::COLLECTABLE_REQUEST_HEADERS.include?(name)
123
+ context.span["http.request.headers.#{name}"] ||= value
127
124
  end
128
125
 
129
- context.span["http.request.headers.#{name}"] ||= value
126
+ if context.state[:has_identity_event] && Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
127
+ context.span["http.request.headers.#{name}"] ||= value
128
+ end
130
129
  end
131
130
 
132
131
  stack.call(gateway_request.request)
@@ -27,7 +27,7 @@ module Datadog
27
27
  end
28
28
 
29
29
  def self.compatible?
30
- super && version >= MINIMUM_VERSION
30
+ !!(super && (version&.>= MINIMUM_VERSION))
31
31
  end
32
32
 
33
33
  def self.auto_instrument?
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'json'
4
4
 
5
+ require_relative 'response_body'
5
6
  require_relative 'gateway/request'
6
7
  require_relative 'gateway/response'
7
8
 
@@ -18,6 +19,13 @@ module Datadog
18
19
  module AppSec
19
20
  module Contrib
20
21
  module Rack
22
+ RESPONSE_HEADERS_TAGS = %w[
23
+ content-length
24
+ content-type
25
+ content-encoding
26
+ content-language
27
+ ].freeze
28
+
21
29
  WAF_VENDOR_HEADERS_TAGS = %w[
22
30
  X-Amzn-Trace-Id
23
31
  Cloudfront-Viewer-Ja3-Fingerprint
@@ -110,8 +118,9 @@ module Datadog
110
118
  ctx.extract_schema!
111
119
  end
112
120
 
113
- AppSec::Event.record(ctx, request: gateway_request, response: gateway_response)
121
+ AppSec::Event.record(ctx, request: gateway_request)
114
122
 
123
+ add_response_tags(ctx, tmp_response)
115
124
  http_response
116
125
  ensure
117
126
  if ctx
@@ -180,7 +189,6 @@ module Datadog
180
189
  # standard:disable Metrics/MethodLength
181
190
  def add_request_tags(context, env)
182
191
  span = context.span
183
-
184
192
  return unless span
185
193
 
186
194
  # Always add WAF vendors headers
@@ -202,6 +210,21 @@ module Datadog
202
210
  end
203
211
  # standard:enable Metrics/MethodLength
204
212
 
213
+ def add_response_tags(context, response)
214
+ span = context.span
215
+ return unless span
216
+
217
+ RESPONSE_HEADERS_TAGS.each do |name|
218
+ value = response.headers[name]
219
+ span.set_tag("http.response.headers.#{name}", value.to_s) if value
220
+ end
221
+
222
+ unless response.headers.key?('content-length')
223
+ length = ResponseBody.content_length(response.body)
224
+ span.set_tag('http.response.headers.content-length', length.to_s) if length
225
+ end
226
+ end
227
+
205
228
  def oneshot_tags_sent?
206
229
  @oneshot_tags_sent
207
230
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module Contrib
6
+ module Rack
7
+ module ResponseBody
8
+ # NOTE: We compute content length only for fixed-size response bodies,
9
+ # ignoring streaming bodies to avoid buffering.
10
+ #
11
+ # On Rack 3.x, `body.to_ary` on a BodyProxy triggers `close` on all
12
+ # nested BodyProxy layers. This is safe because web servers, like Puma
13
+ # handles already-closed bodies (its own `to_ary` becomes a no-op).
14
+ #
15
+ # @see Puma::Response#prepare_response
16
+ # @see https://github.com/puma/puma/blob/b1271222cbf21868f3fb64154caa4d03936a7b9e/lib/puma/response.rb#L165-L168
17
+ def self.content_length(body)
18
+ return unless body.respond_to?(:to_ary)
19
+
20
+ # NOTE: When `to_ary` exists but returns `nil`, the body will be
21
+ # transferred in chunks and we can't compute content length
22
+ # without buffering it.
23
+ body_ary = body.to_ary
24
+ return unless body_ary.is_a?(Array)
25
+
26
+ body_ary.sum { |part| part.is_a?(String) ? part.bytesize : 0 }
27
+ rescue => e
28
+ AppSec.telemetry.report(e, description: 'AppSec: Failed to compute body content length')
29
+
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -21,7 +21,7 @@ module Datadog
21
21
  end
22
22
 
23
23
  def watch_request_action(gateway = Instrumentation.gateway)
24
- gateway.watch('rails.request.action', :appsec) do |stack, gateway_request|
24
+ gateway.watch('rails.request.action') do |stack, gateway_request|
25
25
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
26
26
 
27
27
  persistent_data = {
@@ -47,7 +47,7 @@ module Datadog
47
47
  end
48
48
 
49
49
  def watch_response_body_json(gateway = Instrumentation.gateway)
50
- gateway.watch('rails.response.body.json', :appsec) do |stack, container|
50
+ gateway.watch('rails.response.body.json') do |stack, container|
51
51
  context = container.context
52
52
 
53
53
  persistent_data = {
@@ -26,7 +26,7 @@ module Datadog
26
26
  end
27
27
 
28
28
  def self.compatible?
29
- super && version >= MINIMUM_VERSION
29
+ !!(super && (version&.>= MINIMUM_VERSION))
30
30
  end
31
31
 
32
32
  def self.auto_instrument?
@@ -138,7 +138,7 @@ module Datadog
138
138
  end
139
139
  rescue => e
140
140
  error_message = 'Failed to get application routes'
141
- Datadog.logger.error("#{error_message}, #{e.class}: #{e.message}")
141
+ Datadog.logger.error("#{error_message}, #{e.class}: #{e}")
142
142
  AppSec.telemetry.report(e, description: error_message)
143
143
  end
144
144
  end
@@ -148,7 +148,7 @@ module Datadog
148
148
  Datadog::AppSec::Contrib::Rails::Patcher.report_routes_via_telemetry(::Rails.application.routes.routes)
149
149
  rescue => e
150
150
  error_message = 'Failed to get application routes'
151
- Datadog.logger.error("#{error_message}, #{e.class}: #{e.message}")
151
+ Datadog.logger.error("#{error_message}, #{e.class}: #{e}")
152
152
  AppSec.telemetry.report(e, description: error_message)
153
153
  end
154
154
  end
@@ -20,6 +20,8 @@ module Datadog
20
20
  require_relative 'request_ssrf_detection_patch'
21
21
 
22
22
  ::RestClient::Request.prepend(RequestSSRFDetectionPatch)
23
+
24
+ Patcher.instance_variable_set(:@patched, true)
23
25
  end
24
26
  end
25
27
  end
@@ -50,12 +50,12 @@ module Datadog
50
50
  response = super
51
51
  rescue ::RestClient::Exception => e
52
52
  response = e.response
53
- process_response(response, sample_body: sample_body) if response
53
+ process_response(response, sample_body: sample_body) if response.is_a?(::RestClient::AbstractResponse)
54
54
 
55
55
  raise
56
56
  end
57
57
 
58
- process_response(response, sample_body: sample_body)
58
+ process_response(response, sample_body: sample_body) if response.is_a?(::RestClient::AbstractResponse)
59
59
  response
60
60
  end
61
61
 
@@ -22,7 +22,7 @@ module Datadog
22
22
  end
23
23
 
24
24
  def watch_request_dispatch(gateway = Instrumentation.gateway)
25
- gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
25
+ gateway.watch('sinatra.request.dispatch') do |stack, gateway_request|
26
26
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY] # : Context
27
27
 
28
28
  context.state[:web_framework] = 'sinatra'
@@ -51,7 +51,7 @@ module Datadog
51
51
  end
52
52
 
53
53
  def watch_request_routed(gateway = Instrumentation.gateway)
54
- gateway.watch('sinatra.request.routed', :appsec) do |stack, args|
54
+ gateway.watch('sinatra.request.routed') do |stack, args|
55
55
  gateway_request, gateway_route_params = args # : [Gateway::Request, Gateway::RouteParams]
56
56
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY] # : Context
57
57
 
@@ -77,7 +77,7 @@ module Datadog
77
77
  end
78
78
 
79
79
  def watch_response_body_json(gateway = Instrumentation.gateway)
80
- gateway.watch('sinatra.response.body.json', :appsec) do |stack, container|
80
+ gateway.watch('sinatra.response.body.json') do |stack, container|
81
81
  context = container.context # : Context
82
82
 
83
83
  persistent_data = {
@@ -32,13 +32,6 @@ module Datadog
32
32
  accept-language
33
33
  ].freeze
34
34
 
35
- ALLOWED_RESPONSE_HEADERS = %w[
36
- content-length
37
- content-type
38
- content-encoding
39
- content-language
40
- ].freeze
41
-
42
35
  class << self
43
36
  def tag(context, waf_result)
44
37
  return if context.span.nil?
@@ -50,7 +43,7 @@ module Datadog
50
43
  context.span.set_tag('appsec.event', 'true')
51
44
  end
52
45
 
53
- def record(context, request: nil, response: nil)
46
+ def record(context, request: nil)
54
47
  return if context.events.empty? || context.span.nil?
55
48
 
56
49
  Datadog::AppSec::RateLimiter.thread_local.limit do
@@ -66,7 +59,6 @@ module Datadog
66
59
 
67
60
  context.span['_dd.origin'] = 'appsec'
68
61
  context.span.set_tags(request_tags(request)) if request
69
- context.span.set_tags(response_tags(response)) if response
70
62
  end
71
63
 
72
64
  context.span.set_tags(waf_tags(event_group))
@@ -90,14 +82,6 @@ module Datadog
90
82
  end
91
83
  end
92
84
 
93
- def response_tags(response)
94
- response.headers.each_with_object({}) do |(name, value), memo|
95
- next unless ALLOWED_RESPONSE_HEADERS.include?(name)
96
-
97
- memo["http.response.headers.#{name}"] = value
98
- end
99
- end
100
-
101
85
  def waf_tags(security_events)
102
86
  triggers = []
103
87
 
@@ -7,10 +7,9 @@ module Datadog
7
7
  # NOTE: This class extracted as-is and will be deprecated
8
8
  # Instrumentation gateway middleware
9
9
  class Middleware
10
- attr_reader :key, :block
10
+ attr_reader :block
11
11
 
12
- def initialize(key, &block)
13
- @key = key
12
+ def initialize(&block)
14
13
  @block = block
15
14
  end
16
15
 
@@ -10,18 +10,9 @@ module Datadog
10
10
  class Gateway
11
11
  def initialize
12
12
  @middlewares = Hash.new { |h, k| h[k] = [] }
13
- @pushed_events = {}
14
13
  end
15
14
 
16
- # NOTE: Be careful with pushed names because every pushed event name
17
- # is recorded in order to provide an ability to any subscriber
18
- # to check wether an arbitrary event had happened.
19
- #
20
- # WARNING: If we start pushing generated names we should consider
21
- # limiting the storage of pushed names.
22
15
  def push(name, env, &block)
23
- @pushed_events[name] = true
24
-
25
16
  block ||= -> {}
26
17
  middlewares_for_name = @middlewares[name]
27
18
 
@@ -41,12 +32,8 @@ module Datadog
41
32
  stack.call(env)
42
33
  end
43
34
 
44
- def watch(name, key, &block)
45
- @middlewares[name] << Middleware.new(key, &block) unless @middlewares[name].any? { |m| m.key == key }
46
- end
47
-
48
- def pushed?(name)
49
- @pushed_events.key?(name)
35
+ def watch(name, &block)
36
+ @middlewares[name] << Middleware.new(&block)
50
37
  end
51
38
  end
52
39
 
@@ -24,8 +24,9 @@ module Datadog
24
24
  end
25
25
 
26
26
  def watch_user_id(gateway = Instrumentation.gateway)
27
- gateway.watch('identity.set_user', :appsec) do |stack, user|
27
+ gateway.watch('identity.set_user') do |stack, user|
28
28
  context = AppSec.active_context
29
+ context.state[:has_identity_event] = true
29
30
 
30
31
  if user.id.nil? && user.login.nil? && user.session_id.nil?
31
32
  Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
@@ -55,8 +56,9 @@ module Datadog
55
56
  end
56
57
 
57
58
  def watch_user_login(gateway = Instrumentation.gateway)
58
- gateway.watch('appsec.events.user_lifecycle', :appsec) do |stack, kind|
59
+ gateway.watch('appsec.events.user_lifecycle') do |stack, kind|
59
60
  context = AppSec.active_context
61
+ context.state[:has_identity_event] = true
60
62
 
61
63
  next stack.call(kind) unless WATCHED_LOGIN_EVENTS.include?(kind)
62
64
 
@@ -68,8 +68,7 @@ module Datadog
68
68
  name.downcase! # steep:ignore NoMethod
69
69
  value.downcase!
70
70
 
71
- # See https://github.com/soutaro/steep/issues/2051
72
- parameters[name] = value # steep:ignore ArgumentTypeMismatch
71
+ parameters[name] = value
73
72
  end
74
73
  end
75
74
 
@@ -30,9 +30,9 @@ module Datadog
30
30
  #
31
31
  # @type var key: ::String
32
32
  # @type var value: ::String
33
- key, value = pair.split('=', 2).map! do |value| #: ::String
33
+ key, value = pair.split('=', 2).map! do |value|
34
34
  CGI.unescape(value)
35
- end
35
+ end #: [::String, ::String]
36
36
 
37
37
  if (stored = memo[key])
38
38
  if stored.is_a?(Array)
@@ -11,25 +11,21 @@ module Datadog
11
11
  module AppSec
12
12
  class << self
13
13
  def enabled?
14
- Datadog.configuration.appsec.enabled
14
+ !!components.appsec
15
15
  end
16
16
 
17
17
  def rasp_enabled?
18
- Datadog.configuration.appsec.rasp_enabled
18
+ # TODO this should take rasp_enabled flag from the settings in
19
+ # the appsec component rather than reading global configuration.
20
+ enabled? && Datadog.configuration.appsec.rasp_enabled
19
21
  end
20
22
 
21
23
  def active_context
22
24
  Datadog::AppSec::Context.active
23
25
  end
24
26
 
25
- # NOTE: This is a temporary workaround for type checking.
26
- #
27
- # We want to move from possible nil-component to the disabled-component
28
- # on an initialization error. Technically, telemetry will be never
29
- # used if AppSec was not able to initialize, so it's safe to assume
30
- # that telemetry will never be used and will be nil at the same time.
31
27
  def telemetry
32
- components.appsec&.telemetry || components.telemetry
28
+ components.telemetry
33
29
  end
34
30
 
35
31
  def security_engine
@@ -24,9 +24,21 @@ module Datadog
24
24
  # e.g. `settings :foo { option :bar }` --> `config.foo.bar`
25
25
  # @param [Symbol] name option name. Methods will be created based on this name.
26
26
  def settings(name, &block)
27
- settings_class = new_settings_class(name, &block)
28
-
29
- option(name) do |o|
27
+ nested_settings_path = settings_path ? "#{settings_path}.#{name}" : name.to_s
28
+ settings_class = new_settings_class(name, nested_settings_path, &block)
29
+ # Record the child settings class on the owning class so
30
+ # Options::ClassMethods#settings_path= can later propagate any path
31
+ # changes down to nested settings classes.
32
+ #
33
+ # Example:
34
+ # settings :cache_key { option :enabled }
35
+ # creates a child class whose initial settings_path is "cache_key".
36
+ # If a contrib integration later assigns the parent path to
37
+ # "tracing.active_support", settings_children lets us update the
38
+ # nested class to "tracing.active_support.cache_key" as well.
39
+ settings_children[name] = settings_class
40
+
41
+ option(name, is_settings: true) do |o|
30
42
  o.default { settings_class.new }
31
43
 
32
44
  o.resetter do |value|
@@ -40,9 +52,9 @@ module Datadog
40
52
 
41
53
  private
42
54
 
43
- def new_settings_class(name, &block)
55
+ def new_settings_class(name, settings_path, &block)
44
56
  Class.new { include Configuration::Base }.tap do |klass|
45
- klass.instance_variable_set(:@settings_name, name)
57
+ klass.instance_variable_set(:@settings_path, settings_path)
46
58
  klass.instance_eval(&block) if block
47
59
  end
48
60
  end
@@ -12,6 +12,7 @@ require_relative '../telemetry/component'
12
12
  require_relative '../workers/runtime_metrics'
13
13
  require_relative '../remote/component'
14
14
  require_relative '../utils/at_fork_monkey_patch'
15
+ require_relative '../utils/spawn_monkey_patch'
15
16
  require_relative '../utils/only_once'
16
17
  require_relative '../../tracing/component'
17
18
  require_relative '../../profiling/component'
@@ -22,6 +23,7 @@ require_relative '../../open_feature/component'
22
23
  require_relative '../../error_tracking/component'
23
24
  require_relative '../crashtracking/component'
24
25
  require_relative '../environment/agent_info'
26
+ require_relative '../environment/identity'
25
27
  require_relative '../process_discovery'
26
28
  require_relative '../../data_streams/processor'
27
29
 
@@ -31,7 +33,7 @@ module Datadog
31
33
  # Global components for the trace library.
32
34
  class Components
33
35
  # Class-level constant to ensure fork patch is applied only once
34
- AT_FORK_ONLY_ONCE = Utils::OnlyOnce.new
36
+ PATCH_ONLY_ONCE = Utils::OnlyOnce.new
35
37
 
36
38
  class << self
37
39
  def build_health_metrics(settings, logger, telemetry)
@@ -128,8 +130,11 @@ module Datadog
128
130
  Deprecations.log_deprecations_from_all_sources(@logger)
129
131
 
130
132
  # Register fork handling once globally
131
- self.class::AT_FORK_ONLY_ONCE.run do
133
+ self.class::PATCH_ONLY_ONCE.run do
132
134
  Utils::AtForkMonkeyPatch.apply!
135
+ Utils::SpawnMonkeyPatch.apply!(
136
+ lineage_envs_provider: Core::Environment::Identity.method(:runtime_propagation_envs),
137
+ )
133
138
 
134
139
  # Register callback that calls Components.after_fork
135
140
  Utils::AtForkMonkeyPatch.at_fork(:child) do
@@ -172,6 +177,11 @@ module Datadog
172
177
 
173
178
  # Configure non-privileged components.
174
179
  Datadog::Tracing::Contrib::Component.configure(settings)
180
+
181
+ # Load the core Rails Railtie when Rails is present so all products benefit from Rails-specific setup.
182
+ if defined?(::Rails::Railtie)
183
+ require_relative '../contrib/rails/railtie'
184
+ end
175
185
  end
176
186
 
177
187
  # Called when a fork is detected
@@ -204,14 +214,13 @@ module Datadog
204
214
  end
205
215
  end
206
216
 
207
- if settings.remote.enabled && old_state&.remote_started?
217
+ if remote && old_state&.remote_started?
208
218
  # The library was reconfigured and previously it already started
209
219
  # the remote component (i.e., it received at least one request
210
220
  # through the installed Rack middleware which started the remote).
211
221
  # If the new configuration also has remote enabled, start the
212
222
  # new remote right away.
213
- # remote should always be not nil here but steep doesn't know this.
214
- remote&.start
223
+ remote.start
215
224
  end
216
225
 
217
226
  # This should stay here, not in initialize. During reconfiguration, the order of the calls is:
@@ -279,11 +288,15 @@ module Datadog
279
288
  unused_statsd = (old_statsd - (old_statsd & new_statsd))
280
289
  unused_statsd.each(&:close)
281
290
 
282
- # enqueue closing event before stopping telemetry so it will be sent out on shutdown
291
+ Core::ProcessDiscovery.shutdown!
292
+
293
+ # Shut down telemetry last so that all other components may
294
+ # report shutdown errors.
295
+ #
296
+ # Enqueue closing event before stopping telemetry so it will be
297
+ # sent out on shutdown.
283
298
  telemetry.emit_closing! unless replacement&.telemetry&.enabled
284
299
  telemetry.shutdown!
285
-
286
- Core::ProcessDiscovery.shutdown!
287
300
  end
288
301
 
289
302
  # Returns the current state of various components.
@@ -40,6 +40,12 @@ module Datadog
40
40
  !get_environment_variable(name).nil?
41
41
  end
42
42
 
43
+ # Returns the source environment as a Hash. Used when the full environment
44
+ # must be passed through (e.g. Process.spawn child env).
45
+ def to_h
46
+ @source_env.to_h
47
+ end
48
+
43
49
  alias_method :has_key?, :key?
44
50
  alias_method :include?, :key?
45
51
  alias_method :member?, :key?
@@ -97,4 +103,7 @@ module Datadog
97
103
  end
98
104
  end
99
105
  end
106
+ unless const_defined?(:DATADOG_ENV, false)
107
+ DATADOG_ENV = Core::Configuration::ConfigHelper.new
108
+ end
100
109
  end
@@ -77,6 +77,16 @@ module Datadog
77
77
  @precedence_set = Precedence::DEFAULT
78
78
  end
79
79
 
80
+ # Computes the name of the option with the settings path.
81
+ # E.g. "tracing.rails.middleware_names" for the "middleware_names" option in the "tracing.rails" settings.
82
+ # @return [String] the name of the option with the settings path
83
+ def name_with_settings_path
84
+ @name_with_settings_path ||= begin
85
+ settings_path = @context.class.settings_path
86
+ settings_path.nil? ? definition.name.to_s : "#{settings_path}.#{definition.name}"
87
+ end
88
+ end
89
+
80
90
  # Overrides the current value for this option if the `precedence` is equal or higher than
81
91
  # the previously set value.
82
92
  # The first call to `#set` will always store the value regardless of precedence.
@@ -89,7 +99,7 @@ module Datadog
89
99
  # This should be uncommon, as higher precedence values tend to
90
100
  # happen later in the application lifecycle.
91
101
  Datadog.logger.info do
92
- "Option '#{definition.name}' not changed to '#{value}' (precedence: #{precedence.name}) because the higher " \
102
+ "Option '#{name_with_settings_path}' not changed to '#{value}' (precedence: #{precedence.name}) because the higher " \
93
103
  "precedence value '#{@value}' (precedence: #{@precedence_set.name}) was already set."
94
104
  end
95
105
 
@@ -167,7 +177,8 @@ module Datadog
167
177
 
168
178
  def default_value
169
179
  if definition.default.instance_of?(Proc)
170
- context_eval(&definition.default)
180
+ # Steep: https://github.com/soutaro/steep/issues/335
181
+ context_eval(&definition.default) # steep:ignore BlockTypeMismatch
171
182
  else
172
183
  definition.default_proc || Core::Utils::SafeDup.frozen_or_dup(definition.default)
173
184
  end
@@ -177,6 +188,21 @@ module Datadog
177
188
  precedence_set == Precedence::DEFAULT
178
189
  end
179
190
 
191
+ def settings?
192
+ @definition.is_settings
193
+ end
194
+
195
+ def values_per_precedence
196
+ # value_per_precedence is only filled after we call `get` once.
197
+ get unless @is_set
198
+
199
+ @value_per_precedence.each_with_object({}) do |(precedence, value), result|
200
+ next if value.equal?(UNSET)
201
+
202
+ result[precedence] = value
203
+ end
204
+ end
205
+
180
206
  private
181
207
 
182
208
  def coerce_env_variable(value)
@@ -215,7 +241,7 @@ module Datadog
215
241
  value
216
242
  else
217
243
  raise InvalidDefinitionError,
218
- "The option #{@definition.name} is using an unsupported type option for env coercion `#{@definition.type}`"
244
+ "The option #{name_with_settings_path} is using an unsupported type option for env coercion `#{@definition.type}`"
219
245
  end
220
246
  end
221
247
 
@@ -238,10 +264,10 @@ module Datadog
238
264
 
239
265
  if raise_error
240
266
  error_msg = if @definition.type_options[:nilable]
241
- "The setting `#{@definition.name}` inside your app's `Datadog.configure` block expects a " \
267
+ "The setting `#{name_with_settings_path}` inside your app's `Datadog.configure` block expects a " \
242
268
  "#{@definition.type} or `nil`, but a `#{value.class}` was provided (#{value.inspect})." \
243
269
  else
244
- "The setting `#{@definition.name}` inside your app's `Datadog.configure` block expects a " \
270
+ "The setting `#{name_with_settings_path}` inside your app's `Datadog.configure` block expects a " \
245
271
  "#{@definition.type}, but a `#{value.class}` was provided (#{value.inspect})." \
246
272
  end
247
273
 
@@ -275,7 +301,7 @@ module Datadog
275
301
  true # No validation is performed when option is typeless
276
302
  else
277
303
  raise InvalidDefinitionError,
278
- "The option #{@definition.name} is using an unsupported type option `#{@definition.type}`"
304
+ "The option #{name_with_settings_path} is using an unsupported type option `#{@definition.type}`"
279
305
  end
280
306
  end
281
307