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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +74 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -14
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +3 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.c +69 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.h +7 -0
- data/ext/datadog_profiling_native_extension/http_transport.c +25 -32
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +22 -21
- data/ext/libdatadog_api/datadog_ruby_common.h +3 -0
- data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
- data/lib/datadog/appsec/actions_handler.rb +22 -1
- data/lib/datadog/appsec/anonymizer.rb +16 -0
- data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
- data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +0 -1344
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
- data/lib/datadog/appsec/assets/waf_rules/strict.json +0 -1344
- data/lib/datadog/appsec/component.rb +19 -17
- data/lib/datadog/appsec/compressed_json.rb +40 -0
- data/lib/datadog/appsec/configuration/settings.rb +62 -10
- data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/configuration.rb +7 -31
- data/lib/datadog/appsec/contrib/devise/data_extractor.rb +79 -0
- data/lib/datadog/appsec/contrib/devise/ext.rb +21 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +0 -1
- data/lib/datadog/appsec/contrib/devise/patcher.rb +36 -23
- data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
- data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
- data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +2 -2
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +93 -0
- data/lib/datadog/appsec/contrib/rack/ext.rb +14 -0
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -3
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -2
- data/lib/datadog/appsec/event.rb +22 -51
- data/lib/datadog/appsec/ext.rb +4 -2
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +4 -2
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -3
- data/lib/datadog/appsec/remote.rb +4 -0
- data/lib/datadog/appsec/security_engine/runner.rb +2 -2
- data/lib/datadog/appsec/utils.rb +0 -2
- data/lib/datadog/core/configuration/components.rb +2 -1
- data/lib/datadog/core/configuration/ext.rb +4 -0
- data/lib/datadog/core/configuration/options.rb +2 -2
- data/lib/datadog/core/configuration/settings.rb +53 -30
- data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/core/environment/agent_info.rb +4 -3
- data/lib/datadog/core/metrics/client.rb +1 -1
- data/lib/datadog/core/remote/client.rb +1 -1
- data/lib/datadog/core/remote/component.rb +3 -6
- data/lib/datadog/core/remote/configuration/repository.rb +2 -1
- data/lib/datadog/core/remote/negotiation.rb +9 -9
- data/lib/datadog/core/remote/transport/config.rb +4 -3
- data/lib/datadog/core/remote/transport/http/client.rb +4 -3
- data/lib/datadog/core/remote/transport/http/config.rb +6 -32
- data/lib/datadog/core/remote/transport/http/negotiation.rb +6 -32
- data/lib/datadog/core/remote/transport/http.rb +22 -57
- data/lib/datadog/core/remote/transport/negotiation.rb +4 -3
- data/lib/datadog/core/runtime/metrics.rb +8 -1
- data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
- data/lib/datadog/core/telemetry/metric.rb +5 -5
- data/lib/datadog/core/telemetry/request.rb +1 -1
- data/lib/datadog/core/transport/http/api/instance.rb +17 -0
- data/lib/datadog/core/transport/http/api/spec.rb +17 -0
- data/lib/datadog/core/transport/http/builder.rb +5 -3
- data/lib/datadog/core/transport/http.rb +39 -2
- data/lib/datadog/di/component.rb +0 -2
- data/lib/datadog/di/probe_notification_builder.rb +1 -1
- data/lib/datadog/di/probe_notifier_worker.rb +16 -16
- data/lib/datadog/di/transport/diagnostics.rb +4 -3
- data/lib/datadog/di/transport/http/api.rb +2 -12
- data/lib/datadog/di/transport/http/client.rb +4 -3
- data/lib/datadog/di/transport/http/diagnostics.rb +7 -34
- data/lib/datadog/di/transport/http/input.rb +7 -34
- data/lib/datadog/di/transport/http.rb +14 -62
- data/lib/datadog/di/transport/input.rb +4 -3
- data/lib/datadog/di/utils.rb +5 -0
- data/lib/datadog/kit/appsec/events.rb +12 -0
- data/lib/datadog/kit/identity.rb +5 -1
- data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
- data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
- data/lib/datadog/opentelemetry/api/context.rb +16 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
- data/lib/datadog/opentelemetry.rb +2 -1
- data/lib/datadog/profiling/collectors/info.rb +3 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
- data/lib/datadog/profiling/encoded_profile.rb +11 -0
- data/lib/datadog/profiling/exporter.rb +2 -3
- data/lib/datadog/profiling/ext.rb +0 -1
- data/lib/datadog/profiling/flush.rb +4 -7
- data/lib/datadog/profiling/http_transport.rb +10 -59
- data/lib/datadog/profiling/stack_recorder.rb +4 -4
- data/lib/datadog/profiling.rb +6 -2
- data/lib/datadog/tracing/component.rb +15 -12
- data/lib/datadog/tracing/configuration/ext.rb +7 -1
- data/lib/datadog/tracing/configuration/settings.rb +18 -2
- data/lib/datadog/tracing/context_provider.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
- data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
- data/lib/datadog/tracing/contrib/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -5
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -11
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +6 -10
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +46 -0
- data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
- data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
- data/lib/datadog/tracing/contrib/karafka.rb +37 -0
- data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
- data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/correlation.rb +9 -2
- data/lib/datadog/tracing/distributed/baggage.rb +131 -0
- data/lib/datadog/tracing/distributed/datadog.rb +2 -0
- data/lib/datadog/tracing/distributed/propagation.rb +25 -4
- data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
- data/lib/datadog/tracing/metadata/ext.rb +5 -0
- data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
- data/lib/datadog/tracing/span_event.rb +1 -1
- data/lib/datadog/tracing/span_operation.rb +2 -1
- data/lib/datadog/tracing/sync_writer.rb +1 -2
- data/lib/datadog/tracing/trace_digest.rb +9 -2
- data/lib/datadog/tracing/trace_operation.rb +29 -17
- data/lib/datadog/tracing/trace_segment.rb +6 -4
- data/lib/datadog/tracing/tracer.rb +38 -2
- data/lib/datadog/tracing/transport/http/api.rb +2 -10
- data/lib/datadog/tracing/transport/http/client.rb +5 -4
- data/lib/datadog/tracing/transport/http/traces.rb +13 -41
- data/lib/datadog/tracing/transport/http.rb +11 -44
- data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
- data/lib/datadog/tracing/transport/traces.rb +26 -9
- data/lib/datadog/tracing/workers/trace_writer.rb +2 -6
- data/lib/datadog/tracing/writer.rb +2 -6
- data/lib/datadog/tracing.rb +16 -3
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +1 -1
- metadata +28 -13
- data/lib/datadog/appsec/contrib/devise/event.rb +0 -54
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -72
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -47
- data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
- data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
- 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
|
-
|
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
|
-
|
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}"]
|
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
|
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -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 '
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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] =
|
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
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
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
|
-
|
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.
|
161
|
+
trace.set_distributed_source(Datadog::AppSec::Ext::PRODUCT_BIT)
|
191
162
|
end
|
192
163
|
end
|
193
164
|
end
|
data/lib/datadog/appsec/ext.rb
CHANGED
@@ -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
|
-
|
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
|
@@ -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
|
-
|
23
|
-
|
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?
|
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?
|
33
|
+
v.nil? || v.empty?
|
34
34
|
end
|
35
35
|
|
36
36
|
_code, result = try_run(persistent_data, ephemeral_data, timeout)
|
data/lib/datadog/appsec/utils.rb
CHANGED
@@ -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}="
|
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
|
-
|
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
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
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
|
@@ -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
|
-
@
|
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.
|
@@ -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
|
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
|
-
|
22
|
-
|
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
|
-
|
275
|
+
|
276
|
+
updated(path, content, previous) if content && previous
|
276
277
|
end
|
277
278
|
|
278
279
|
def deleted(path, previous)
|