datadog 2.23.0 → 2.28.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -2
  3. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +2 -1
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +100 -29
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +2 -2
  6. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +23 -10
  8. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -12
  9. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  10. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +48 -1
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +41 -0
  12. data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
  13. data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
  14. data/ext/datadog_profiling_native_extension/heap_recorder.c +24 -24
  15. data/ext/datadog_profiling_native_extension/http_transport.c +10 -4
  16. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
  17. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
  18. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +21 -8
  19. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  20. data/ext/datadog_profiling_native_extension/profiling.c +22 -15
  21. data/ext/datadog_profiling_native_extension/ruby_helpers.c +55 -44
  22. data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
  23. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +8 -2
  24. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +3 -0
  25. data/ext/datadog_profiling_native_extension/stack_recorder.c +16 -16
  26. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
  27. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +5 -2
  28. data/ext/libdatadog_api/crashtracker.c +5 -8
  29. data/ext/libdatadog_api/datadog_ruby_common.c +48 -1
  30. data/ext/libdatadog_api/datadog_ruby_common.h +41 -0
  31. data/ext/libdatadog_api/ddsketch.c +4 -8
  32. data/ext/libdatadog_api/feature_flags.c +5 -5
  33. data/ext/libdatadog_api/helpers.h +27 -0
  34. data/ext/libdatadog_api/init.c +4 -0
  35. data/ext/libdatadog_extconf_helpers.rb +1 -1
  36. data/lib/datadog/ai_guard/api_client.rb +82 -0
  37. data/lib/datadog/ai_guard/component.rb +42 -0
  38. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  39. data/lib/datadog/ai_guard/configuration/settings.rb +110 -0
  40. data/lib/datadog/ai_guard/configuration.rb +11 -0
  41. data/lib/datadog/ai_guard/contrib/integration.rb +37 -0
  42. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +42 -0
  43. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +41 -0
  44. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +30 -0
  45. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  46. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  47. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  48. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  49. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  50. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  51. data/lib/datadog/ai_guard/ext.rb +16 -0
  52. data/lib/datadog/ai_guard.rb +155 -0
  53. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +8 -1
  54. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +9 -2
  55. data/lib/datadog/appsec/component.rb +1 -1
  56. data/lib/datadog/appsec/context.rb +5 -4
  57. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  58. data/lib/datadog/appsec/contrib/active_record/patcher.rb +1 -1
  59. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  60. data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
  61. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +47 -12
  62. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +32 -15
  63. data/lib/datadog/appsec/contrib/rails/patcher.rb +10 -2
  64. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  65. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
  66. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +50 -14
  67. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -4
  68. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  69. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +4 -4
  70. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +1 -1
  71. data/lib/datadog/appsec/ext.rb +2 -0
  72. data/lib/datadog/appsec/metrics/collector.rb +8 -3
  73. data/lib/datadog/appsec/metrics/exporter.rb +7 -0
  74. data/lib/datadog/appsec/metrics/telemetry.rb +7 -2
  75. data/lib/datadog/appsec/metrics.rb +5 -5
  76. data/lib/datadog/appsec/remote.rb +7 -14
  77. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  78. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  79. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  80. data/lib/datadog/appsec/utils/http/media_type.rb +37 -23
  81. data/lib/datadog/appsec.rb +7 -1
  82. data/lib/datadog/core/configuration/components.rb +7 -0
  83. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  84. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  85. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  86. data/lib/datadog/core/configuration/options.rb +8 -5
  87. data/lib/datadog/core/configuration/settings.rb +31 -3
  88. data/lib/datadog/core/configuration/supported_configurations.rb +10 -1
  89. data/lib/datadog/core/crashtracking/tag_builder.rb +6 -0
  90. data/lib/datadog/core/environment/cgroup.rb +52 -25
  91. data/lib/datadog/core/environment/container.rb +140 -46
  92. data/lib/datadog/core/environment/ext.rb +1 -0
  93. data/lib/datadog/core/environment/process.rb +9 -1
  94. data/lib/datadog/core/error.rb +6 -6
  95. data/lib/datadog/core/knuth_sampler.rb +57 -0
  96. data/lib/datadog/core/pin.rb +4 -0
  97. data/lib/datadog/core/rate_limiter.rb +9 -1
  98. data/lib/datadog/core/remote/client.rb +14 -6
  99. data/lib/datadog/core/remote/component.rb +6 -4
  100. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  101. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  102. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  103. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  104. data/lib/datadog/core/remote/transport/config.rb +3 -16
  105. data/lib/datadog/core/remote/transport/http/config.rb +4 -44
  106. data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
  107. data/lib/datadog/core/remote/transport/http.rb +13 -24
  108. data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
  109. data/lib/datadog/core/runtime/metrics.rb +11 -1
  110. data/lib/datadog/core/semaphore.rb +1 -4
  111. data/lib/datadog/core/telemetry/component.rb +52 -13
  112. data/lib/datadog/core/telemetry/event/app_started.rb +36 -1
  113. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  114. data/lib/datadog/core/telemetry/logger.rb +2 -0
  115. data/lib/datadog/core/telemetry/logging.rb +20 -2
  116. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  117. data/lib/datadog/core/telemetry/request.rb +17 -3
  118. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
  119. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  120. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
  121. data/lib/datadog/core/telemetry/worker.rb +88 -32
  122. data/lib/datadog/core/transport/ext.rb +2 -0
  123. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  124. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  125. data/lib/datadog/core/transport/http/builder.rb +9 -5
  126. data/lib/datadog/core/transport/http/client.rb +19 -8
  127. data/lib/datadog/core/transport/http.rb +22 -19
  128. data/lib/datadog/core/transport/response.rb +12 -1
  129. data/lib/datadog/core/transport/transport.rb +90 -0
  130. data/lib/datadog/core/utils/only_once_successful.rb +2 -0
  131. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  132. data/lib/datadog/core/utils/sequence.rb +2 -0
  133. data/lib/datadog/core/utils/time.rb +1 -1
  134. data/lib/datadog/core/workers/async.rb +10 -1
  135. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  136. data/lib/datadog/core/workers/polling.rb +2 -0
  137. data/lib/datadog/core/workers/queue.rb +100 -1
  138. data/lib/datadog/data_streams/processor.rb +1 -1
  139. data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
  140. data/lib/datadog/data_streams/transport/http.rb +5 -6
  141. data/lib/datadog/data_streams/transport/stats.rb +3 -17
  142. data/lib/datadog/di/boot.rb +4 -2
  143. data/lib/datadog/di/configuration/settings.rb +22 -0
  144. data/lib/datadog/di/contrib/active_record.rb +30 -5
  145. data/lib/datadog/di/el/compiler.rb +8 -4
  146. data/lib/datadog/di/error.rb +5 -0
  147. data/lib/datadog/di/instrumenter.rb +26 -7
  148. data/lib/datadog/di/logger.rb +2 -2
  149. data/lib/datadog/di/probe_builder.rb +2 -1
  150. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  151. data/lib/datadog/di/probe_manager.rb +37 -31
  152. data/lib/datadog/di/probe_notification_builder.rb +15 -2
  153. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  154. data/lib/datadog/di/redactor.rb +8 -1
  155. data/lib/datadog/di/remote.rb +89 -84
  156. data/lib/datadog/di/transport/diagnostics.rb +7 -35
  157. data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
  158. data/lib/datadog/di/transport/http/input.rb +1 -31
  159. data/lib/datadog/di/transport/http.rb +28 -17
  160. data/lib/datadog/di/transport/input.rb +7 -34
  161. data/lib/datadog/di.rb +61 -5
  162. data/lib/datadog/error_tracking/filters.rb +2 -2
  163. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  164. data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
  165. data/lib/datadog/open_feature/remote.rb +3 -10
  166. data/lib/datadog/open_feature/transport.rb +9 -11
  167. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  168. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
  169. data/lib/datadog/opentelemetry/metrics.rb +21 -14
  170. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
  171. data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
  172. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  173. data/lib/datadog/profiling/collectors/info.rb +5 -4
  174. data/lib/datadog/profiling/component.rb +25 -11
  175. data/lib/datadog/profiling/exporter.rb +4 -0
  176. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  177. data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
  178. data/lib/datadog/profiling/flush.rb +3 -0
  179. data/lib/datadog/profiling/http_transport.rb +4 -1
  180. data/lib/datadog/profiling/profiler.rb +3 -5
  181. data/lib/datadog/profiling/scheduler.rb +8 -7
  182. data/lib/datadog/profiling/tag_builder.rb +1 -0
  183. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  184. data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
  185. data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
  186. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  187. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
  188. data/lib/datadog/tracing/contrib/waterdrop.rb +4 -0
  189. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  190. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  191. data/lib/datadog/tracing/remote.rb +1 -9
  192. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  193. data/lib/datadog/tracing/sampling/rate_sampler.rb +8 -19
  194. data/lib/datadog/tracing/span.rb +1 -1
  195. data/lib/datadog/tracing/span_event.rb +2 -2
  196. data/lib/datadog/tracing/span_operation.rb +20 -9
  197. data/lib/datadog/tracing/trace_operation.rb +44 -6
  198. data/lib/datadog/tracing/tracer.rb +42 -16
  199. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  200. data/lib/datadog/tracing/transport/http.rb +15 -9
  201. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  202. data/lib/datadog/tracing/transport/traces.rb +6 -66
  203. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  204. data/lib/datadog/tracing/writer.rb +1 -0
  205. data/lib/datadog/version.rb +2 -2
  206. data/lib/datadog.rb +1 -0
  207. metadata +33 -19
  208. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  209. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  210. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  211. data/lib/datadog/data_streams/transport/http/api.rb +0 -33
  212. data/lib/datadog/data_streams/transport/http/client.rb +0 -21
  213. data/lib/datadog/di/transport/http/api.rb +0 -42
  214. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  215. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -13,6 +13,7 @@ module Datadog
13
13
  :duration_ns,
14
14
  :duration_ext_ns,
15
15
  :inputs_truncated,
16
+ :downstream_requests,
16
17
  keyword_init: true
17
18
  )
18
19
 
@@ -22,10 +23,13 @@ module Datadog
22
23
  @mutex = Mutex.new
23
24
 
24
25
  @waf = Store.new(
25
- evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0, inputs_truncated: 0
26
+ evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0,
27
+ duration_ext_ns: 0, inputs_truncated: 0, downstream_requests: 0
26
28
  )
29
+
27
30
  @rasp = Store.new(
28
- evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0, inputs_truncated: 0
31
+ evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0,
32
+ duration_ext_ns: 0, inputs_truncated: 0, downstream_requests: 0
29
33
  )
30
34
  end
31
35
 
@@ -41,7 +45,7 @@ module Datadog
41
45
  end
42
46
  end
43
47
 
44
- def record_rasp(result)
48
+ def record_rasp(result, type:, phase: nil)
45
49
  @mutex.synchronize do
46
50
  @rasp.evals += 1
47
51
  @waf.matches += 1 if result.match?
@@ -50,6 +54,7 @@ module Datadog
50
54
  @rasp.duration_ns += result.duration_ns
51
55
  @rasp.duration_ext_ns += result.duration_ext_ns
52
56
  @rasp.inputs_truncated += 1 if result.input_truncated?
57
+ @rasp.downstream_requests += 1 if type == Ext::RASP_SSRF && phase == Ext::RASP_REQUEST_PHASE
53
58
  end
54
59
  end
55
60
  end
@@ -22,6 +22,13 @@ module Datadog
22
22
  span.set_tag('_dd.appsec.rasp.timeout', 1) unless metrics.timeouts.zero?
23
23
  span.set_tag('_dd.appsec.rasp.duration', convert_ns_to_us(metrics.duration_ns))
24
24
  span.set_tag('_dd.appsec.rasp.duration_ext', convert_ns_to_us(metrics.duration_ext_ns))
25
+
26
+ # NOTE: In case of downstream requests being analyzed additionally
27
+ # with `Context.run_waf` method, we would need to share it
28
+ # between two exporting methods
29
+ unless metrics.downstream_requests.zero?
30
+ span.set_tag('_dd.appsec.downstream_request', metrics.downstream_requests)
31
+ end
25
32
  end
26
33
 
27
34
  # private
@@ -7,10 +7,15 @@ module Datadog
7
7
  module Telemetry
8
8
  module_function
9
9
 
10
- def report_rasp(type, result)
10
+ def report_rasp(type, result, phase: nil)
11
11
  return if result.error?
12
12
 
13
- tags = {rule_type: type, waf_version: Datadog::AppSec::WAF::VERSION::BASE_STRING}
13
+ tags = {rule_type: type, waf_version: WAF::VERSION::BASE_STRING}
14
+ tags[:rule_variant] = phase if phase
15
+
16
+ context = AppSec.active_context
17
+ tags[:event_rules_version] = context.waf_runner_ruleset_version if context
18
+
14
19
  namespace = Ext::TELEMETRY_METRICS_NAMESPACE
15
20
 
16
21
  AppSec.telemetry.inc(namespace, 'rasp.rule.eval', 1, tags: tags)
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'metrics/collector'
4
+ require_relative 'metrics/exporter'
5
+ require_relative 'metrics/telemetry'
6
+ require_relative 'metrics/telemetry_exporter'
7
+
3
8
  module Datadog
4
9
  module AppSec
5
10
  # This namespace contains classes related to metrics collection and exportation.
@@ -7,8 +12,3 @@ module Datadog
7
12
  end
8
13
  end
9
14
  end
10
-
11
- require_relative 'metrics/collector'
12
- require_relative 'metrics/exporter'
13
- require_relative 'metrics/telemetry'
14
- require_relative 'metrics/telemetry_exporter'
@@ -7,8 +7,6 @@ module Datadog
7
7
  module AppSec
8
8
  # Remote
9
9
  module Remote
10
- class ReadError < StandardError; end
11
-
12
10
  class NoRulesError < StandardError; end
13
11
 
14
12
  class << self
@@ -77,7 +75,8 @@ module Datadog
77
75
 
78
76
  matcher = Core::Remote::Dispatcher::Matcher::Product.new(ASM_PRODUCTS)
79
77
  receiver = Core::Remote::Dispatcher::Receiver.new(matcher) do |repository, changes|
80
- next unless AppSec.security_engine
78
+ engine = AppSec.security_engine
79
+ next unless engine
81
80
 
82
81
  changes.each do |change|
83
82
  content = repository[change.path]
@@ -85,11 +84,11 @@ module Datadog
85
84
 
86
85
  case change.type
87
86
  when :insert, :update
88
- AppSec.security_engine.add_or_update_config(parse_content(content), path: change.path.to_s) # steep:ignore
89
-
90
- content.applied # steep:ignore
87
+ # @type var content: Core::Remote::Configuration::Content
88
+ engine.add_or_update_config(parse_content(content), path: change.path.to_s)
89
+ content.applied
91
90
  when :delete
92
- AppSec.security_engine.remove_config_at_path(change.path.to_s) # steep:ignore
91
+ engine.remove_config_at_path(change.path.to_s)
93
92
  end
94
93
  end
95
94
 
@@ -109,13 +108,7 @@ module Datadog
109
108
  end
110
109
 
111
110
  def parse_content(content)
112
- data = content.data.read
113
-
114
- content.data.rewind
115
-
116
- raise ReadError, 'EOF reached' if data.nil?
117
-
118
- JSON.parse(data)
111
+ JSON.parse(content.data)
119
112
  end
120
113
  end
121
114
  end
@@ -54,17 +54,17 @@ module Datadog
54
54
  end
55
55
 
56
56
  def add_or_update_config(config, path:)
57
- @is_ruleset_update = path.include?('ASM_DD')
57
+ is_ruleset_update = path.include?('ASM_DD')
58
58
 
59
59
  # default config has to be removed when adding an ASM_DD config
60
- remove_config_at_path(DEFAULT_RULES_CONFIG_PATH) if @is_ruleset_update
60
+ remove_config_at_path(DEFAULT_RULES_CONFIG_PATH) if is_ruleset_update
61
61
 
62
62
  diagnostics = @waf_builder.add_or_update_config(config, path: path)
63
63
  @reconfigured_ruleset_version = diagnostics['ruleset_version'] if diagnostics.key?('ruleset_version')
64
64
  report_configuration_diagnostics(diagnostics, action: 'update', telemetry: AppSec.telemetry)
65
65
 
66
66
  # we need to load default config if diagnostics contains top-level error for rules or processors
67
- if @is_ruleset_update &&
67
+ if is_ruleset_update &&
68
68
  (diagnostics.key?('error') ||
69
69
  diagnostics.dig('rules', 'error') ||
70
70
  diagnostics.dig('processors', 'errors'))
@@ -70,7 +70,8 @@ module Datadog
70
70
 
71
71
  def initialize(duration_ext_ns:, input_truncated:)
72
72
  @events = []
73
- @actions = @attributes = {}
73
+ @actions = {}.freeze
74
+ @attributes = {}.freeze
74
75
  @duration_ns = 0
75
76
  @duration_ext_ns = duration_ext_ns
76
77
  @input_truncated = !!input_truncated
@@ -27,13 +27,13 @@ module Datadog
27
27
  persistent_data.reject! do |_, v|
28
28
  next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
29
29
 
30
- v.nil? || v.empty?
30
+ v.nil? || (v.respond_to?(:empty?) && v.empty?)
31
31
  end
32
32
 
33
33
  ephemeral_data.reject! do |_, v|
34
34
  next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
35
35
 
36
- v.nil? || v.empty?
36
+ v.nil? || (v.respond_to?(:empty?) && v.empty?)
37
37
  end
38
38
 
39
39
  result = try_run(persistent_data, ephemeral_data, timeout)
@@ -4,14 +4,13 @@ module Datadog
4
4
  module AppSec
5
5
  module Utils
6
6
  module HTTP
7
- # Implementation of media type for content negotiation
7
+ # Implementation of media type for HTTP headers
8
8
  #
9
9
  # See:
10
10
  # - https://www.rfc-editor.org/rfc/rfc7231#section-5.3.1
11
11
  # - https://www.rfc-editor.org/rfc/rfc7231#section-5.3.2
12
12
  class MediaType
13
- class ParseError < ::StandardError
14
- end
13
+ ParseError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
15
14
 
16
15
  WILDCARD = '*'
17
16
 
@@ -19,7 +18,7 @@ module Datadog
19
18
  TOKEN_RE = /[-#$%&'*+.^_`|~A-Za-z0-9]+/.freeze
20
19
 
21
20
  # See: https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1.1
22
- PARAMETER_RE = %r{ # rubocop:disable Style/RegexpLiteral
21
+ PARAMETER_RE = %r{
23
22
  (?:
24
23
  (?<parameter_name>#{TOKEN_RE})
25
24
  =
@@ -46,39 +45,54 @@ module Datadog
46
45
 
47
46
  attr_reader :type, :subtype, :parameters
48
47
 
49
- def initialize(media_type)
50
- media_type_match = MEDIA_TYPE_RE.match(media_type)
48
+ def self.json?(media_type)
49
+ return false if media_type.nil? || media_type.empty?
51
50
 
52
- raise ParseError, media_type.inspect if media_type_match.nil?
51
+ match = MEDIA_TYPE_RE.match(media_type)
52
+ return false if match.nil?
53
53
 
54
- @type = (media_type_match['type'] || WILDCARD).downcase
55
- @subtype = (media_type_match['subtype'] || WILDCARD).downcase
56
- @parameters = {}
54
+ subtype = match['subtype']
55
+ return false if subtype.nil? || subtype.empty?
57
56
 
58
- parameters = media_type_match['parameters']
57
+ subtype.downcase!
58
+ subtype == 'json' || subtype.end_with?('+json')
59
+ end
59
60
 
60
- return if parameters.nil?
61
+ def initialize(media_type)
62
+ match = MEDIA_TYPE_RE.match(media_type)
63
+ raise ParseError, media_type.inspect if match.nil?
61
64
 
62
- parameters.split(';').map(&:strip).each do |parameter|
63
- parameter_match = PARAMETER_RE.match(parameter)
65
+ @type = match['type'] || WILDCARD
66
+ @type.downcase!
64
67
 
65
- next if parameter_match.nil?
68
+ @subtype = match['subtype'] || WILDCARD
69
+ @subtype.downcase!
66
70
 
67
- parameter_name = parameter_match['parameter_name']
68
- parameter_value = parameter_match['parameter_value']
71
+ @parameters = {}
72
+
73
+ parameters = match['parameters']
74
+ return if parameters.nil? || parameters.empty?
69
75
 
70
- next if parameter_name.nil? || parameter_value.nil?
76
+ parameters.scan(PARAMETER_RE) do |name, unquoted_value, quoted_value|
77
+ # NOTE: Order of unquoted_value and quoted_value does not matter,
78
+ # as they are mutually exclusive by the regex.
79
+ # @type var value: ::String?
80
+ value = unquoted_value || quoted_value
81
+ next if name.nil? || value.nil?
71
82
 
72
- @parameters[parameter_name.downcase] = parameter_value.downcase
83
+ # See https://github.com/soutaro/steep/issues/2051
84
+ name.downcase! # steep:ignore NoMethod
85
+ value.downcase!
86
+
87
+ # See https://github.com/soutaro/steep/issues/2051
88
+ @parameters[name] = value # steep:ignore ArgumentTypeMismatch
73
89
  end
74
90
  end
75
91
 
76
92
  def to_s
77
- s = +"#{@type}/#{@subtype}"
78
-
79
- s << ';' << @parameters.map { |k, v| "#{k}=#{v}" }.join(';') if @parameters.count > 0
93
+ return "#{@type}/#{@subtype}" if @parameters.empty?
80
94
 
81
- s
95
+ "#{@type}/#{@subtype};#{@parameters.map { |k, v| "#{k}=#{v}" }.join(";")}"
82
96
  end
83
97
  end
84
98
  end
@@ -22,8 +22,14 @@ module Datadog
22
22
  Datadog::AppSec::Context.active
23
23
  end
24
24
 
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.
25
31
  def telemetry
26
- components.appsec&.telemetry
32
+ components.appsec&.telemetry || components.telemetry
27
33
  end
28
34
 
29
35
  def security_engine
@@ -14,6 +14,7 @@ require_relative '../remote/component'
14
14
  require_relative '../../tracing/component'
15
15
  require_relative '../../profiling/component'
16
16
  require_relative '../../appsec/component'
17
+ require_relative '../../ai_guard/component'
17
18
  require_relative '../../di/component'
18
19
  require_relative '../../open_feature/component'
19
20
  require_relative '../../error_tracking/component'
@@ -48,6 +49,7 @@ module Datadog
48
49
  options[:statsd] = settings.runtime_metrics.statsd unless settings.runtime_metrics.statsd.nil?
49
50
  options[:services] = [settings.service] unless settings.service.nil?
50
51
  options[:experimental_runtime_id_enabled] = settings.runtime_metrics.experimental_runtime_id_enabled
52
+ options[:experimental_propagate_process_tags_enabled] = settings.experimental_propagate_process_tags_enabled
51
53
 
52
54
  Core::Runtime::Metrics.new(logger: logger, telemetry: telemetry, **options)
53
55
  end
@@ -107,6 +109,7 @@ module Datadog
107
109
  :error_tracking,
108
110
  :dynamic_instrumentation,
109
111
  :appsec,
112
+ :ai_guard,
110
113
  :agent_info,
111
114
  :data_streams,
112
115
  :open_feature
@@ -143,6 +146,7 @@ module Datadog
143
146
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings, @logger, telemetry)
144
147
  @health_metrics = self.class.build_health_metrics(settings, @logger, telemetry)
145
148
  @appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
149
+ @ai_guard = Datadog::AIGuard::Component.build(settings, logger: @logger, telemetry: telemetry)
146
150
  @open_feature = OpenFeature::Component.build(settings, agent_settings, logger: @logger, telemetry: telemetry)
147
151
  @dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
148
152
  @error_tracking = Datadog::ErrorTracking::Component.build(settings, @tracer, @logger)
@@ -209,6 +213,9 @@ module Datadog
209
213
  # Decommission AppSec
210
214
  appsec&.shutdown!
211
215
 
216
+ # Shutdown AIGuard component
217
+ ai_guard&.shutdown!
218
+
212
219
  # Shutdown the old tracer, unless it's still being used.
213
220
  # (e.g. a custom tracer instance passed in.)
214
221
  tracer.shutdown! unless replacement && tracer.equal?(replacement.tracer)
@@ -9,7 +9,7 @@ module Datadog
9
9
  class ConfigHelper
10
10
  def initialize(
11
11
  source_env: ENV,
12
- supported_configurations: SUPPORTED_CONFIGURATIONS,
12
+ supported_configurations: SUPPORTED_CONFIGURATION_NAMES,
13
13
  aliases: ALIASES,
14
14
  alias_to_canonical: ALIAS_TO_CANONICAL,
15
15
  raise_on_unknown_env_var: false
@@ -21,12 +21,12 @@ module Datadog
21
21
  end
22
22
 
23
23
  private_class_method def self.log_deprecated_environment_variables(logger, source_env, source_name, deprecations, alias_to_canonical)
24
- deprecations.each do |deprecated_env_var, message|
24
+ deprecations.each do |deprecated_env_var|
25
25
  next unless source_env.key?(deprecated_env_var)
26
26
 
27
27
  Datadog::Core.log_deprecation(disallowed_next_major: false, logger: logger) do
28
28
  "#{deprecated_env_var} #{source_name} variable is deprecated" +
29
- (alias_to_canonical[deprecated_env_var] ? ", use #{alias_to_canonical[deprecated_env_var]} instead." : ". #{message}.")
29
+ (alias_to_canonical[deprecated_env_var] ? ", use #{alias_to_canonical[deprecated_env_var]} instead." : ".")
30
30
  end
31
31
  end
32
32
  end
@@ -42,7 +42,8 @@ module Datadog
42
42
  # Acts as DSL for building OptionDefinitions
43
43
  # @public_api
44
44
  class Builder
45
- InvalidOptionError = Class.new(StandardError)
45
+ # Steep: https://github.com/soutaro/steep/issues/1880
46
+ InvalidOptionError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
46
47
 
47
48
  attr_reader \
48
49
  :helpers
@@ -119,7 +120,8 @@ module Datadog
119
120
  env_parser(&options[:env_parser]) if options.key?(:env_parser)
120
121
  after_set(&options[:after_set]) if options.key?(:after_set)
121
122
  resetter(&options[:resetter]) if options.key?(:resetter)
122
- setter(&options[:setter]) if options.key?(:setter)
123
+ # Steep: https://github.com/soutaro/steep/issues/1979
124
+ setter(&options[:setter]) if options.key?(:setter) # steep:ignore BlockTypeMismatch
123
125
  type(options[:type], **(options[:type_options] || {})) if options.key?(:type)
124
126
  end
125
127
 
@@ -40,14 +40,16 @@ module Datadog
40
40
 
41
41
  def default_helpers(name)
42
42
  option_name = name.to_sym
43
- # @type var opt_getter: Configuration::OptionDefinition::helper_proc
44
- opt_getter = proc do
43
+ # Steep: https://github.com/soutaro/steep/issues/335
44
+ # @type var opt_getter: Configuration::OptionDefinition::generic_proc
45
+ opt_getter = proc do # steep:ignore IncompatibleAssignment
45
46
  # These Procs uses `get/set_option`, but we only add them to the OptionDefinition helpers here.
46
47
  # Steep is right that these methods are not defined, but we only run these Procs in instance context.
47
48
  get_option(option_name) # steep:ignore NoMethod
48
49
  end
49
- # @type var opt_setter: Configuration::OptionDefinition::helper_proc
50
- opt_setter = proc do |value|
50
+ # Steep: https://github.com/soutaro/steep/issues/335
51
+ # @type var opt_setter: Configuration::OptionDefinition::generic_proc
52
+ opt_setter = proc do |value| # steep:ignore IncompatibleAssignment
51
53
  set_option(option_name, value) # steep:ignore NoMethod
52
54
  end
53
55
  {
@@ -127,7 +129,8 @@ module Datadog
127
129
  end
128
130
  end
129
131
 
130
- InvalidOptionError = Class.new(StandardError)
132
+ # Steep: https://github.com/soutaro/steep/issues/1880
133
+ InvalidOptionError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
131
134
  end
132
135
  end
133
136
  end
@@ -170,6 +170,20 @@ module Datadog
170
170
  o.env Core::Environment::Ext::ENV_ENVIRONMENT
171
171
  end
172
172
 
173
+ # Configuration for container environments. For internal use only.
174
+ # @!visibility private
175
+ settings :container do
176
+ # Data supplied by the container runner to assist in uniquely identifying this process.
177
+ # Used in [Origin Detection](https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host#origin-detection)
178
+ #
179
+ # @default `DD_EXTERNAL_ENV` environment variable, otherwise `nil`
180
+ # @return [String,nil]
181
+ option :external_env do |o|
182
+ o.type :string, nilable: true
183
+ o.env Core::Environment::Ext::ENV_EXTERNAL_ENV
184
+ end
185
+ end
186
+
173
187
  # Internal {Datadog::Statsd} metrics collection.
174
188
  #
175
189
  # @public_api
@@ -293,9 +307,6 @@ module Datadog
293
307
  # for Ruby versions 2.x, 3.1.4+, 3.2.3+ and 3.3.0+
294
308
  # (more details in {Datadog::Profiling::Component.enable_gc_profiling?})
295
309
  #
296
- # @warn Due to a VM bug in the Ractor implementation (https://bugs.ruby-lang.org/issues/19112) this feature
297
- # stops working when Ractors get garbage collected.
298
- #
299
310
  # @default `DD_PROFILING_GC_ENABLED` environment variable, otherwise `true`
300
311
  option :gc_enabled do |o|
301
312
  o.type :bool
@@ -425,6 +436,23 @@ module Datadog
425
436
  o.default true
426
437
  end
427
438
 
439
+ # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
440
+ #
441
+ # When using `Kernel#exec` on Linux, it can happen that a signal sent before calling `exec` arrives after
442
+ # the new process is running, causing it to fail with the `Profiling timer expired` error message.
443
+ # To avoid this, the profiler installs a monkey patch on `Kernel#exec` to stop profiling before actually
444
+ # calling `exec`.
445
+ # This monkey patch is available for Ruby 2.7+; let us know if you need it on earlier Rubies.
446
+ # For more details see https://github.com/DataDog/dd-trace-rb/issues/5101 .
447
+ #
448
+ # @default `DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED` environment variable as a boolean,
449
+ # otherwise `true`
450
+ option :shutdown_on_exec_enabled do |o|
451
+ o.env 'DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED'
452
+ o.type :bool
453
+ o.default true
454
+ end
455
+
428
456
  # Configures how much wall-time overhead the profiler targets. The profiler will dynamically adjust the
429
457
  # interval between samples it takes so as to try and maintain the property that it spends no longer than
430
458
  # this amount of wall-clock time profiling. For example, with the default value of 2%, the profiler will
@@ -8,8 +8,13 @@ require 'set'
8
8
  module Datadog
9
9
  module Core
10
10
  module Configuration
11
- SUPPORTED_CONFIGURATIONS =
11
+ SUPPORTED_CONFIGURATION_NAMES =
12
12
  Set["DD_AGENT_HOST",
13
+ "DD_AI_GUARD_ENABLED",
14
+ "DD_AI_GUARD_ENDPOINT",
15
+ "DD_AI_GUARD_MAX_CONTENT_SIZE",
16
+ "DD_AI_GUARD_MAX_MESSAGES_LENGTH",
17
+ "DD_AI_GUARD_TIMEOUT",
13
18
  "DD_API_KEY",
14
19
  "DD_API_SECURITY_ENABLED",
15
20
  "DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED",
@@ -34,6 +39,7 @@ module Datadog
34
39
  "DD_APPSEC_TRACE_RATE_LIMIT",
35
40
  "DD_APPSEC_WAF_DEBUG",
36
41
  "DD_APPSEC_WAF_TIMEOUT",
42
+ "DD_APP_KEY",
37
43
  "DD_CRASHTRACKING_ENABLED",
38
44
  "DD_DATA_STREAMS_ENABLED",
39
45
  "DD_DBM_PROPAGATION_MODE",
@@ -42,11 +48,13 @@ module Datadog
42
48
  "DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE",
43
49
  "DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS",
44
50
  "DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES",
51
+ "DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS",
45
52
  "DD_ENV",
46
53
  "DD_ERROR_TRACKING_HANDLED_ERRORS",
47
54
  "DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE",
48
55
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
49
56
  "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
57
+ "DD_EXTERNAL_ENV",
50
58
  "DD_GIT_COMMIT_SHA",
51
59
  "DD_GIT_REPOSITORY_URL",
52
60
  "DD_HEALTH_METRICS_ENABLED",
@@ -74,6 +82,7 @@ module Datadog
74
82
  "DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED",
75
83
  "DD_PROFILING_OVERHEAD_TARGET_PERCENTAGE",
76
84
  "DD_PROFILING_PREVIEW_OTEL_CONTEXT_ENABLED",
85
+ "DD_PROFILING_SHUTDOWN_ON_EXEC_ENABLED",
77
86
  "DD_PROFILING_SIGHANDLER_SAMPLING_ENABLED",
78
87
  "DD_PROFILING_SKIP_MYSQL2_CHECK",
79
88
  "DD_PROFILING_TIMELINE_ENABLED",
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../tag_builder'
4
4
  require_relative '../utils'
5
+ require_relative '../environment/process'
5
6
 
6
7
  module Datadog
7
8
  module Core
@@ -13,6 +14,11 @@ module Datadog
13
14
  'is_crash' => 'true',
14
15
  )
15
16
 
17
+ if settings.experimental_propagate_process_tags_enabled
18
+ process_tags = Environment::Process.serialized
19
+ hash['process_tags'] = process_tags unless process_tags.empty?
20
+ end
21
+
16
22
  Utils.encode_tags(hash)
17
23
  end
18
24
  end
@@ -10,40 +10,67 @@ module Datadog
10
10
  # about the current Linux container identity.
11
11
  # @see https://man7.org/linux/man-pages/man7/cgroups.7.html
12
12
  module Cgroup
13
- LINE_REGEX = /^(\d+):([^:]*):(.+)$/.freeze
14
-
15
- Descriptor = Struct.new(
16
- :id,
17
- :groups,
13
+ # A parsed cgroup entry from /proc/<pid>/cgroup
14
+ Entry = Struct.new(
15
+ :hierarchy,
16
+ :controllers,
18
17
  :path,
19
- :controllers
18
+ :inode
20
19
  )
21
20
 
22
21
  module_function
23
22
 
24
- def descriptors(process = 'self')
25
- [].tap do |descriptors|
26
- filepath = "/proc/#{process}/cgroup"
27
-
28
- if File.exist?(filepath)
29
- File.foreach("/proc/#{process}/cgroup") do |line|
30
- line = line.strip
31
- descriptors << parse(line) unless line.empty?
32
- end
33
- end
34
- rescue => e
35
- Datadog.logger.error(
36
- "Error while parsing cgroup. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
37
- )
23
+ # Parses the /proc/self/cgroup file,
24
+ # @return [Array<Entry>] one entry for each valid cgroup line
25
+ def entries
26
+ filepath = '/proc/self/cgroup'
27
+ return [] unless File.exist?(filepath)
28
+
29
+ ret = []
30
+ File.foreach(filepath) do |entry_line|
31
+ ret << parse(entry_line) unless entry_line.empty?
38
32
  end
33
+ ret
39
34
  end
40
35
 
41
- def parse(line)
42
- id, groups, path = line.scan(LINE_REGEX).first
36
+ # Parses a single cgroup entry from /proc/<pid>/cgroup.
37
+ #
38
+ # Files can have cgroup v1 and v2 entries mixed. Their format is the same.
39
+ #
40
+ # Each entry has 3 colon-separated fields:
41
+ # hierarchy-ID:controllers:path
42
+ # Examples:
43
+ # 10:memory:/docker/1234567890abcdef (cgroup v1)
44
+ # 0::/docker/1234567890abcdef (cgroup v2)
45
+ #
46
+ # @see https://man7.org/linux/man-pages/man7/cgroups.7.html#:~:text=%2Fproc%2Fpid%2Fcgroup
47
+ # @return [Entry]
48
+ def parse(entry_line)
49
+ hierarchy, controllers, path = entry_line.split(':', 3)
43
50
 
44
- Descriptor.new(id, groups, path).tap do |descriptor|
45
- descriptor.controllers = groups.split(',') unless groups.nil?
46
- end
51
+ Entry.new(
52
+ hierarchy,
53
+ controllers,
54
+ path,
55
+ inode_for(controllers, path)
56
+ )
57
+ end
58
+
59
+ # We can find the container inode by running a file stat on the cgroup filesystem path.
60
+ # Example:
61
+ # For the entry `0:cpu:/docker/abc123`,
62
+ # we read `stat -c '%i' /sys/fs/cgroup/cpu/docker/abc123`
63
+ def inode_for(controllers, path)
64
+ return if controllers.nil? || path.nil?
65
+
66
+ # In cgroup v1, when multiple controllers are co-mounted, the controllers
67
+ # becomes part of the directory name (with commas preserved).
68
+ # Example entry:
69
+ # For the line "10:cpu,cpuacct:/docker/abc123", the path is
70
+ # "/sys/fs/cgroup/cpu,cpuacct/docker/abc123"
71
+ inode_path = File.join('/sys/fs/cgroup', controllers, path)
72
+
73
+ File.stat(inode_path).ino if File.exist?(inode_path)
47
74
  end
48
75
  end
49
76
  end