datadog 2.12.2 → 2.15.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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -2
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -14
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +3 -0
  5. data/ext/datadog_profiling_native_extension/encoded_profile.c +69 -0
  6. data/ext/datadog_profiling_native_extension/encoded_profile.h +7 -0
  7. data/ext/datadog_profiling_native_extension/http_transport.c +25 -32
  8. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  9. data/ext/datadog_profiling_native_extension/stack_recorder.c +22 -21
  10. data/ext/libdatadog_api/datadog_ruby_common.h +3 -0
  11. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  12. data/lib/datadog/appsec/actions_handler.rb +22 -1
  13. data/lib/datadog/appsec/anonymizer.rb +16 -0
  14. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  15. data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
  16. data/lib/datadog/appsec/assets/waf_rules/recommended.json +0 -1344
  17. data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
  18. data/lib/datadog/appsec/assets/waf_rules/strict.json +0 -1344
  19. data/lib/datadog/appsec/component.rb +19 -17
  20. data/lib/datadog/appsec/compressed_json.rb +40 -0
  21. data/lib/datadog/appsec/configuration/settings.rb +62 -10
  22. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  23. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  24. data/lib/datadog/appsec/contrib/devise/configuration.rb +7 -31
  25. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +79 -0
  26. data/lib/datadog/appsec/contrib/devise/ext.rb +21 -0
  27. data/lib/datadog/appsec/contrib/devise/integration.rb +0 -1
  28. data/lib/datadog/appsec/contrib/devise/patcher.rb +36 -23
  29. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
  30. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
  31. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +2 -2
  32. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +93 -0
  33. data/lib/datadog/appsec/contrib/rack/ext.rb +14 -0
  34. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -3
  35. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -2
  36. data/lib/datadog/appsec/event.rb +22 -51
  37. data/lib/datadog/appsec/ext.rb +4 -2
  38. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +4 -2
  39. data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -3
  40. data/lib/datadog/appsec/remote.rb +4 -0
  41. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  42. data/lib/datadog/appsec/utils.rb +0 -2
  43. data/lib/datadog/core/configuration/components.rb +2 -1
  44. data/lib/datadog/core/configuration/ext.rb +4 -0
  45. data/lib/datadog/core/configuration/options.rb +2 -2
  46. data/lib/datadog/core/configuration/settings.rb +53 -30
  47. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  48. data/lib/datadog/core/environment/agent_info.rb +4 -3
  49. data/lib/datadog/core/metrics/client.rb +1 -1
  50. data/lib/datadog/core/remote/client.rb +1 -1
  51. data/lib/datadog/core/remote/component.rb +3 -6
  52. data/lib/datadog/core/remote/configuration/repository.rb +2 -1
  53. data/lib/datadog/core/remote/negotiation.rb +9 -9
  54. data/lib/datadog/core/remote/transport/config.rb +4 -3
  55. data/lib/datadog/core/remote/transport/http/client.rb +4 -3
  56. data/lib/datadog/core/remote/transport/http/config.rb +6 -32
  57. data/lib/datadog/core/remote/transport/http/negotiation.rb +6 -32
  58. data/lib/datadog/core/remote/transport/http.rb +22 -57
  59. data/lib/datadog/core/remote/transport/negotiation.rb +4 -3
  60. data/lib/datadog/core/runtime/metrics.rb +8 -1
  61. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  62. data/lib/datadog/core/telemetry/metric.rb +5 -5
  63. data/lib/datadog/core/telemetry/request.rb +1 -1
  64. data/lib/datadog/core/transport/http/api/instance.rb +17 -0
  65. data/lib/datadog/core/transport/http/api/spec.rb +17 -0
  66. data/lib/datadog/core/transport/http/builder.rb +5 -3
  67. data/lib/datadog/core/transport/http.rb +39 -2
  68. data/lib/datadog/di/component.rb +0 -2
  69. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  70. data/lib/datadog/di/probe_notifier_worker.rb +16 -16
  71. data/lib/datadog/di/transport/diagnostics.rb +4 -3
  72. data/lib/datadog/di/transport/http/api.rb +2 -12
  73. data/lib/datadog/di/transport/http/client.rb +4 -3
  74. data/lib/datadog/di/transport/http/diagnostics.rb +7 -34
  75. data/lib/datadog/di/transport/http/input.rb +7 -34
  76. data/lib/datadog/di/transport/http.rb +14 -62
  77. data/lib/datadog/di/transport/input.rb +4 -3
  78. data/lib/datadog/di/utils.rb +5 -0
  79. data/lib/datadog/kit/appsec/events.rb +12 -0
  80. data/lib/datadog/kit/identity.rb +5 -1
  81. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  82. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  83. data/lib/datadog/opentelemetry/api/context.rb +16 -2
  84. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  85. data/lib/datadog/opentelemetry.rb +2 -1
  86. data/lib/datadog/profiling/collectors/info.rb +3 -0
  87. data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
  88. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  89. data/lib/datadog/profiling/exporter.rb +2 -3
  90. data/lib/datadog/profiling/ext.rb +0 -1
  91. data/lib/datadog/profiling/flush.rb +4 -7
  92. data/lib/datadog/profiling/http_transport.rb +10 -59
  93. data/lib/datadog/profiling/stack_recorder.rb +4 -4
  94. data/lib/datadog/profiling.rb +6 -2
  95. data/lib/datadog/tracing/component.rb +15 -12
  96. data/lib/datadog/tracing/configuration/ext.rb +7 -1
  97. data/lib/datadog/tracing/configuration/settings.rb +18 -2
  98. data/lib/datadog/tracing/context_provider.rb +1 -1
  99. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  100. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  101. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  102. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
  103. data/lib/datadog/tracing/contrib/ext.rb +1 -0
  104. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
  105. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
  106. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
  107. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
  108. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
  109. data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -5
  110. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -11
  111. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +6 -10
  112. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  113. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +46 -0
  114. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  115. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  116. data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
  117. data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
  118. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  119. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  120. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  121. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  122. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  123. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
  124. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
  125. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
  126. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  127. data/lib/datadog/tracing/contrib.rb +1 -0
  128. data/lib/datadog/tracing/correlation.rb +9 -2
  129. data/lib/datadog/tracing/distributed/baggage.rb +131 -0
  130. data/lib/datadog/tracing/distributed/datadog.rb +2 -0
  131. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  132. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  133. data/lib/datadog/tracing/metadata/ext.rb +5 -0
  134. data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
  135. data/lib/datadog/tracing/span_event.rb +1 -1
  136. data/lib/datadog/tracing/span_operation.rb +2 -1
  137. data/lib/datadog/tracing/sync_writer.rb +1 -2
  138. data/lib/datadog/tracing/trace_digest.rb +9 -2
  139. data/lib/datadog/tracing/trace_operation.rb +29 -17
  140. data/lib/datadog/tracing/trace_segment.rb +6 -4
  141. data/lib/datadog/tracing/tracer.rb +38 -2
  142. data/lib/datadog/tracing/transport/http/api.rb +2 -10
  143. data/lib/datadog/tracing/transport/http/client.rb +5 -4
  144. data/lib/datadog/tracing/transport/http/traces.rb +13 -41
  145. data/lib/datadog/tracing/transport/http.rb +11 -44
  146. data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
  147. data/lib/datadog/tracing/transport/traces.rb +26 -9
  148. data/lib/datadog/tracing/workers/trace_writer.rb +2 -6
  149. data/lib/datadog/tracing/writer.rb +2 -6
  150. data/lib/datadog/tracing.rb +16 -3
  151. data/lib/datadog/version.rb +2 -2
  152. data/lib/datadog.rb +1 -1
  153. metadata +28 -13
  154. data/lib/datadog/appsec/contrib/devise/event.rb +0 -54
  155. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -72
  156. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -47
  157. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  158. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  159. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+ require_relative '../../anonymizer'
5
+
6
+ module Datadog
7
+ module AppSec
8
+ module Contrib
9
+ module Devise
10
+ # A Rack middleware capable of tracking currently signed user
11
+ class TrackingMiddleware
12
+ WARDEN_KEY = 'warden'
13
+
14
+ def initialize(app)
15
+ @app = app
16
+ @devise_session_scope_keys = {}
17
+ end
18
+
19
+ def call(env)
20
+ return @app.call(env) unless AppSec.enabled?
21
+ return @app.call(env) unless Configuration.auto_user_instrumentation_enabled?
22
+ return @app.call(env) unless AppSec.active_context
23
+
24
+ unless env.key?(WARDEN_KEY)
25
+ Datadog.logger.debug { 'AppSec: unable to track requests, due to missing warden manager' }
26
+ return @app.call(env)
27
+ end
28
+
29
+ context = AppSec.active_context
30
+ if context.trace.nil? || context.span.nil?
31
+ Datadog.logger.debug { 'AppSec: unable to track requests, due to missing trace or span' }
32
+ return @app.call(env)
33
+ end
34
+
35
+ id = transform(extract_id(env[WARDEN_KEY]))
36
+ if id
37
+ unless context.span.has_tag?(Ext::TAG_USR_ID)
38
+ context.span[Ext::TAG_USR_ID] = id
39
+ AppSec::Instrumentation.gateway.push(
40
+ 'identity.set_user', AppSec::Instrumentation::Gateway::User.new(id, nil)
41
+ )
42
+ end
43
+
44
+ context.span[Ext::TAG_DD_USR_ID] = id.to_s
45
+ context.span[Ext::TAG_DD_COLLECTION_MODE] ||= Configuration.auto_user_instrumentation_mode
46
+ end
47
+
48
+ @app.call(env)
49
+ end
50
+
51
+ private
52
+
53
+ def extract_id(warden)
54
+ session_serializer = warden.session_serializer
55
+
56
+ key = session_key_for(session_serializer, ::Devise.default_scope)
57
+ id = session_serializer.session[key]&.dig(0, 0)
58
+
59
+ return id if ::Devise.mappings.size == 1
60
+ return "#{::Devise.default_scope}:#{id}" if id
61
+
62
+ ::Devise.mappings.each_key do |scope|
63
+ next if scope == ::Devise.default_scope
64
+
65
+ key = session_key_for(session_serializer, scope)
66
+ id = session_serializer.session[key]&.dig(0, 0)
67
+
68
+ return "#{scope}:#{id}" if id
69
+ end
70
+
71
+ nil
72
+ end
73
+
74
+ def session_key_for(session_serializer, scope)
75
+ @devise_session_scope_keys[scope] ||= session_serializer.key_for(scope)
76
+ end
77
+
78
+ def transform(value)
79
+ return if value.nil?
80
+ return value.to_s unless anonymize?
81
+
82
+ Anonymizer.anonimyze(value.to_s)
83
+ end
84
+
85
+ def anonymize?
86
+ Configuration.auto_user_instrumentation_mode ==
87
+ AppSec::Configuration::Settings::ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -6,6 +6,20 @@ module Datadog
6
6
  module Rack
7
7
  # Rack integration constants
8
8
  module Ext
9
+ COLLECTABLE_REQUEST_HEADERS = [
10
+ 'accept',
11
+ 'akamai-user-risk',
12
+ 'cf-ray',
13
+ 'cloudfront-viewer-ja3-fingerprint',
14
+ 'content-type',
15
+ 'user-agent',
16
+ 'x-amzn-trace-Id',
17
+ 'x-appgw-trace-id',
18
+ 'x-cloud-trace-context',
19
+ 'x-sigsci-requestid',
20
+ 'x-sigsci-tags'
21
+ ].freeze
22
+
9
23
  IDENTITY_COLLECTABLE_REQUEST_HEADERS = [
10
24
  'accept-encoding',
11
25
  'accept-language',
@@ -18,6 +18,7 @@ module Datadog
18
18
  watch_request(gateway)
19
19
  watch_response(gateway)
20
20
  watch_request_body(gateway)
21
+ watch_request_finish(gateway)
21
22
  end
22
23
 
23
24
  def watch_request(gateway = Instrumentation.gateway)
@@ -119,12 +120,18 @@ module Datadog
119
120
  def watch_request_finish(gateway = Instrumentation.gateway)
120
121
  gateway.watch('rack.request.finish', :appsec) do |stack, gateway_request|
121
122
  context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
122
- next stack.call(gateway_request.request) if context.span.nil? || !gateway.pushed?('identity.set_user')
123
+
124
+ if context.span.nil? || !gateway.pushed?('appsec.events.user_lifecycle')
125
+ next stack.call(gateway_request.request)
126
+ end
123
127
 
124
128
  gateway_request.headers.each do |name, value|
125
- next unless Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
129
+ if !Ext::COLLECTABLE_REQUEST_HEADERS.include?(name) &&
130
+ !Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
131
+ next
132
+ end
126
133
 
127
- context.span["http.request.headers.#{name}"] = value
134
+ context.span["http.request.headers.#{name}"] ||= value
128
135
  end
129
136
 
130
137
  stack.call(gateway_request.request)
@@ -150,8 +150,6 @@ module Datadog
150
150
  return unless trace && span
151
151
 
152
152
  span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
153
- # We add this tag when ASM standalone is enabled to make sure we don't bill APM
154
- span.set_metric(Datadog::AppSec::Ext::TAG_APM_ENABLED, 0) if Datadog.configuration.appsec.standalone.enabled
155
153
  span.set_tag('_dd.runtime_family', 'ruby')
156
154
  span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
157
155
 
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'zlib'
5
-
6
4
  require_relative 'rate_limiter'
7
- require_relative '../core/utils/base64'
5
+ require_relative 'compressed_json'
8
6
 
9
7
  module Datadog
10
8
  module AppSec
11
9
  # AppSec event
12
10
  module Event
11
+ DERIVATIVE_SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
12
+ DERIVATIVE_SCHEMA_MAX_COMPRESSED_SIZE = 25000
13
13
  ALLOWED_REQUEST_HEADERS = %w[
14
14
  X-Forwarded-For
15
15
  X-Client-IP
@@ -38,11 +38,6 @@ module Datadog
38
38
  Content-Language
39
39
  ].map!(&:downcase).freeze
40
40
 
41
- MAX_ENCODED_SCHEMA_SIZE = 25000
42
- # For more information about this number
43
- # please check https://github.com/DataDog/dd-trace-rb/pull/3177#issuecomment-1747221082
44
- MIN_SCHEMA_SIZE_FOR_COMPRESSION = 260
45
-
46
41
  # Record events for a trace
47
42
  #
48
43
  # This is expected to be called only once per trace for the rate limiter
@@ -80,7 +75,6 @@ module Datadog
80
75
  end
81
76
  end
82
77
 
83
- # rubocop:disable Metrics/MethodLength
84
78
  def build_service_entry_tags(event_group)
85
79
  waf_events = []
86
80
  entry_tags = event_group.each_with_object({ '_dd.origin' => 'appsec' }) do |event, tags|
@@ -106,26 +100,17 @@ module Datadog
106
100
  waf_events += waf_result.events
107
101
 
108
102
  waf_result.derivatives.each do |key, value|
109
- parsed_value = json_parse(value)
110
- next unless parsed_value
111
-
112
- parsed_value_size = parsed_value.size
113
-
114
- schema_value = if parsed_value_size >= MIN_SCHEMA_SIZE_FOR_COMPRESSION
115
- compressed_and_base64_encoded(parsed_value)
116
- else
117
- parsed_value
118
- end
119
- next unless schema_value
120
-
121
- if schema_value.size >= MAX_ENCODED_SCHEMA_SIZE
122
- Datadog.logger.debug do
123
- "Schema key: #{key} exceeds the max size value. It will not be included as part of the span tags"
124
- end
103
+ next tags[key] = value unless key.start_with?(DERIVATIVE_SCHEMA_KEY_PREFIX)
104
+
105
+ value = CompressedJson.dump(value)
106
+ next if value.nil?
107
+
108
+ if value.size >= DERIVATIVE_SCHEMA_MAX_COMPRESSED_SIZE
109
+ Datadog.logger.debug { "AppSec: Schema key '#{key}' will not be included into span tags due to it's size" }
125
110
  next
126
111
  end
127
112
 
128
- tags[key] = schema_value
113
+ tags[key] = value
129
114
  end
130
115
 
131
116
  tags
@@ -135,14 +120,16 @@ module Datadog
135
120
  entry_tags['_dd.appsec.json'] = appsec_events if appsec_events
136
121
  entry_tags
137
122
  end
138
- # rubocop:enable Metrics/MethodLength
139
123
 
140
124
  def tag_and_keep!(context, waf_result)
141
125
  # We want to keep the trace in case of security event
142
126
  context.trace.keep! if context.trace
143
127
 
144
128
  if context.span
145
- context.span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
129
+ if waf_result.actions.key?('block_request') || waf_result.actions.key?('redirect_request')
130
+ context.span.set_tag('appsec.blocked', 'true')
131
+ end
132
+
146
133
  context.span.set_tag('appsec.event', 'true')
147
134
  end
148
135
 
@@ -151,31 +138,15 @@ module Datadog
151
138
 
152
139
  private
153
140
 
154
- def compressed_and_base64_encoded(value)
155
- Datadog::Core::Utils::Base64.strict_encode64(gzip(value))
156
- rescue TypeError => e
157
- Datadog.logger.debug do
158
- "Failed to compress and encode value when populating AppSec::Event. Error: #{e.message}"
159
- end
160
- nil
161
- end
162
-
141
+ # NOTE: Handling of Encoding::UndefinedConversionError is added as a quick fix to
142
+ # the issue between Ruby encoded strings and libddwaf produced events and now
143
+ # is under investigation.
163
144
  def json_parse(value)
164
145
  JSON.dump(value)
165
- rescue ArgumentError => e
166
- Datadog.logger.debug do
167
- "Failed to parse value to JSON when populating AppSec::Event. Error: #{e.message}"
168
- end
169
- nil
170
- end
146
+ rescue ArgumentError, Encoding::UndefinedConversionError, JSON::JSONError => e
147
+ AppSec.telemetry.report(e, description: 'AppSec: Failed to convert value into JSON')
171
148
 
172
- def gzip(value)
173
- sio = StringIO.new
174
- # For an in depth comparison of Zlib options check https://github.com/DataDog/dd-trace-rb/pull/3177#issuecomment-1747215473
175
- gz = Zlib::GzipWriter.new(sio, Zlib::BEST_SPEED, Zlib::DEFAULT_STRATEGY)
176
- gz.write(value)
177
- gz.close
178
- sio.string
149
+ nil
179
150
  end
180
151
 
181
152
  # Propagate to downstream services the information that the current distributed trace is
@@ -187,7 +158,7 @@ module Datadog
187
158
  Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
188
159
  Datadog::Tracing::Sampling::Ext::Decision::ASM
189
160
  )
190
- trace.set_tag(Datadog::AppSec::Ext::TAG_DISTRIBUTED_APPSEC_EVENT, '1')
161
+ trace.set_distributed_source(Datadog::AppSec::Ext::PRODUCT_BIT)
191
162
  end
192
163
  end
193
164
  end
@@ -7,13 +7,15 @@ module Datadog
7
7
  RASP_LFI = 'lfi'
8
8
  RASP_SSRF = 'ssrf'
9
9
 
10
+ PRODUCT_BIT = 0b00000010
11
+
10
12
  INTERRUPT = :datadog_appsec_interrupt
11
13
  CONTEXT_KEY = 'datadog.appsec.context'
12
14
  ACTIVE_CONTEXT_KEY = :datadog_appsec_active_context
15
+ EXPLOIT_PREVENTION_EVENT_CATEGORY = 'exploit'
13
16
 
14
17
  TAG_APPSEC_ENABLED = '_dd.appsec.enabled'
15
- TAG_APM_ENABLED = '_dd.apm.enabled'
16
- TAG_DISTRIBUTED_APPSEC_EVENT = '_dd.p.appsec'
18
+ TAG_METASTRUCT_STACK_TRACE = '_dd.stack'
17
19
 
18
20
  TELEMETRY_METRICS_NAMESPACE = 'appsec'
19
21
  end
@@ -9,11 +9,13 @@ module Datadog
9
9
 
10
10
  # Gateway User argument
11
11
  class User < Argument
12
- attr_reader :id
12
+ attr_reader :id, :login
13
13
 
14
- def initialize(id)
14
+ def initialize(id, login)
15
15
  super()
16
+
16
17
  @id = id
18
+ @login = login
17
19
  end
18
20
  end
19
21
  end
@@ -19,9 +19,14 @@ module Datadog
19
19
  gateway.watch('identity.set_user', :appsec) do |stack, user|
20
20
  context = Datadog::AppSec.active_context
21
21
 
22
- persistent_data = {
23
- 'usr.id' => user.id
24
- }
22
+ if user.id.nil? && user.login.nil?
23
+ Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
24
+ next stack.call(user)
25
+ end
26
+
27
+ persistent_data = {}
28
+ persistent_data['usr.id'] = user.id if user.id
29
+ persistent_data['usr.login'] = user.login if user.login
25
30
 
26
31
  result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
27
32
 
@@ -23,6 +23,8 @@ module Datadog
23
23
  CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
24
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
25
  CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
26
+ CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
27
+ CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
26
28
 
27
29
  # TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
28
30
  ASM_CAPABILITIES = [
@@ -35,6 +37,8 @@ module Datadog
35
37
  CAP_ASM_CUSTOM_RULES,
36
38
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE,
37
39
  CAP_ASM_TRUSTED_IPS,
40
+ CAP_ASM_RASP_SSRF,
41
+ CAP_ASM_RASP_SQLI,
38
42
  ].freeze
39
43
 
40
44
  ASM_PRODUCTS = [
@@ -24,13 +24,13 @@ module Datadog
24
24
  persistent_data.reject! do |_, v|
25
25
  next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
26
26
 
27
- v.nil? ? true : v.empty?
27
+ v.nil? || v.empty?
28
28
  end
29
29
 
30
30
  ephemeral_data.reject! do |_, v|
31
31
  next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
32
32
 
33
- v.nil? ? true : v.empty?
33
+ v.nil? || v.empty?
34
34
  end
35
35
 
36
36
  _code, result = try_run(persistent_data, ephemeral_data, timeout)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'utils/trace_operation'
4
-
5
3
  module Datadog
6
4
  module AppSec
7
5
  # Utilities for AppSec
@@ -45,6 +45,7 @@ module Datadog
45
45
  options = { enabled: settings.runtime_metrics.enabled }
46
46
  options[:statsd] = settings.runtime_metrics.statsd unless settings.runtime_metrics.statsd.nil?
47
47
  options[:services] = [settings.service] unless settings.service.nil?
48
+ options[:experimental_runtime_id_enabled] = settings.runtime_metrics.experimental_runtime_id_enabled
48
49
 
49
50
  Core::Runtime::Metrics.new(logger: logger, **options)
50
51
  end
@@ -101,7 +102,7 @@ module Datadog
101
102
  agent_settings = AgentSettingsResolver.call(settings, logger: @logger)
102
103
 
103
104
  # Exposes agent capability information for detection by any components
104
- @agent_info = Core::Environment::AgentInfo.new(agent_settings)
105
+ @agent_info = Core::Environment::AgentInfo.new(agent_settings, logger: @logger)
105
106
 
106
107
  @telemetry = self.class.build_telemetry(settings, agent_settings, @logger)
107
108
 
@@ -18,6 +18,10 @@ module Datadog
18
18
  ENV_DEFAULT_PORT = 'DD_METRIC_AGENT_PORT'
19
19
  end
20
20
 
21
+ module APM
22
+ ENV_TRACING_ENABLED = 'DD_APM_TRACING_ENABLED'
23
+ end
24
+
21
25
  module Agent
22
26
  ENV_DEFAULT_HOST = 'DD_AGENT_HOST'
23
27
  # Some env vars have "trace" in them, but they apply to all products
@@ -45,7 +45,7 @@ module Datadog
45
45
  option_name.to_sym => proc do
46
46
  get_option(option_name)
47
47
  end,
48
- "#{option_name}=".to_sym => proc do |value|
48
+ :"#{option_name}=" => proc do |value|
49
49
  set_option(option_name, value)
50
50
  end
51
51
  }
@@ -117,7 +117,7 @@ module Datadog
117
117
  end
118
118
 
119
119
  def resolved_env(name)
120
- return options[name].resolved_env if options.key?(name)
120
+ options[name].resolved_env if options.key?(name)
121
121
  end
122
122
 
123
123
  def assert_valid_option!(name)
@@ -570,6 +570,12 @@ module Datadog
570
570
  o.type :bool
571
571
  end
572
572
 
573
+ option :experimental_runtime_id_enabled do |o|
574
+ o.type :bool
575
+ o.env 'DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED'
576
+ o.default false
577
+ end
578
+
573
579
  option :opts, default: {}, type: :hash
574
580
  option :statsd
575
581
  end
@@ -619,38 +625,31 @@ module Datadog
619
625
  o.type :hash, nilable: true
620
626
  o.env [Core::Environment::Ext::ENV_TAGS, Core::Environment::Ext::ENV_OTEL_RESOURCE_ATTRIBUTES]
621
627
  o.env_parser do |env_value|
622
- values = if env_value.include?(',')
623
- env_value.split(',')
624
- else
625
- env_value.split(' ') # rubocop:disable Style/RedundantArgument
626
- end
627
- values.map! do |v|
628
- v.gsub!(/\A[\s,]*|[\s,]*\Z/, '')
629
-
630
- v.empty? ? nil : v
631
- end
632
-
633
- values.compact!
634
- values.each_with_object({}) do |tag, tags|
635
- key, value = tag.split(':', 2)
636
- if value.nil?
637
- # support tags/attributes delimited by the OpenTelemetry separator (`=`)
638
- key, value = tag.split('=', 2)
639
- end
640
- next if value.nil? || value.empty?
641
-
642
- # maps OpenTelemetry semantic attributes to Datadog tags
643
- case key.downcase
644
- when 'deployment.environment'
645
- tags['env'] = value
646
- when 'service.version'
647
- tags['version'] = value
648
- when 'service.name'
649
- tags['service'] = value
650
- else
651
- tags[key] = value
628
+ # Parses a string containing key-value pairs and returns a hash.
629
+ # Key-value pairs are delimited by ':' OR `=`, and pairs are separated by whitespace, comma, OR BOTH.
630
+ result = {}
631
+ unless env_value.nil? || env_value.empty?
632
+ # falling back to comma as separator
633
+ sep = env_value.include?(',') ? ',' : ' '
634
+ # split by separator
635
+ env_value.split(sep).each do |tag|
636
+ tag.strip!
637
+ next if tag.empty?
638
+
639
+ # tag by : or = (for OpenTelemetry)
640
+ key, val = tag.split(/[:=]/, 2).map(&:strip)
641
+ val ||= ''
642
+ # maps OpenTelemetry semantic attributes to Datadog tags
643
+ key = case key.downcase
644
+ when 'deployment.environment' then 'env'
645
+ when 'service.version' then 'version'
646
+ when 'service.name' then 'service'
647
+ else key
648
+ end
649
+ result[key] = val unless key.empty?
652
650
  end
653
651
  end
652
+ result
654
653
  end
655
654
  o.setter do |new_value, old_value|
656
655
  raw_tags = new_value || {}
@@ -958,6 +957,30 @@ module Datadog
958
957
  end
959
958
  end
960
959
 
960
+ # Tracer specific configuration starting with APM (e.g. DD_APM_TRACING_ENABLED).
961
+ # @public_api
962
+ settings :apm do
963
+ # Tracing as a transport
964
+ # @public_api
965
+ settings :tracing do
966
+ # Enables tracing as transport.
967
+ # Disabling it will set sampling priority to -1 (FORCE_DROP) on most traces,
968
+ # (which tells to the agent to drop these traces)
969
+ # except heartbeat ones (1 per minute) and manually kept ones (sampling priority to 2) (e.g. appsec events)
970
+ #
971
+ # This is different than `DD_TRACE_ENABLED`, which completely disables tracing (sends no trace at all),
972
+ # while this will send heartbeat traces (1 per minute) so that the service is considered alive in the backend.
973
+ #
974
+ # @default `DD_APM_TRACING_ENABLED` environment variable, otherwise `true`
975
+ # @return [Boolean]
976
+ option :enabled do |o|
977
+ o.env Configuration::Ext::APM::ENV_TRACING_ENABLED
978
+ o.default true
979
+ o.type :bool
980
+ end
981
+ end
982
+ end
983
+
961
984
  # TODO: Tracing should manage its own settings.
962
985
  # Keep this extension here for now to keep things working.
963
986
  extend Datadog::Tracing::Configuration::Settings
@@ -79,7 +79,7 @@ module Datadog
79
79
 
80
80
  # @return [String] current time in ISO8601 format
81
81
  def date
82
- Time.now.utc.iso8601
82
+ Core::Utils::Time.now.utc.iso8601
83
83
  end
84
84
 
85
85
  # Best portable guess of OS information.
@@ -51,11 +51,12 @@ module Datadog
51
51
  #
52
52
  # @see https://github.com/DataDog/datadog-agent/blob/f07df0a3c1fca0c83b5a15f553bd994091b0c8ac/pkg/trace/api/info.go#L20
53
53
  class AgentInfo
54
- attr_reader :agent_settings
54
+ attr_reader :agent_settings, :logger
55
55
 
56
- def initialize(agent_settings)
56
+ def initialize(agent_settings, logger: Datadog.logger)
57
57
  @agent_settings = agent_settings
58
- @client = Remote::Transport::HTTP.root(agent_settings: agent_settings)
58
+ @logger = logger
59
+ @client = Remote::Transport::HTTP.root(agent_settings: agent_settings, logger: logger)
59
60
  end
60
61
 
61
62
  # Fetches the information from the agent.
@@ -23,7 +23,7 @@ module Datadog
23
23
 
24
24
  attr_reader :statsd, :logger
25
25
 
26
- def initialize(logger:, statsd: nil, enabled: true, **_)
26
+ def initialize(logger: Datadog.logger, statsd: nil, enabled: true, **_)
27
27
  @logger = logger
28
28
  @statsd =
29
29
  if supported?
@@ -15,7 +15,7 @@ module Datadog
15
15
 
16
16
  attr_reader :transport, :repository, :id, :dispatcher, :logger
17
17
 
18
- def initialize(transport, capabilities, logger:, repository: Configuration::Repository.new)
18
+ def initialize(transport, capabilities, logger: Datadog.logger, repository: Configuration::Repository.new)
19
19
  @transport = transport
20
20
  @logger = logger
21
21
 
@@ -18,11 +18,8 @@ module Datadog
18
18
  def initialize(settings, capabilities, agent_settings, logger:)
19
19
  @logger = logger
20
20
 
21
- transport_options = {}
22
- transport_options[:agent_settings] = agent_settings if agent_settings
23
-
24
- negotiation = Negotiation.new(settings, agent_settings)
25
- transport_v7 = Datadog::Core::Remote::Transport::HTTP.v7(**transport_options) # steep:ignore
21
+ negotiation = Negotiation.new(settings, agent_settings, logger: logger)
22
+ transport_v7 = Datadog::Core::Remote::Transport::HTTP.v7(agent_settings: agent_settings, logger: logger)
26
23
 
27
24
  @barrier = Barrier.new(settings.remote.boot_timeout_seconds)
28
25
 
@@ -49,7 +46,7 @@ module Datadog
49
46
  # In case of unexpected errors, reset the negotiation object
50
47
  # given external conditions have changed and the negotiation
51
48
  # negotiation object stores error logging state that should be reset.
52
- negotiation = Negotiation.new(settings, agent_settings)
49
+ negotiation = Negotiation.new(settings, agent_settings, logger: logger)
53
50
 
54
51
  # Transient errors due to network or agent. Logged the error but not via telemetry
55
52
  logger.error do
@@ -272,7 +272,8 @@ module Datadog
272
272
 
273
273
  return deleted(path, previous) if previous && content.nil?
274
274
  return inserted(path, content) if content && previous.nil?
275
- return updated(path, content, previous) if content && previous
275
+
276
+ updated(path, content, previous) if content && previous
276
277
  end
277
278
 
278
279
  def deleted(path, previous)