datadog 2.12.1 → 2.13.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 +45 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +14 -13
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +8 -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/configuration/settings.rb +62 -10
- 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 +34 -0
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +27 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -2
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/appsec/ext.rb +4 -2
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +4 -2
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
- data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -3
- data/lib/datadog/appsec/processor/rule_merger.rb +2 -1
- data/lib/datadog/appsec/remote.rb +7 -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/environment/agent_info.rb +4 -3
- 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/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_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 -33
- data/lib/datadog/di/transport/http/input.rb +7 -33
- data/lib/datadog/di/transport/http.rb +14 -56
- data/lib/datadog/di/transport/input.rb +4 -3
- data/lib/datadog/di/utils.rb +5 -0
- data/lib/datadog/kit/appsec/events.rb +9 -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/thread_context.rb +1 -1
- data/lib/datadog/profiling.rb +5 -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/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/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/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/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/metadata/metastruct.rb +36 -0
- data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
- data/lib/datadog/tracing/metadata.rb +2 -0
- data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
- data/lib/datadog/tracing/span.rb +10 -1
- data/lib/datadog/tracing/span_operation.rb +8 -2
- 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/serializable_trace.rb +3 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
- data/lib/datadog/tracing/transport/traces.rb +21 -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
- metadata +20 -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
@@ -1,15 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
|
5
|
-
require_relative '
|
3
|
+
require_relative '../../../core/utils/only_once'
|
4
|
+
|
5
|
+
require_relative 'tracking_middleware'
|
6
|
+
require_relative 'patches/signup_tracking_patch'
|
7
|
+
require_relative 'patches/signin_tracking_patch'
|
8
|
+
require_relative 'patches/skip_signin_tracking_patch'
|
6
9
|
|
7
10
|
module Datadog
|
8
11
|
module AppSec
|
9
12
|
module Contrib
|
10
13
|
module Devise
|
11
|
-
#
|
14
|
+
# Devise patcher
|
12
15
|
module Patcher
|
16
|
+
GUARD_ONCE_PER_APP = Hash.new do |hash, key|
|
17
|
+
hash[key] = Datadog::Core::Utils::OnlyOnce.new
|
18
|
+
end
|
19
|
+
|
13
20
|
module_function
|
14
21
|
|
15
22
|
def patched?
|
@@ -21,29 +28,35 @@ module Datadog
|
|
21
28
|
end
|
22
29
|
|
23
30
|
def patch
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
31
|
+
::ActiveSupport.on_load(:before_initialize) do |app|
|
32
|
+
GUARD_ONCE_PER_APP[app].run do
|
33
|
+
begin
|
34
|
+
app.middleware.insert_after(Warden::Manager, TrackingMiddleware)
|
35
|
+
rescue RuntimeError
|
36
|
+
AppSec.telemetry.error('AppSec: unable to insert Devise TrackingMiddleware')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
34
40
|
|
35
|
-
|
36
|
-
|
41
|
+
::ActiveSupport.on_load(:after_initialize) do
|
42
|
+
if ::Devise::RegistrationsController.descendants.empty?
|
43
|
+
::Devise::RegistrationsController.prepend(Patches::SignupTrackingPatch)
|
44
|
+
else
|
45
|
+
::Devise::RegistrationsController.descendants.each do |controller|
|
46
|
+
controller.prepend(Patches::SignupTrackingPatch)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
37
50
|
|
38
|
-
|
39
|
-
::Devise::Models::Rememberable # rubocop:disable Lint/Void
|
40
|
-
::Devise::Strategies::Rememberable.prepend(RememberablePatch)
|
41
|
-
end
|
51
|
+
::Devise::Strategies::Authenticatable.prepend(Patches::SigninTrackingPatch)
|
42
52
|
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
if ::Devise::STRATEGIES.include?(:rememberable)
|
54
|
+
# Rememberable strategy is required in autoloaded Rememberable model
|
55
|
+
require 'devise/models/rememberable'
|
56
|
+
::Devise::Strategies::Rememberable.prepend(Patches::SkipSigninTrackingPatch)
|
46
57
|
end
|
58
|
+
|
59
|
+
Patcher.instance_variable_set(:@patched, true)
|
47
60
|
end
|
48
61
|
end
|
49
62
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../ext'
|
4
|
+
require_relative '../configuration'
|
5
|
+
require_relative '../data_extractor'
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module AppSec
|
9
|
+
module Contrib
|
10
|
+
module Devise
|
11
|
+
module Patches
|
12
|
+
# A patch for Devise::Authenticatable strategy with tracking functionality
|
13
|
+
module SigninTrackingPatch
|
14
|
+
def validate(resource, &block)
|
15
|
+
result = super
|
16
|
+
|
17
|
+
return result unless AppSec.enabled?
|
18
|
+
return result if @_datadog_appsec_skip_track_login_event
|
19
|
+
return result unless Configuration.auto_user_instrumentation_enabled?
|
20
|
+
return result unless AppSec.active_context
|
21
|
+
|
22
|
+
context = AppSec.active_context
|
23
|
+
if context.trace.nil? || context.span.nil?
|
24
|
+
Datadog.logger.debug { 'AppSec: unable to track signin events, due to missing trace or span' }
|
25
|
+
return result
|
26
|
+
end
|
27
|
+
|
28
|
+
context.trace.keep!
|
29
|
+
|
30
|
+
if result
|
31
|
+
record_successful_signin(context, resource)
|
32
|
+
Instrumentation.gateway.push('appsec.events.user_lifecycle', Ext::EVENT_LOGIN_SUCCESS)
|
33
|
+
|
34
|
+
return result
|
35
|
+
end
|
36
|
+
|
37
|
+
record_failed_signin(context, resource)
|
38
|
+
Instrumentation.gateway.push('appsec.events.user_lifecycle', Ext::EVENT_LOGIN_FAILURE)
|
39
|
+
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def record_successful_signin(context, resource)
|
46
|
+
extractor = DataExtractor.new(mode: Configuration.auto_user_instrumentation_mode)
|
47
|
+
|
48
|
+
id = extractor.extract_id(resource)
|
49
|
+
login = extractor.extract_login(authentication_hash) || extractor.extract_login(resource)
|
50
|
+
|
51
|
+
if id
|
52
|
+
context.span[Ext::TAG_USR_ID] ||= id
|
53
|
+
context.span[Ext::TAG_DD_USR_ID] = id
|
54
|
+
end
|
55
|
+
|
56
|
+
context.span[Ext::TAG_LOGIN_SUCCESS_USR_LOGIN] ||= login
|
57
|
+
context.span[Ext::TAG_LOGIN_SUCCESS_TRACK] = 'true'
|
58
|
+
context.span[Ext::TAG_DD_USR_LOGIN] = login
|
59
|
+
context.span[Ext::TAG_DD_LOGIN_SUCCESS_MODE] = Configuration.auto_user_instrumentation_mode
|
60
|
+
|
61
|
+
# NOTE: We don't have a way to make one-shot receivers for events,
|
62
|
+
# and because of that we will trigger an additional event even
|
63
|
+
# if it was already done via the SDK
|
64
|
+
AppSec::Instrumentation.gateway.push(
|
65
|
+
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(id, login)
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def record_failed_signin(context, resource)
|
70
|
+
extractor = DataExtractor.new(mode: Configuration.auto_user_instrumentation_mode)
|
71
|
+
|
72
|
+
context.span[Ext::TAG_LOGIN_FAILURE_TRACK] = 'true'
|
73
|
+
context.span[Ext::TAG_DD_LOGIN_FAILURE_MODE] = Configuration.auto_user_instrumentation_mode
|
74
|
+
|
75
|
+
unless resource
|
76
|
+
login = extractor.extract_login(authentication_hash)
|
77
|
+
|
78
|
+
context.span[Ext::TAG_DD_USR_LOGIN] = login
|
79
|
+
context.span[Ext::TAG_LOGIN_FAILURE_USR_LOGIN] ||= login
|
80
|
+
context.span[Ext::TAG_LOGIN_FAILURE_USR_EXISTS] ||= 'false'
|
81
|
+
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
id = extractor.extract_id(resource)
|
86
|
+
login = extractor.extract_login(authentication_hash) || extractor.extract_login(resource)
|
87
|
+
|
88
|
+
if id
|
89
|
+
context.span[Ext::TAG_DD_USR_ID] = id
|
90
|
+
context.span[Ext::TAG_LOGIN_FAILURE_USR_ID] ||= id
|
91
|
+
end
|
92
|
+
|
93
|
+
context.span[Ext::TAG_DD_USR_LOGIN] = login
|
94
|
+
context.span[Ext::TAG_LOGIN_FAILURE_USR_LOGIN] ||= login
|
95
|
+
context.span[Ext::TAG_LOGIN_FAILURE_USR_EXISTS] ||= 'true'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../ext'
|
4
|
+
require_relative '../configuration'
|
5
|
+
require_relative '../data_extractor'
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module AppSec
|
9
|
+
module Contrib
|
10
|
+
module Devise
|
11
|
+
module Patches
|
12
|
+
# A patch for Devise::RegistrationsController with tracking functionality
|
13
|
+
module SignupTrackingPatch
|
14
|
+
def create
|
15
|
+
return super unless AppSec.enabled?
|
16
|
+
return super unless Configuration.auto_user_instrumentation_enabled?
|
17
|
+
return super unless AppSec.active_context
|
18
|
+
|
19
|
+
super do |resource|
|
20
|
+
context = AppSec.active_context
|
21
|
+
|
22
|
+
if context.trace.nil? || context.span.nil?
|
23
|
+
Datadog.logger.debug { 'AppSec: unable to track signup events, due to missing trace or span' }
|
24
|
+
next yield(resource) if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
next yield(resource) if resource.new_record? && block_given?
|
28
|
+
|
29
|
+
context.trace.keep!
|
30
|
+
record_successful_signup(context, resource)
|
31
|
+
Instrumentation.gateway.push('appsec.events.user_lifecycle', Ext::EVENT_SIGNUP)
|
32
|
+
|
33
|
+
yield(resource) if block_given?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def record_successful_signup(context, resource)
|
40
|
+
extractor = DataExtractor.new(mode: Configuration.auto_user_instrumentation_mode)
|
41
|
+
|
42
|
+
id = extractor.extract_id(resource)
|
43
|
+
login = extractor.extract_login(resource_params) || extractor.extract_login(resource)
|
44
|
+
|
45
|
+
context.span[Ext::TAG_SIGNUP_TRACK] = 'true'
|
46
|
+
context.span[Ext::TAG_DD_USR_LOGIN] = login
|
47
|
+
context.span[Ext::TAG_SIGNUP_USR_LOGIN] ||= login
|
48
|
+
context.span[Ext::TAG_DD_SIGNUP_MODE] = Configuration.auto_user_instrumentation_mode
|
49
|
+
|
50
|
+
if id
|
51
|
+
context.span[Ext::TAG_DD_USR_ID] = id
|
52
|
+
|
53
|
+
id_tag = resource.active_for_authentication? ? Ext::TAG_USR_ID : Ext::TAG_SIGNUP_USR_ID
|
54
|
+
context.span[id_tag] ||= id
|
55
|
+
end
|
56
|
+
|
57
|
+
# NOTE: We don't have a way to make one-shot receivers for events,
|
58
|
+
# and because of that we will trigger an additional event even
|
59
|
+
# if it was already done via the SDK
|
60
|
+
AppSec::Instrumentation.gateway.push(
|
61
|
+
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(id, login)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -4,10 +4,10 @@ module Datadog
|
|
4
4
|
module AppSec
|
5
5
|
module Contrib
|
6
6
|
module Devise
|
7
|
-
module
|
7
|
+
module Patches
|
8
8
|
# To avoid tracking new sessions that are created by
|
9
9
|
# Rememberable strategy as Login Success events.
|
10
|
-
module
|
10
|
+
module SkipSigninTrackingPatch
|
11
11
|
def validate(*args)
|
12
12
|
@_datadog_appsec_skip_track_login_event = true
|
13
13
|
|
@@ -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,40 @@ 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
|
+
|
23
|
+
IDENTITY_COLLECTABLE_REQUEST_HEADERS = [
|
24
|
+
'accept-encoding',
|
25
|
+
'accept-language',
|
26
|
+
'cf-connecting-ip',
|
27
|
+
'cf-connecting-ipv6',
|
28
|
+
'content-encoding',
|
29
|
+
'content-language',
|
30
|
+
'content-length',
|
31
|
+
'fastly-client-ip',
|
32
|
+
'forwarded',
|
33
|
+
'forwarded-for',
|
34
|
+
'host',
|
35
|
+
'true-client-ip',
|
36
|
+
'via',
|
37
|
+
'x-client-ip',
|
38
|
+
'x-cluster-client-ip',
|
39
|
+
'x-forwarded',
|
40
|
+
'x-forwarded-for',
|
41
|
+
'x-real-ip'
|
42
|
+
].freeze
|
9
43
|
end
|
10
44
|
end
|
11
45
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../ext'
|
3
4
|
require_relative '../../../instrumentation/gateway'
|
4
5
|
require_relative '../../../event'
|
5
6
|
|
@@ -17,6 +18,7 @@ module Datadog
|
|
17
18
|
watch_request(gateway)
|
18
19
|
watch_response(gateway)
|
19
20
|
watch_request_body(gateway)
|
21
|
+
watch_request_finish(gateway)
|
20
22
|
end
|
21
23
|
|
22
24
|
def watch_request(gateway = Instrumentation.gateway)
|
@@ -110,6 +112,31 @@ module Datadog
|
|
110
112
|
stack.call(gateway_request.request)
|
111
113
|
end
|
112
114
|
end
|
115
|
+
|
116
|
+
# NOTE: In the current state we unable to substibe twice to the same
|
117
|
+
# event within the same group. Ideally this code should live
|
118
|
+
# somewhere closer to identity related monitor.
|
119
|
+
# WARNING: The Gateway is a subject of refactoring
|
120
|
+
def watch_request_finish(gateway = Instrumentation.gateway)
|
121
|
+
gateway.watch('rack.request.finish', :appsec) do |stack, gateway_request|
|
122
|
+
context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
|
123
|
+
|
124
|
+
if context.span.nil? || !gateway.pushed?('appsec.events.user_lifecycle')
|
125
|
+
next stack.call(gateway_request.request)
|
126
|
+
end
|
127
|
+
|
128
|
+
gateway_request.headers.each do |name, value|
|
129
|
+
if !Ext::COLLECTABLE_REQUEST_HEADERS.include?(name) &&
|
130
|
+
!Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
context.span["http.request.headers.#{name}"] ||= value
|
135
|
+
end
|
136
|
+
|
137
|
+
stack.call(gateway_request.request)
|
138
|
+
end
|
139
|
+
end
|
113
140
|
end
|
114
141
|
end
|
115
142
|
end
|
@@ -77,6 +77,8 @@ module Datadog
|
|
77
77
|
gateway_response = nil
|
78
78
|
|
79
79
|
interrupt_params = catch(::Datadog::AppSec::Ext::INTERRUPT) do
|
80
|
+
# TODO: This event should be renamed into `rack.request.start` to
|
81
|
+
# reflect that it's the beginning of the request-cycle
|
80
82
|
http_response, _gateway_request = Instrumentation.gateway.push('rack.request', gateway_request) do
|
81
83
|
@app.call(env)
|
82
84
|
end
|
@@ -85,6 +87,7 @@ module Datadog
|
|
85
87
|
http_response[2], http_response[0], http_response[1], context: ctx
|
86
88
|
)
|
87
89
|
|
90
|
+
Instrumentation.gateway.push('rack.request.finish', gateway_request)
|
88
91
|
Instrumentation.gateway.push('rack.response', gateway_response)
|
89
92
|
|
90
93
|
nil
|
@@ -147,8 +150,6 @@ module Datadog
|
|
147
150
|
return unless trace && span
|
148
151
|
|
149
152
|
span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
|
150
|
-
# We add this tag when ASM standalone is enabled to make sure we don't bill APM
|
151
|
-
span.set_metric(Datadog::AppSec::Ext::TAG_APM_ENABLED, 0) if Datadog.configuration.appsec.standalone.enabled
|
152
153
|
span.set_tag('_dd.runtime_family', 'ruby')
|
153
154
|
span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
|
154
155
|
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -187,7 +187,7 @@ module Datadog
|
|
187
187
|
Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
|
188
188
|
Datadog::Tracing::Sampling::Ext::Decision::ASM
|
189
189
|
)
|
190
|
-
trace.
|
190
|
+
trace.set_distributed_source(Datadog::AppSec::Ext::PRODUCT_BIT)
|
191
191
|
end
|
192
192
|
end
|
193
193
|
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
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module Instrumentation
|
6
|
+
class Gateway
|
7
|
+
# NOTE: This class extracted as-is and will be deprecated
|
8
|
+
# Instrumentation gateway middleware
|
9
|
+
class Middleware
|
10
|
+
attr_reader :key, :block
|
11
|
+
|
12
|
+
def initialize(key, &block)
|
13
|
+
@key = key
|
14
|
+
@block = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(stack, env)
|
18
|
+
@block.call(stack, env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,35 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'gateway/middleware'
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module AppSec
|
5
7
|
# Instrumentation for AppSec
|
6
8
|
module Instrumentation
|
7
9
|
# Instrumentation gateway implementation
|
8
10
|
class Gateway
|
9
|
-
# Instrumentation gateway middleware
|
10
|
-
class Middleware
|
11
|
-
attr_reader :key, :block
|
12
|
-
|
13
|
-
def initialize(key, &block)
|
14
|
-
@key = key
|
15
|
-
@block = block
|
16
|
-
end
|
17
|
-
|
18
|
-
def call(stack, env)
|
19
|
-
@block.call(stack, env)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private_constant :Middleware
|
24
|
-
|
25
11
|
def initialize
|
26
12
|
@middlewares = Hash.new { |h, k| h[k] = [] }
|
13
|
+
@pushed_events = {}
|
27
14
|
end
|
28
15
|
|
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.
|
29
22
|
def push(name, env, &block)
|
30
|
-
|
23
|
+
@pushed_events[name] = true
|
31
24
|
|
32
|
-
|
25
|
+
block ||= -> {}
|
26
|
+
middlewares_for_name = @middlewares[name]
|
33
27
|
|
34
28
|
return [block.call, nil] if middlewares_for_name.empty?
|
35
29
|
|
@@ -48,14 +42,15 @@ module Datadog
|
|
48
42
|
end
|
49
43
|
|
50
44
|
def watch(name, key, &block)
|
51
|
-
@middlewares[name] << Middleware.new(key, &block) unless middlewares[name].any? { |m| m.key == key }
|
45
|
+
@middlewares[name] << Middleware.new(key, &block) unless @middlewares[name].any? { |m| m.key == key }
|
52
46
|
end
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
|
48
|
+
def pushed?(name)
|
49
|
+
@pushed_events.key?(name)
|
50
|
+
end
|
57
51
|
end
|
58
52
|
|
53
|
+
# NOTE: This left as-is and will be depricated soon.
|
59
54
|
def self.gateway
|
60
55
|
@gateway ||= Gateway.new # TODO: not thread safe
|
61
56
|
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
|
|
@@ -22,7 +22,7 @@ module Datadog
|
|
22
22
|
# TODO: `processors` and `scanners` are not provided by the caller, consider removing them
|
23
23
|
def merge(
|
24
24
|
telemetry:,
|
25
|
-
rules:, data: [], overrides: [], exclusions: [], custom_rules: [],
|
25
|
+
rules:, actions: [], data: [], overrides: [], exclusions: [], custom_rules: [],
|
26
26
|
processors: nil, scanners: nil
|
27
27
|
)
|
28
28
|
processors ||= begin
|
@@ -54,6 +54,7 @@ module Datadog
|
|
54
54
|
combined_exclusions = combine_exclusions(exclusions) if exclusions.any?
|
55
55
|
combined_custom_rules = combine_custom_rules(custom_rules) if custom_rules.any?
|
56
56
|
|
57
|
+
combined_rules['actions'] = actions if actions.any?
|
57
58
|
combined_rules['rules_data'] = combined_data if combined_data
|
58
59
|
combined_rules['rules_override'] = combined_overrides if combined_overrides
|
59
60
|
combined_rules['exclusions'] = combined_exclusions if combined_exclusions
|