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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +21 -12
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +9 -7
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -24
- data/ext/datadog_profiling_native_extension/heap_recorder.c +5 -6
- data/ext/datadog_profiling_native_extension/http_transport.c +51 -64
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
- data/ext/datadog_profiling_native_extension/profiling.c +3 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +24 -8
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.c +29 -43
- data/ext/libdatadog_api/crashtracker.c +5 -8
- data/ext/libdatadog_api/crashtracker_report_exception.c +34 -144
- data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
- data/ext/libdatadog_api/di.c +79 -0
- data/ext/libdatadog_api/extconf.rb +5 -20
- data/ext/libdatadog_api/init.c +5 -2
- data/ext/libdatadog_extconf_helpers.rb +57 -11
- data/lib/datadog/ai_guard/component.rb +2 -0
- data/lib/datadog/ai_guard/configuration/settings.rb +3 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
- data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
- data/lib/datadog/ai_guard/evaluation.rb +36 -7
- data/lib/datadog/ai_guard.rb +26 -8
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +11 -7
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +3 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +2 -0
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +25 -2
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +2 -2
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +2 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +3 -3
- data/lib/datadog/appsec/event.rb +1 -17
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
- data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -2
- data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
- data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
- data/lib/datadog/appsec.rb +5 -9
- data/lib/datadog/core/configuration/base.rb +17 -5
- data/lib/datadog/core/configuration/components.rb +21 -8
- data/lib/datadog/core/configuration/config_helper.rb +9 -0
- data/lib/datadog/core/configuration/option.rb +32 -6
- data/lib/datadog/core/configuration/option_definition.rb +38 -12
- data/lib/datadog/core/configuration/options.rb +41 -7
- data/lib/datadog/core/configuration/settings.rb +42 -3
- data/lib/datadog/core/configuration/supported_configurations.rb +17 -0
- data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
- data/lib/datadog/core/contrib/rails/utils.rb +7 -3
- data/lib/datadog/core/crashtracking/component.rb +7 -15
- data/lib/datadog/core/environment/container.rb +2 -2
- data/lib/datadog/core/environment/ext.rb +1 -0
- data/lib/datadog/core/environment/identity.rb +25 -3
- data/lib/datadog/core/environment/process.rb +12 -0
- data/lib/datadog/core/metrics/client.rb +5 -5
- data/lib/datadog/core/process_discovery.rb +5 -0
- data/lib/datadog/core/remote/component.rb +38 -21
- data/lib/datadog/core/runtime/metrics.rb +2 -3
- data/lib/datadog/core/telemetry/component.rb +3 -0
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
- data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
- data/lib/datadog/core/telemetry/event.rb +1 -7
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
- data/lib/datadog/core/telemetry/worker.rb +20 -0
- data/lib/datadog/core/utils/base64.rb +1 -1
- data/lib/datadog/core/utils/only_once.rb +1 -1
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/core/workers/interval_loop.rb +13 -6
- data/lib/datadog/core/workers/queue.rb +0 -4
- data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
- data/lib/datadog/core.rb +0 -1
- data/lib/datadog/data_streams/pathway_context.rb +1 -1
- data/lib/datadog/data_streams/processor.rb +1 -0
- data/lib/datadog/di/boot.rb +3 -4
- data/lib/datadog/di/component.rb +20 -4
- data/lib/datadog/di/instrumenter.rb +20 -10
- data/lib/datadog/di/probe_manager.rb +79 -62
- data/lib/datadog/di/probe_notification_builder.rb +148 -33
- data/lib/datadog/di/probe_notifier_worker.rb +52 -6
- data/lib/datadog/di/probe_repository.rb +198 -0
- data/lib/datadog/di/remote.rb +5 -6
- data/lib/datadog/di/serializer.rb +127 -9
- data/lib/datadog/di/transport/http.rb +12 -3
- data/lib/datadog/di/transport/input.rb +46 -8
- data/lib/datadog/di.rb +81 -0
- data/lib/datadog/kit/enable_core_dumps.rb +1 -1
- data/lib/datadog/open_feature/configuration.rb +2 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
- data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
- data/lib/datadog/open_feature/exposures/worker.rb +1 -1
- data/lib/datadog/open_feature/remote.rb +1 -1
- data/lib/datadog/open_feature/transport.rb +1 -1
- data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/component.rb +31 -1
- data/lib/datadog/profiling/http_transport.rb +5 -6
- data/lib/datadog/profiling/load_native_extension.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +15 -12
- data/lib/datadog/profiling/scheduler.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +2 -2
- data/lib/datadog/profiling/tasks/setup.rb +2 -2
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/buffer.rb +3 -3
- data/lib/datadog/tracing/component.rb +11 -0
- data/lib/datadog/tracing/configuration/settings.rb +2 -1
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/configurable.rb +18 -3
- data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +7 -7
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
- data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
- data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
- data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +1 -0
- data/lib/datadog/tracing/distributed/datadog.rb +4 -2
- data/lib/datadog/tracing/event.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/remote.rb +1 -1
- data/lib/datadog/tracing/sampling/ext.rb +2 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
- data/lib/datadog/tracing/sampling/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
- data/lib/datadog/tracing/span_operation.rb +1 -1
- data/lib/datadog/tracing/sync_writer.rb +0 -1
- data/lib/datadog/tracing/trace_operation.rb +50 -6
- data/lib/datadog/tracing/tracer.rb +25 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/writer.rb +0 -1
- data/lib/datadog/version.rb +1 -1
- metadata +15 -8
- 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'
|
|
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'
|
|
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'
|
|
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'
|
|
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?
|
|
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
|
|
125
|
-
|
|
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.
|
|
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)
|
|
@@ -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
|
|
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'
|
|
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'
|
|
50
|
+
gateway.watch('rails.response.body.json') do |stack, container|
|
|
51
51
|
context = container.context
|
|
52
52
|
|
|
53
53
|
persistent_data = {
|
|
@@ -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
|
|
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
|
|
151
|
+
Datadog.logger.error("#{error_message}, #{e.class}: #{e}")
|
|
152
152
|
AppSec.telemetry.report(e, description: error_message)
|
|
153
153
|
end
|
|
154
154
|
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'
|
|
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'
|
|
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'
|
|
80
|
+
gateway.watch('sinatra.response.body.json') do |stack, container|
|
|
81
81
|
context = container.context # : Context
|
|
82
82
|
|
|
83
83
|
persistent_data = {
|
data/lib/datadog/appsec/event.rb
CHANGED
|
@@ -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
|
|
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 :
|
|
10
|
+
attr_reader :block
|
|
11
11
|
|
|
12
|
-
def initialize(
|
|
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,
|
|
45
|
-
@middlewares[name] << Middleware.new(
|
|
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'
|
|
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'
|
|
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
|
|
|
@@ -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|
|
|
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)
|
data/lib/datadog/appsec.rb
CHANGED
|
@@ -11,25 +11,21 @@ module Datadog
|
|
|
11
11
|
module AppSec
|
|
12
12
|
class << self
|
|
13
13
|
def enabled?
|
|
14
|
-
|
|
14
|
+
!!components.appsec
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def rasp_enabled?
|
|
18
|
-
|
|
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.
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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(:@
|
|
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
|
-
|
|
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::
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 '#{
|
|
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
|
-
|
|
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 #{
|
|
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 `#{
|
|
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 `#{
|
|
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 #{
|
|
304
|
+
"The option #{name_with_settings_path} is using an unsupported type option `#{@definition.type}`"
|
|
279
305
|
end
|
|
280
306
|
end
|
|
281
307
|
|