datadog 2.3.0 → 2.5.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 +64 -2
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
- data/ext/datadog_profiling_loader/extconf.rb +10 -22
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +3 -3
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +198 -41
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +89 -46
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +645 -107
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +15 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +0 -27
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -4
- data/ext/datadog_profiling_native_extension/extconf.rb +42 -25
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +194 -34
- data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
- data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +53 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/datadog_profiling_native_extension/profiling.c +1 -1
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +14 -11
- data/ext/datadog_profiling_native_extension/stack_recorder.c +58 -22
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
- data/ext/libdatadog_api/crashtracker.c +20 -18
- data/ext/libdatadog_api/datadog_ruby_common.c +0 -27
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
- data/lib/datadog/appsec/component.rb +29 -8
- data/lib/datadog/appsec/configuration/settings.rb +10 -2
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
- data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
- data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +0 -14
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +67 -31
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +14 -15
- data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -15
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +6 -18
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +5 -18
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +5 -18
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -10
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +7 -20
- data/lib/datadog/appsec/event.rb +25 -1
- data/lib/datadog/appsec/ext.rb +4 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +7 -20
- data/lib/datadog/appsec/processor/context.rb +109 -0
- data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
- data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
- data/lib/datadog/appsec/processor.rb +42 -107
- data/lib/datadog/appsec/rate_limiter.rb +25 -40
- data/lib/datadog/appsec/remote.rb +7 -3
- data/lib/datadog/appsec/scope.rb +1 -4
- data/lib/datadog/appsec/utils/trace_operation.rb +15 -0
- data/lib/datadog/appsec/utils.rb +2 -0
- data/lib/datadog/appsec.rb +3 -2
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
- data/lib/datadog/core/configuration/components.rb +4 -3
- data/lib/datadog/core/configuration/settings.rb +96 -5
- data/lib/datadog/core/configuration.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +9 -6
- data/lib/datadog/core/environment/execution.rb +5 -5
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/metrics/client.rb +7 -0
- data/lib/datadog/core/rate_limiter.rb +183 -0
- data/lib/datadog/core/remote/client/capabilities.rb +4 -3
- data/lib/datadog/core/remote/component.rb +4 -2
- data/lib/datadog/core/remote/negotiation.rb +4 -4
- data/lib/datadog/core/remote/tie.rb +2 -0
- data/lib/datadog/core/remote/transport/http.rb +5 -0
- data/lib/datadog/core/remote/worker.rb +1 -1
- data/lib/datadog/core/runtime/ext.rb +1 -0
- data/lib/datadog/core/runtime/metrics.rb +5 -1
- data/lib/datadog/core/semaphore.rb +35 -0
- data/lib/datadog/core/telemetry/component.rb +2 -0
- data/lib/datadog/core/telemetry/event.rb +12 -7
- data/lib/datadog/core/telemetry/logger.rb +51 -0
- data/lib/datadog/core/telemetry/logging.rb +50 -14
- data/lib/datadog/core/telemetry/request.rb +13 -1
- data/lib/datadog/core/transport/ext.rb +1 -0
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/di/code_tracker.rb +166 -0
- data/lib/datadog/di/configuration/settings.rb +163 -0
- data/lib/datadog/di/configuration.rb +11 -0
- data/lib/datadog/di/error.rb +31 -0
- data/lib/datadog/di/extensions.rb +16 -0
- data/lib/datadog/di/instrumenter.rb +301 -0
- data/lib/datadog/di/probe.rb +162 -0
- data/lib/datadog/di/probe_builder.rb +47 -0
- data/lib/datadog/di/probe_notification_builder.rb +207 -0
- data/lib/datadog/di/probe_notifier_worker.rb +244 -0
- data/lib/datadog/di/redactor.rb +188 -0
- data/lib/datadog/di/serializer.rb +215 -0
- data/lib/datadog/di/transport.rb +67 -0
- data/lib/datadog/di/utils.rb +39 -0
- data/lib/datadog/di.rb +57 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -10
- data/lib/datadog/profiling/collectors/info.rb +12 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +32 -8
- data/lib/datadog/profiling/component.rb +21 -4
- data/lib/datadog/profiling/http_transport.rb +6 -1
- data/lib/datadog/profiling/scheduler.rb +2 -0
- data/lib/datadog/profiling/stack_recorder.rb +40 -9
- data/lib/datadog/single_step_instrument.rb +12 -0
- data/lib/datadog/tracing/component.rb +13 -0
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +12 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +24 -2
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
- data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +13 -9
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +6 -3
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +9 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +22 -15
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -5
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +9 -0
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -2
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
- data/lib/datadog/tracing/contrib/patcher.rb +2 -1
- data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
- data/lib/datadog/tracing/distributed/propagation.rb +7 -0
- data/lib/datadog/tracing/metadata/ext.rb +2 -0
- data/lib/datadog/tracing/remote.rb +5 -2
- data/lib/datadog/tracing/sampling/matcher.rb +6 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/rule.rb +2 -0
- data/lib/datadog/tracing/sampling/rule_sampler.rb +15 -9
- data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
- data/lib/datadog/tracing/trace_operation.rb +26 -2
- data/lib/datadog/tracing/tracer.rb +29 -22
- data/lib/datadog/tracing/transport/http/client.rb +1 -0
- data/lib/datadog/tracing/transport/http.rb +4 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
- data/lib/datadog/tracing/workers.rb +2 -2
- data/lib/datadog/tracing/writer.rb +26 -28
- data/lib/datadog/version.rb +1 -1
- metadata +40 -15
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -10,10 +10,11 @@ module Datadog
|
|
10
10
|
# Core-pluggable component for AppSec
|
11
11
|
class Component
|
12
12
|
class << self
|
13
|
-
def build_appsec_component(settings)
|
14
|
-
return
|
13
|
+
def build_appsec_component(settings, telemetry:)
|
14
|
+
return if !settings.respond_to?(:appsec) || !settings.appsec.enabled
|
15
|
+
return if incompatible_ffi_version?
|
15
16
|
|
16
|
-
processor = create_processor(settings)
|
17
|
+
processor = create_processor(settings, telemetry)
|
17
18
|
|
18
19
|
# We want to always instrument user events when AppSec is enabled.
|
19
20
|
# There could be cases in which users use the DD_APPSEC_ENABLED Env variable to
|
@@ -28,8 +29,27 @@ module Datadog
|
|
28
29
|
|
29
30
|
private
|
30
31
|
|
31
|
-
def
|
32
|
-
|
32
|
+
def incompatible_ffi_version?
|
33
|
+
ffi_version = Gem.loaded_specs['ffi'] && Gem.loaded_specs['ffi'].version
|
34
|
+
return true unless ffi_version
|
35
|
+
|
36
|
+
return false unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3') &&
|
37
|
+
ffi_version < Gem::Version.new('1.16.0')
|
38
|
+
|
39
|
+
Datadog.logger.warn(
|
40
|
+
'AppSec is not supported in Ruby versions above 3.3.0 when using `ffi` versions older than 1.16.0, ' \
|
41
|
+
'and will be forcibly disabled due to a memory leak in `ffi`. ' \
|
42
|
+
'Please upgrade your `ffi` version to 1.16.0 or higher.'
|
43
|
+
)
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_processor(settings, telemetry)
|
49
|
+
rules = AppSec::Processor::RuleLoader.load_rules(
|
50
|
+
telemetry: telemetry,
|
51
|
+
ruleset: settings.appsec.ruleset
|
52
|
+
)
|
33
53
|
return nil unless rules
|
34
54
|
|
35
55
|
actions = rules['actions']
|
@@ -47,9 +67,10 @@ module Datadog
|
|
47
67
|
rules: [rules],
|
48
68
|
data: data,
|
49
69
|
exclusions: exclusions,
|
70
|
+
telemetry: telemetry
|
50
71
|
)
|
51
72
|
|
52
|
-
processor = Processor.new(ruleset: ruleset)
|
73
|
+
processor = Processor.new(ruleset: ruleset, telemetry: telemetry)
|
53
74
|
return nil unless processor.ready?
|
54
75
|
|
55
76
|
processor
|
@@ -63,11 +84,11 @@ module Datadog
|
|
63
84
|
@mutex = Mutex.new
|
64
85
|
end
|
65
86
|
|
66
|
-
def reconfigure(ruleset:, actions:)
|
87
|
+
def reconfigure(ruleset:, actions:, telemetry:)
|
67
88
|
@mutex.synchronize do
|
68
89
|
AppSec::Processor::Actions.merge(actions)
|
69
90
|
|
70
|
-
new = Processor.new(ruleset: ruleset)
|
91
|
+
new = Processor.new(ruleset: ruleset, telemetry: telemetry)
|
71
92
|
|
72
93
|
if new && new.ready?
|
73
94
|
old = @processor
|
@@ -9,8 +9,8 @@ module Datadog
|
|
9
9
|
# Settings
|
10
10
|
module Settings
|
11
11
|
# rubocop:disable Layout/LineLength
|
12
|
-
DEFAULT_OBFUSCATOR_KEY_REGEX = '(?i)(?:
|
13
|
-
DEFAULT_OBFUSCATOR_VALUE_REGEX = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret
|
12
|
+
DEFAULT_OBFUSCATOR_KEY_REGEX = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\.net[_-]sessionid|sid|jwt'
|
13
|
+
DEFAULT_OBFUSCATOR_VALUE_REGEX = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\.net(?:[_-]|-)sessionid|sid|jwt)(?:\s*=[^;]|"\s*:\s*"[^"]+")|bearer\s+[a-z0-9\._\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\w=-]+\.ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}'
|
14
14
|
# rubocop:enable Layout/LineLength
|
15
15
|
APPSEC_VALID_TRACK_USER_EVENTS_MODE = [
|
16
16
|
'safe',
|
@@ -197,6 +197,14 @@ module Datadog
|
|
197
197
|
o.type :bool, nilable: true
|
198
198
|
o.env 'DD_APPSEC_SCA_ENABLED'
|
199
199
|
end
|
200
|
+
|
201
|
+
settings :standalone do
|
202
|
+
option :enabled do |o|
|
203
|
+
o.type :bool
|
204
|
+
o.env 'DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED'
|
205
|
+
o.default false
|
206
|
+
end
|
207
|
+
end
|
200
208
|
end
|
201
209
|
end
|
202
210
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module Contrib
|
6
|
+
module Devise
|
7
|
+
module Patcher
|
8
|
+
# To avoid tracking new sessions that are created by
|
9
|
+
# Rememberable strategy as Login Success events.
|
10
|
+
module RememberablePatch
|
11
|
+
def validate(*args)
|
12
|
+
@_datadog_skip_track_login_event = true
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../patcher'
|
4
4
|
require_relative 'patcher/authenticatable_patch'
|
5
|
+
require_relative 'patcher/rememberable_patch'
|
5
6
|
require_relative 'patcher/registration_controller_patch'
|
6
7
|
|
7
8
|
module Datadog
|
@@ -23,16 +24,25 @@ module Datadog
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def patch
|
26
|
-
|
27
|
+
patch_authenticatable_strategy
|
28
|
+
patch_rememberable_strategy
|
27
29
|
patch_registration_controller
|
28
30
|
|
29
31
|
Patcher.instance_variable_set(:@patched, true)
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def patch_authenticatable_strategy
|
33
35
|
::Devise::Strategies::Authenticatable.prepend(AuthenticatablePatch)
|
34
36
|
end
|
35
37
|
|
38
|
+
def patch_rememberable_strategy
|
39
|
+
return unless ::Devise::STRATEGIES.include?(:rememberable)
|
40
|
+
|
41
|
+
# Rememberable strategy is required in autoloaded Rememberable model
|
42
|
+
::Devise::Models::Rememberable # rubocop:disable Lint/Void
|
43
|
+
::Devise::Strategies::Rememberable.prepend(RememberablePatch)
|
44
|
+
end
|
45
|
+
|
36
46
|
def patch_registration_controller
|
37
47
|
::ActiveSupport.on_load(:after_initialize) do
|
38
48
|
::Devise::RegistrationsController.prepend(RegistrationControllerPatch)
|
@@ -28,20 +28,6 @@ module Datadog
|
|
28
28
|
|
29
29
|
multiplex_return
|
30
30
|
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def active_trace
|
35
|
-
return unless defined?(Datadog::Tracing)
|
36
|
-
|
37
|
-
Datadog::Tracing.active_trace
|
38
|
-
end
|
39
|
-
|
40
|
-
def active_span
|
41
|
-
return unless defined?(Datadog::Tracing)
|
42
|
-
|
43
|
-
Datadog::Tracing.active_span
|
44
|
-
end
|
45
31
|
end
|
46
32
|
end
|
47
33
|
end
|
@@ -19,7 +19,7 @@ module Datadog
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def arguments
|
22
|
-
@arguments ||=
|
22
|
+
@arguments ||= build_arguments_hash
|
23
23
|
end
|
24
24
|
|
25
25
|
def queries
|
@@ -28,41 +28,77 @@ module Datadog
|
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
# This method builds an array of argument hashes for each field with arguments in the query.
|
32
|
+
#
|
33
|
+
# For example, given the following query:
|
34
|
+
# query ($postSlug: ID = "my-first-post", $withComments: Boolean!) {
|
35
|
+
# firstPost: post(slug: $postSlug) {
|
36
|
+
# title
|
37
|
+
# comments @include(if: $withComments) {
|
38
|
+
# author { name }
|
39
|
+
# content
|
40
|
+
# }
|
41
|
+
# }
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# The result would be:
|
45
|
+
# {"post"=>[{"slug"=>"my-first-post"}], "comments"=>[{"include"=>{"if"=>true}}]}
|
46
|
+
#
|
47
|
+
# Note that the `comments` "include" directive is included in the arguments list
|
48
|
+
def build_arguments_hash
|
49
|
+
queries.each_with_object({}) do |query, args_hash|
|
50
|
+
next unless query.selected_operation
|
51
|
+
|
52
|
+
arguments_from_selections(query.selected_operation.selections, query.variables, args_hash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def arguments_from_selections(selections, query_variables, args_hash)
|
57
|
+
selections.each do |selection|
|
58
|
+
# rubocop:disable Style/ClassEqualityComparison
|
59
|
+
next unless selection.class.name == Integration::AST_NODE_CLASS_NAMES[:field]
|
60
|
+
# rubocop:enable Style/ClassEqualityComparison
|
61
|
+
|
62
|
+
selection_name = selection.alias || selection.name
|
63
|
+
|
64
|
+
if !selection.arguments.empty? || !selection.directives.empty?
|
65
|
+
args_hash[selection_name] ||= []
|
66
|
+
args_hash[selection_name] <<
|
67
|
+
arguments_hash(selection.arguments, query_variables).merge!(
|
68
|
+
arguments_from_directives(selection.directives, query_variables)
|
69
|
+
)
|
46
70
|
end
|
47
|
-
next if resolver_args.empty? && resolver_dirs.empty?
|
48
71
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
72
|
+
arguments_from_selections(selection.selections, query_variables, args_hash)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def arguments_from_directives(directives, query_variables)
|
77
|
+
directives.each_with_object({}) do |directive, args_hash|
|
78
|
+
# rubocop:disable Style/ClassEqualityComparison
|
79
|
+
next unless directive.class.name == Integration::AST_NODE_CLASS_NAMES[:directive]
|
80
|
+
# rubocop:enable Style/ClassEqualityComparison
|
81
|
+
|
82
|
+
args_hash[directive.name] = arguments_hash(directive.arguments, query_variables)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def arguments_hash(arguments, query_variables)
|
87
|
+
arguments.each_with_object({}) do |argument, args_hash|
|
88
|
+
args_hash[argument.name] = argument_value(argument, query_variables)
|
53
89
|
end
|
54
|
-
args
|
55
90
|
end
|
56
91
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
92
|
+
def argument_value(argument, query_variables)
|
93
|
+
case argument.value.class.name
|
94
|
+
when Integration::AST_NODE_CLASS_NAMES[:variable_identifier]
|
95
|
+
# we need to pass query.variables here instead of query.provided_variables,
|
96
|
+
# since #provided_variables don't know anything about variable default value
|
97
|
+
query_variables[argument.value.name]
|
98
|
+
when Integration::AST_NODE_CLASS_NAMES[:input_object]
|
99
|
+
arguments_hash(argument.value.arguments, query_variables)
|
100
|
+
else
|
101
|
+
argument.value
|
66
102
|
end
|
67
103
|
end
|
68
104
|
end
|
@@ -24,27 +24,26 @@ module Datadog
|
|
24
24
|
gateway.watch('graphql.multiplex', :appsec) do |stack, gateway_multiplex|
|
25
25
|
block = false
|
26
26
|
event = nil
|
27
|
+
|
27
28
|
scope = AppSec::Scope.active_scope
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
if scope
|
31
|
+
AppSec::Reactive::Operation.new('graphql.multiplex') do |op|
|
32
|
+
GraphQL::Reactive::Multiplex.subscribe(op, scope.processor_context) do |result|
|
33
|
+
event = {
|
34
|
+
waf_result: result,
|
35
|
+
trace: scope.trace,
|
36
|
+
span: scope.service_entry_span,
|
37
|
+
multiplex: gateway_multiplex,
|
38
|
+
actions: result.actions
|
39
|
+
}
|
38
40
|
|
39
|
-
|
40
|
-
scope.
|
41
|
-
scope.service_entry_span.set_tag('appsec.event', 'true')
|
41
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
42
|
+
scope.processor_context.events << event
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
+
block = GraphQL::Reactive::Multiplex.publish(op, gateway_multiplex)
|
45
46
|
end
|
46
|
-
|
47
|
-
block = GraphQL::Reactive::Multiplex.publish(op, gateway_multiplex)
|
48
47
|
end
|
49
48
|
|
50
49
|
next [nil, [[:block, event]]] if block
|
@@ -13,6 +13,13 @@ module Datadog
|
|
13
13
|
|
14
14
|
MINIMUM_VERSION = Gem::Version.new('2.0.19')
|
15
15
|
|
16
|
+
AST_NODE_CLASS_NAMES = {
|
17
|
+
field: 'GraphQL::Language::Nodes::Field',
|
18
|
+
directive: 'GraphQL::Language::Nodes::Directive',
|
19
|
+
variable_identifier: 'GraphQL::Language::Nodes::VariableIdentifier',
|
20
|
+
input_object: 'GraphQL::Language::Nodes::InputObject',
|
21
|
+
}.freeze
|
22
|
+
|
16
23
|
register_as :graphql, auto_patch: false
|
17
24
|
|
18
25
|
def self.version
|
@@ -24,13 +31,19 @@ module Datadog
|
|
24
31
|
end
|
25
32
|
|
26
33
|
def self.compatible?
|
27
|
-
super && version >= MINIMUM_VERSION
|
34
|
+
super && version >= MINIMUM_VERSION && ast_node_classes_defined?
|
28
35
|
end
|
29
36
|
|
30
37
|
def self.auto_instrument?
|
31
38
|
true
|
32
39
|
end
|
33
40
|
|
41
|
+
def self.ast_node_classes_defined?
|
42
|
+
AST_NODE_CLASS_NAMES.all? do |_, class_name|
|
43
|
+
Object.const_defined?(class_name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
34
47
|
def patcher
|
35
48
|
Patcher
|
36
49
|
end
|
@@ -25,30 +25,17 @@ module Datadog
|
|
25
25
|
Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
|
26
26
|
arguments = values[0]
|
27
27
|
|
28
|
-
|
28
|
+
persistent_data = {
|
29
29
|
'graphql.server.all_resolvers' => arguments
|
30
30
|
}
|
31
31
|
|
32
32
|
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
33
|
-
result = waf_context.run(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
40
|
-
|
41
|
-
yield result
|
42
|
-
throw(:block, true) unless result.actions.empty?
|
43
|
-
when :ok
|
44
|
-
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
45
|
-
when :invalid_call
|
46
|
-
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
47
|
-
when :invalid_rule, :invalid_flow, :no_rule
|
48
|
-
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
49
|
-
else
|
50
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
51
|
-
end
|
33
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
34
|
+
|
35
|
+
next if result.status != :match
|
36
|
+
|
37
|
+
yield result
|
38
|
+
throw(:block, true) unless result.actions.empty?
|
52
39
|
end
|
53
40
|
end
|
54
41
|
end
|
@@ -45,14 +45,11 @@ module Datadog
|
|
45
45
|
end
|
46
46
|
|
47
47
|
result['content-type'] = request.content_type if request.content_type
|
48
|
-
|
48
|
+
# Since Rack 3.1, content-length is nil if the body is empty, but we still want to send it to the WAF.
|
49
|
+
result['content-length'] = request.content_length || '0'
|
49
50
|
result
|
50
51
|
end
|
51
52
|
|
52
|
-
def body
|
53
|
-
request.body.read.tap { request.body.rewind }
|
54
|
-
end
|
55
|
-
|
56
53
|
def url
|
57
54
|
request.url
|
58
55
|
end
|
@@ -41,11 +41,9 @@ module Datadog
|
|
41
41
|
actions: result.actions
|
42
42
|
}
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
44
|
+
# We want to keep the trace in case of security event
|
45
|
+
scope.trace.keep! if scope.trace
|
46
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
49
47
|
scope.processor_context.events << event
|
50
48
|
end
|
51
49
|
end
|
@@ -85,11 +83,9 @@ module Datadog
|
|
85
83
|
actions: result.actions
|
86
84
|
}
|
87
85
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
|
86
|
+
# We want to keep the trace in case of security event
|
87
|
+
scope.trace.keep! if scope.trace
|
88
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
93
89
|
scope.processor_context.events << event
|
94
90
|
end
|
95
91
|
end
|
@@ -129,11 +125,9 @@ module Datadog
|
|
129
125
|
actions: result.actions
|
130
126
|
}
|
131
127
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
|
128
|
+
# We want to keep the trace in case of security event
|
129
|
+
scope.trace.keep! if scope.trace
|
130
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
137
131
|
scope.processor_context.events << event
|
138
132
|
end
|
139
133
|
end
|
@@ -33,6 +33,7 @@ module Datadog
|
|
33
33
|
def self.subscribe(op, waf_context)
|
34
34
|
op.subscribe(*ADDRESSES) do |*values|
|
35
35
|
Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
|
36
|
+
|
36
37
|
headers = values[0]
|
37
38
|
headers_no_cookies = headers.dup.tap { |h| h.delete('cookie') }
|
38
39
|
uri_raw = values[1]
|
@@ -41,7 +42,7 @@ module Datadog
|
|
41
42
|
client_ip = values[4]
|
42
43
|
request_method = values[5]
|
43
44
|
|
44
|
-
|
45
|
+
persistent_data = {
|
45
46
|
'server.request.cookies' => cookies,
|
46
47
|
'server.request.query' => query,
|
47
48
|
'server.request.uri.raw' => uri_raw,
|
@@ -52,25 +53,12 @@ module Datadog
|
|
52
53
|
}
|
53
54
|
|
54
55
|
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
55
|
-
result = waf_context.run(
|
56
|
-
|
57
|
-
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
56
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
58
57
|
|
59
|
-
|
60
|
-
when :match
|
61
|
-
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
58
|
+
next if result.status != :match
|
62
59
|
|
63
|
-
|
64
|
-
|
65
|
-
when :ok
|
66
|
-
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
67
|
-
when :invalid_call
|
68
|
-
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
69
|
-
when :invalid_rule, :invalid_flow, :no_rule
|
70
|
-
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
71
|
-
else
|
72
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
73
|
-
end
|
60
|
+
yield result
|
61
|
+
throw(:block, true) unless result.actions.empty?
|
74
62
|
end
|
75
63
|
end
|
76
64
|
end
|
@@ -26,30 +26,17 @@ module Datadog
|
|
26
26
|
Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
|
27
27
|
body = values[0]
|
28
28
|
|
29
|
-
|
29
|
+
persistent_data = {
|
30
30
|
'server.request.body' => body,
|
31
31
|
}
|
32
32
|
|
33
33
|
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
34
|
-
result = waf_context.run(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
41
|
-
|
42
|
-
yield result
|
43
|
-
throw(:block, true) unless result.actions.empty?
|
44
|
-
when :ok
|
45
|
-
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
46
|
-
when :invalid_call
|
47
|
-
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
48
|
-
when :invalid_rule, :invalid_flow, :no_rule
|
49
|
-
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
50
|
-
else
|
51
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
52
|
-
end
|
34
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
35
|
+
|
36
|
+
next if result.status != :match
|
37
|
+
|
38
|
+
yield result
|
39
|
+
throw(:block, true) unless result.actions.empty?
|
53
40
|
end
|
54
41
|
end
|
55
42
|
end
|
@@ -30,32 +30,19 @@ module Datadog
|
|
30
30
|
response_headers = values[1]
|
31
31
|
response_headers_no_cookies = response_headers.dup.tap { |h| h.delete('set-cookie') }
|
32
32
|
|
33
|
-
|
33
|
+
persistent_data = {
|
34
34
|
'server.response.status' => response_status.to_s,
|
35
35
|
'server.response.headers' => response_headers,
|
36
36
|
'server.response.headers.no_cookies' => response_headers_no_cookies,
|
37
37
|
}
|
38
38
|
|
39
39
|
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
40
|
-
result = waf_context.run(
|
40
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
41
41
|
|
42
|
-
|
42
|
+
next if result.status != :match
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
47
|
-
|
48
|
-
yield result
|
49
|
-
throw(:block, true) unless result.actions.empty?
|
50
|
-
when :ok
|
51
|
-
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
52
|
-
when :invalid_call
|
53
|
-
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
54
|
-
when :invalid_rule, :invalid_flow, :no_rule
|
55
|
-
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
56
|
-
else
|
57
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
58
|
-
end
|
44
|
+
yield result
|
45
|
+
throw(:block, true) unless result.actions.empty?
|
59
46
|
end
|
60
47
|
end
|
61
48
|
end
|
@@ -150,7 +150,9 @@ module Datadog
|
|
150
150
|
|
151
151
|
return unless trace && span
|
152
152
|
|
153
|
-
span.
|
153
|
+
span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
|
154
|
+
# We add this tag when ASM standalone is enabled to make sure we don't bill APM
|
155
|
+
span.set_metric(Datadog::AppSec::Ext::TAG_APM_ENABLED, 0) if Datadog.configuration.appsec.standalone.enabled
|
154
156
|
span.set_tag('_dd.runtime_family', 'ruby')
|
155
157
|
span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
|
156
158
|
|
@@ -38,11 +38,9 @@ module Datadog
|
|
38
38
|
actions: result.actions
|
39
39
|
}
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
|
41
|
+
# We want to keep the trace in case of security event
|
42
|
+
scope.trace.keep! if scope.trace
|
43
|
+
Datadog::AppSec::Event.tag_and_keep!(scope, result)
|
46
44
|
scope.processor_context.events << event
|
47
45
|
end
|
48
46
|
end
|