datadog 2.2.0 → 2.3.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 +51 -2
- data/ext/datadog_profiling_loader/extconf.rb +15 -15
- data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +113 -43
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +49 -37
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +65 -60
- data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
- data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/helpers.h +6 -17
- data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
- data/ext/datadog_profiling_native_extension/profiling.c +0 -2
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
- data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
- data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
- data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
- data/ext/libdatadog_api/extconf.rb +108 -0
- data/ext/libdatadog_api/macos_development.md +26 -0
- data/ext/libdatadog_extconf_helpers.rb +130 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/processor/actions.rb +1 -1
- data/lib/datadog/appsec/response.rb +15 -1
- data/lib/datadog/appsec.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +14 -12
- data/lib/datadog/core/configuration/settings.rb +54 -7
- data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
- data/lib/datadog/core/crashtracking/component.rb +111 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- data/lib/datadog/core/telemetry/component.rb +49 -2
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +32 -1
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
- data/lib/datadog/core/telemetry/http/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/transport.rb +38 -9
- data/lib/datadog/core/telemetry/logging.rb +35 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +3 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
- data/lib/datadog/profiling/component.rb +69 -91
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/profiling/ext.rb +21 -21
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +8 -6
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/preload.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +5 -8
- data/lib/datadog/profiling/scheduler.rb +31 -25
- data/lib/datadog/profiling/tag_builder.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +5 -5
- data/lib/datadog/profiling/tasks/setup.rb +16 -35
- data/lib/datadog/profiling.rb +4 -5
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/span.rb +9 -2
- data/lib/datadog/tracing/span_event.rb +41 -0
- data/lib/datadog/tracing/span_operation.rb +6 -2
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/version.rb +1 -1
- metadata +28 -10
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
# Contains a bunch of shared helpers that get used during building of extensions that link to libdatadog
|
8
|
+
module LibdatadogExtconfHelpers
|
9
|
+
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
10
|
+
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
11
|
+
LIBDATADOG_VERSION = '~> 11.0.0.1.0'
|
12
|
+
|
13
|
+
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
14
|
+
# libdatadog are moved after the extension gets compiled.
|
15
|
+
#
|
16
|
+
# Because the libdatadog native library is installed on a non-standard system path, in order for it to be
|
17
|
+
# found by the system dynamic linker (e.g. what takes care of dlopen(), which is used to load
|
18
|
+
# native extensions), we need to add a "runpath" -- a list of folders to search for libdatadog.
|
19
|
+
#
|
20
|
+
# This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
|
21
|
+
# Linux: e.g. `readelf -d datadog_profiling_native_extension.2.7.3_x86_64-linux.so`.
|
22
|
+
#
|
23
|
+
# In older versions of the datadog gem, we only set as runpath an absolute path to libdatadog.
|
24
|
+
# (This gets set automatically by the call
|
25
|
+
# to `pkg_config('datadog_profiling_with_rpath')` in `extconf.rb`). This worked fine as long as libdatadog was **NOT**
|
26
|
+
# moved from the folder it was present at datadog gem installation/linking time.
|
27
|
+
#
|
28
|
+
# Unfortunately, environments such as Heroku and AWS Elastic Beanstalk move gems around in the filesystem after
|
29
|
+
# installation. Thus, the profiling native extension could not be loaded in these environments
|
30
|
+
# (see https://github.com/DataDog/dd-trace-rb/issues/2067) because libdatadog could not be found.
|
31
|
+
#
|
32
|
+
# To workaround this issue, this method computes the **relative** path between the folder where
|
33
|
+
# native extensions are going to be installed and the folder where libdatadog is installed, and returns it
|
34
|
+
# to be set as an additional runpath. (Yes, you can set multiple runpath folders to be searched).
|
35
|
+
#
|
36
|
+
# This way, if both gems are moved together (and it turns out that they are in these environments),
|
37
|
+
# the relative path can still be traversed to find libdatadog.
|
38
|
+
#
|
39
|
+
# This is incredibly awful, and it's kinda bizarre how it's not possible to just find these paths at runtime
|
40
|
+
# and set them correctly; rather than needing to set stuff at linking-time and then praying to $deity that
|
41
|
+
# weird moves don't happen.
|
42
|
+
#
|
43
|
+
# As a curiosity, `LD_LIBRARY_PATH` can be used to influence the folders that get searched but **CANNOT BE
|
44
|
+
# SET DYNAMICALLY**, e.g. it needs to be set at the start of the process (Ruby VM) and thus it's not something
|
45
|
+
# we could setup when doing a `require`.
|
46
|
+
#
|
47
|
+
def self.libdatadog_folder_relative_to_native_lib_folder(
|
48
|
+
current_folder:,
|
49
|
+
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
50
|
+
)
|
51
|
+
return unless libdatadog_pkgconfig_folder
|
52
|
+
|
53
|
+
native_lib_folder = "#{current_folder}/../../lib/"
|
54
|
+
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
55
|
+
|
56
|
+
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(native_lib_folder)).to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
# In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
|
60
|
+
# only got installed into the extensions folder.
|
61
|
+
#
|
62
|
+
# But then this fix was not enough to fully get them moving because then they started to see the issue from
|
63
|
+
# https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
|
64
|
+
#
|
65
|
+
# > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
|
66
|
+
# > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
|
67
|
+
# > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
|
68
|
+
#
|
69
|
+
# The problem is that when loading the native extension from the extensions directory, the relative rpath we add
|
70
|
+
# with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
|
71
|
+
# rpath to the extensions directory.
|
72
|
+
#
|
73
|
+
# So how do we find the full path where the native extension is placed?
|
74
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
|
75
|
+
# `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
|
76
|
+
# we get that's in one of two fixed subdirectories of `Gem.dir`
|
77
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
|
78
|
+
# we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
|
79
|
+
#
|
80
|
+
# Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
|
81
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
|
82
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
|
83
|
+
#
|
84
|
+
# We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
|
85
|
+
def self.libdatadog_folder_relative_to_ruby_extensions_folders(
|
86
|
+
gem_dir: Gem.dir,
|
87
|
+
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
88
|
+
)
|
89
|
+
return unless libdatadog_pkgconfig_folder
|
90
|
+
|
91
|
+
# For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
|
92
|
+
# platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
|
93
|
+
# the Gem.dir.
|
94
|
+
expected_ruby_extensions_folders = [
|
95
|
+
"#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
|
96
|
+
"#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
|
97
|
+
]
|
98
|
+
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
99
|
+
|
100
|
+
expected_ruby_extensions_folders.map do |folder|
|
101
|
+
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# mkmf sets $PKGCONFIG after the `pkg_config` gets used in extconf.rb. When `pkg_config` is unsuccessful, we use
|
106
|
+
# this helper to decide if we can show more specific error message vs a generic "something went wrong".
|
107
|
+
def self.pkg_config_missing?(command: $PKGCONFIG) # rubocop:disable Style/GlobalVars
|
108
|
+
pkg_config_available = command && xsystem("#{command} --version")
|
109
|
+
|
110
|
+
pkg_config_available != true
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.try_loading_libdatadog
|
114
|
+
gem 'libdatadog', LIBDATADOG_VERSION
|
115
|
+
require 'libdatadog'
|
116
|
+
nil
|
117
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
118
|
+
if block_given?
|
119
|
+
yield e
|
120
|
+
else
|
121
|
+
e
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.libdatadog_issue?
|
126
|
+
try_loading_libdatadog { |_exception| return true }
|
127
|
+
Libdatadog.pkgconfig_folder.nil?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative 'gateway/multiplex'
|
5
|
+
require_relative '../../instrumentation/gateway'
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module AppSec
|
9
|
+
module Contrib
|
10
|
+
module GraphQL
|
11
|
+
# These methods will be called by the GraphQL runtime to send the variables to the WAF.
|
12
|
+
# We actually don't need to create any span/trace.
|
13
|
+
module AppSecTrace
|
14
|
+
def execute_multiplex(multiplex:)
|
15
|
+
return super unless Datadog::AppSec.enabled?
|
16
|
+
|
17
|
+
gateway_multiplex = Gateway::Multiplex.new(multiplex)
|
18
|
+
|
19
|
+
multiplex_return, multiplex_response = Instrumentation.gateway.push('graphql.multiplex', gateway_multiplex) do
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an error * the number of queries so that the entire multiplex is blocked
|
24
|
+
if multiplex_response
|
25
|
+
blocked_event = multiplex_response.find { |action, _options| action == :block }
|
26
|
+
multiplex_return = AppSec::Response.graphql_response(gateway_multiplex) if blocked_event
|
27
|
+
end
|
28
|
+
|
29
|
+
multiplex_return
|
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
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql'
|
4
|
+
|
5
|
+
require_relative '../../../instrumentation/gateway/argument'
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module AppSec
|
9
|
+
module Contrib
|
10
|
+
module GraphQL
|
11
|
+
module Gateway
|
12
|
+
# Gateway Request argument. Normalized extration of data from Rack::Request
|
13
|
+
class Multiplex < Instrumentation::Gateway::Argument
|
14
|
+
attr_reader :multiplex
|
15
|
+
|
16
|
+
def initialize(multiplex)
|
17
|
+
super()
|
18
|
+
@multiplex = multiplex
|
19
|
+
end
|
20
|
+
|
21
|
+
def arguments
|
22
|
+
@arguments ||= create_arguments_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def queries
|
26
|
+
@multiplex.queries
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def create_arguments_hash
|
32
|
+
args = {}
|
33
|
+
@multiplex.queries.each_with_index do |query, index|
|
34
|
+
resolver_args = {}
|
35
|
+
resolver_dirs = {}
|
36
|
+
selections = (query.selected_operation.selections.dup if query.selected_operation) || []
|
37
|
+
# Iterative tree traversal
|
38
|
+
while selections.any?
|
39
|
+
selection = selections.shift
|
40
|
+
set_hash_with_variables(resolver_args, selection.arguments, query.provided_variables)
|
41
|
+
selection.directives.each do |dir|
|
42
|
+
resolver_dirs[dir.name] ||= {}
|
43
|
+
set_hash_with_variables(resolver_dirs[dir.name], dir.arguments, query.provided_variables)
|
44
|
+
end
|
45
|
+
selections.concat(selection.selections)
|
46
|
+
end
|
47
|
+
next if resolver_args.empty? && resolver_dirs.empty?
|
48
|
+
|
49
|
+
args_resolver = (args[query.operation_name || "query#{index + 1}"] ||= [])
|
50
|
+
# We don't want to add empty hashes so we check again if the arguments and directives are empty
|
51
|
+
args_resolver << resolver_args unless resolver_args.empty?
|
52
|
+
args_resolver << resolver_dirs unless resolver_dirs.empty?
|
53
|
+
end
|
54
|
+
args
|
55
|
+
end
|
56
|
+
|
57
|
+
# Set the resolver hash (resolver_args and resolver_dirs) with the arguments and provided variables
|
58
|
+
def set_hash_with_variables(resolver_hash, arguments, provided_variables)
|
59
|
+
arguments.each do |arg|
|
60
|
+
resolver_hash[arg.name] =
|
61
|
+
if arg.value.is_a?(::GraphQL::Language::Nodes::VariableIdentifier)
|
62
|
+
provided_variables[arg.value.name]
|
63
|
+
else
|
64
|
+
arg.value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative '../../../instrumentation/gateway'
|
5
|
+
require_relative '../reactive/multiplex'
|
6
|
+
require_relative '../../../reactive/operation'
|
7
|
+
|
8
|
+
module Datadog
|
9
|
+
module AppSec
|
10
|
+
module Contrib
|
11
|
+
module GraphQL
|
12
|
+
module Gateway
|
13
|
+
# Watcher for Rack gateway events
|
14
|
+
module Watcher
|
15
|
+
class << self
|
16
|
+
def watch
|
17
|
+
gateway = Instrumentation.gateway
|
18
|
+
|
19
|
+
watch_multiplex(gateway)
|
20
|
+
end
|
21
|
+
|
22
|
+
# This time we don't throw but use next
|
23
|
+
def watch_multiplex(gateway = Instrumentation.gateway)
|
24
|
+
gateway.watch('graphql.multiplex', :appsec) do |stack, gateway_multiplex|
|
25
|
+
block = false
|
26
|
+
event = nil
|
27
|
+
scope = AppSec::Scope.active_scope
|
28
|
+
|
29
|
+
AppSec::Reactive::Operation.new('graphql.multiplex') do |op|
|
30
|
+
GraphQL::Reactive::Multiplex.subscribe(op, scope.processor_context) do |result|
|
31
|
+
event = {
|
32
|
+
waf_result: result,
|
33
|
+
trace: scope.trace,
|
34
|
+
span: scope.service_entry_span,
|
35
|
+
multiplex: gateway_multiplex,
|
36
|
+
actions: result.actions
|
37
|
+
}
|
38
|
+
|
39
|
+
if scope.service_entry_span
|
40
|
+
scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
|
41
|
+
scope.service_entry_span.set_tag('appsec.event', 'true')
|
42
|
+
end
|
43
|
+
|
44
|
+
scope.processor_context.events << event
|
45
|
+
end
|
46
|
+
|
47
|
+
block = GraphQL::Reactive::Multiplex.publish(op, gateway_multiplex)
|
48
|
+
end
|
49
|
+
|
50
|
+
next [nil, [[:block, event]]] if block
|
51
|
+
|
52
|
+
ret, res = stack.call(gateway_multiplex.arguments)
|
53
|
+
|
54
|
+
if event
|
55
|
+
res ||= []
|
56
|
+
res << [:monitor, event]
|
57
|
+
end
|
58
|
+
|
59
|
+
[ret, res]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../integration'
|
4
|
+
require_relative 'patcher'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module AppSec
|
8
|
+
module Contrib
|
9
|
+
module GraphQL
|
10
|
+
# Description of GraphQL integration
|
11
|
+
class Integration
|
12
|
+
include Datadog::AppSec::Contrib::Integration
|
13
|
+
|
14
|
+
MINIMUM_VERSION = Gem::Version.new('2.0.19')
|
15
|
+
|
16
|
+
register_as :graphql, auto_patch: false
|
17
|
+
|
18
|
+
def self.version
|
19
|
+
Gem.loaded_specs['graphql'] && Gem.loaded_specs['graphql'].version
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.loaded?
|
23
|
+
!defined?(::GraphQL).nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.compatible?
|
27
|
+
super && version >= MINIMUM_VERSION
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.auto_instrument?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def patcher
|
35
|
+
Patcher
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../patcher'
|
4
|
+
require_relative 'gateway/watcher'
|
5
|
+
|
6
|
+
if Gem.loaded_specs['graphql'] && Gem.loaded_specs['graphql'].version >= Gem::Version.new('2.0.19')
|
7
|
+
require_relative 'appsec_trace'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Datadog
|
11
|
+
module AppSec
|
12
|
+
module Contrib
|
13
|
+
module GraphQL
|
14
|
+
# Patcher for AppSec on GraphQL
|
15
|
+
module Patcher
|
16
|
+
include Datadog::AppSec::Contrib::Patcher
|
17
|
+
|
18
|
+
module_function
|
19
|
+
|
20
|
+
def patched?
|
21
|
+
Patcher.instance_variable_get(:@patched)
|
22
|
+
end
|
23
|
+
|
24
|
+
def target_version
|
25
|
+
Integration.version
|
26
|
+
end
|
27
|
+
|
28
|
+
def patch
|
29
|
+
Gateway::Watcher.watch
|
30
|
+
::GraphQL::Schema.trace_with(AppSecTrace)
|
31
|
+
Patcher.instance_variable_set(:@patched, true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module Contrib
|
6
|
+
module GraphQL
|
7
|
+
module Reactive
|
8
|
+
# Dispatch data from a GraphQL resolve query to the WAF context
|
9
|
+
module Multiplex
|
10
|
+
ADDRESSES = [
|
11
|
+
'graphql.server.all_resolvers'
|
12
|
+
].freeze
|
13
|
+
private_constant :ADDRESSES
|
14
|
+
|
15
|
+
def self.publish(op, gateway_multiplex)
|
16
|
+
catch(:block) do
|
17
|
+
op.publish('graphql.server.all_resolvers', gateway_multiplex.arguments)
|
18
|
+
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.subscribe(op, waf_context)
|
24
|
+
op.subscribe(*ADDRESSES) do |*values|
|
25
|
+
Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
|
26
|
+
arguments = values[0]
|
27
|
+
|
28
|
+
waf_args = {
|
29
|
+
'graphql.server.all_resolvers' => arguments
|
30
|
+
}
|
31
|
+
|
32
|
+
waf_timeout = Datadog.configuration.appsec.waf_timeout
|
33
|
+
result = waf_context.run(waf_args, waf_timeout)
|
34
|
+
|
35
|
+
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
36
|
+
|
37
|
+
case result.status
|
38
|
+
when :match
|
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
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -41,7 +41,7 @@ module Datadog
|
|
41
41
|
|
42
42
|
def headers
|
43
43
|
result = request.env.each_with_object({}) do |(k, v), h|
|
44
|
-
h[k.
|
44
|
+
h[k.delete_prefix('HTTP_').tap(&:downcase!).tap { |s| s.tr!('_', '-') }] = v if k.start_with?('HTTP_')
|
45
45
|
end
|
46
46
|
|
47
47
|
result['content-type'] = request.content_type if request.content_type
|
@@ -36,7 +36,7 @@ module Datadog
|
|
36
36
|
# I rather use break to stop the execution
|
37
37
|
next if configured_response
|
38
38
|
|
39
|
-
action_configuration = AppSec::Processor::Actions.
|
39
|
+
action_configuration = AppSec::Processor::Actions.fetch_configuration(action)
|
40
40
|
next unless action_configuration
|
41
41
|
|
42
42
|
configured_response = case action_configuration['type']
|
@@ -50,6 +50,20 @@ module Datadog
|
|
50
50
|
configured_response || default_response(env)
|
51
51
|
end
|
52
52
|
|
53
|
+
def graphql_response(gateway_multiplex)
|
54
|
+
multiplex_return = []
|
55
|
+
gateway_multiplex.queries.each do |query|
|
56
|
+
# This method is only called in places where GraphQL-Ruby is already required
|
57
|
+
query_result = ::GraphQL::Query::Result.new(
|
58
|
+
query: query,
|
59
|
+
values: JSON.parse(content('application/json'))
|
60
|
+
)
|
61
|
+
multiplex_return << query_result
|
62
|
+
end
|
63
|
+
|
64
|
+
multiplex_return
|
65
|
+
end
|
66
|
+
|
53
67
|
private
|
54
68
|
|
55
69
|
def default_response(env)
|
data/lib/datadog/appsec.rb
CHANGED
@@ -56,5 +56,6 @@ require_relative 'appsec/contrib/rack/integration'
|
|
56
56
|
require_relative 'appsec/contrib/sinatra/integration'
|
57
57
|
require_relative 'appsec/contrib/rails/integration'
|
58
58
|
require_relative 'appsec/contrib/devise/integration'
|
59
|
+
require_relative 'appsec/contrib/graphql/integration'
|
59
60
|
|
60
61
|
require_relative 'appsec/autoload'
|
@@ -13,6 +13,7 @@ require_relative '../remote/component'
|
|
13
13
|
require_relative '../../tracing/component'
|
14
14
|
require_relative '../../profiling/component'
|
15
15
|
require_relative '../../appsec/component'
|
16
|
+
require_relative '../crashtracking/component'
|
16
17
|
|
17
18
|
module Datadog
|
18
19
|
module Core
|
@@ -56,19 +57,18 @@ module Datadog
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def build_telemetry(settings, agent_settings, logger)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
Telemetry::Component.build(settings, agent_settings, logger)
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_crashtracker(settings, agent_settings, logger:)
|
64
|
+
return unless settings.crashtracking.enabled
|
65
|
+
|
66
|
+
if (libdatadog_api_failure = Datadog::Core::Crashtracking::Component::LIBDATADOG_API_FAILURE)
|
67
|
+
logger.debug("Cannot enable crashtracking: #{libdatadog_api_failure}")
|
68
|
+
return
|
63
69
|
end
|
64
70
|
|
65
|
-
|
66
|
-
enabled: enabled,
|
67
|
-
metrics_enabled: enabled && settings.telemetry.metrics_enabled,
|
68
|
-
heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
|
69
|
-
metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
|
70
|
-
dependency_collection: settings.telemetry.dependency_collection
|
71
|
-
)
|
71
|
+
Datadog::Core::Crashtracking::Component.build(settings, agent_settings, logger: logger)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -82,6 +82,7 @@ module Datadog
|
|
82
82
|
:runtime_metrics,
|
83
83
|
:telemetry,
|
84
84
|
:tracer,
|
85
|
+
:crashtracker,
|
85
86
|
:appsec
|
86
87
|
|
87
88
|
def initialize(settings)
|
@@ -95,11 +96,12 @@ module Datadog
|
|
95
96
|
|
96
97
|
@remote = Remote::Component.build(settings, agent_settings)
|
97
98
|
@tracer = self.class.build_tracer(settings, agent_settings, logger: @logger)
|
99
|
+
@crashtracker = self.class.build_crashtracker(settings, agent_settings, logger: @logger)
|
98
100
|
|
99
101
|
@profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
|
100
102
|
settings: settings,
|
101
103
|
agent_settings: agent_settings,
|
102
|
-
optional_tracer: @tracer
|
104
|
+
optional_tracer: @tracer
|
103
105
|
)
|
104
106
|
@environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
|
105
107
|
|
@@ -301,6 +301,16 @@ module Datadog
|
|
301
301
|
o.default true
|
302
302
|
end
|
303
303
|
|
304
|
+
# Can be used to enable/disable the Datadog::Profiling.allocation_count feature.
|
305
|
+
#
|
306
|
+
# Requires allocation profiling to be enabled.
|
307
|
+
#
|
308
|
+
# @default false
|
309
|
+
option :allocation_counting_enabled do |o|
|
310
|
+
o.type :bool
|
311
|
+
o.default false
|
312
|
+
end
|
313
|
+
|
304
314
|
# Can be used to enable/disable the collection of heap profiles.
|
305
315
|
#
|
306
316
|
# This feature is alpha and disabled by default
|
@@ -441,14 +451,16 @@ module Datadog
|
|
441
451
|
o.default 60
|
442
452
|
end
|
443
453
|
|
444
|
-
#
|
445
|
-
#
|
446
|
-
# @default `DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED` environment variable as a boolean,
|
447
|
-
# otherwise `false`
|
454
|
+
# DEV-3.0: Remove `experimental_crash_tracking_enabled` option
|
448
455
|
option :experimental_crash_tracking_enabled do |o|
|
449
|
-
o.
|
450
|
-
|
451
|
-
|
456
|
+
o.after_set do |_, _, precedence|
|
457
|
+
unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
|
458
|
+
Core.log_deprecation(key: :experimental_crash_tracking_enabled) do
|
459
|
+
'The profiling.advanced.experimental_crash_tracking_enabled setting has been deprecated for removal '\
|
460
|
+
'and no longer does anything. Please remove it from your Datadog.configure block.'
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
452
464
|
end
|
453
465
|
end
|
454
466
|
|
@@ -663,6 +675,24 @@ module Datadog
|
|
663
675
|
o.type :bool
|
664
676
|
end
|
665
677
|
|
678
|
+
# Enable agentless mode for telemetry: submit telemetry events directly to the intake without Datadog Agent.
|
679
|
+
#
|
680
|
+
# @return [Boolean]
|
681
|
+
# @!visibility private
|
682
|
+
option :agentless_enabled do |o|
|
683
|
+
o.type :bool
|
684
|
+
o.default false
|
685
|
+
end
|
686
|
+
|
687
|
+
# Overrides agentless telemetry URL. To be used internally for testing purposes only.
|
688
|
+
#
|
689
|
+
# @return [String]
|
690
|
+
# @!visibility private
|
691
|
+
option :agentless_url_override do |o|
|
692
|
+
o.type :string, nilable: true
|
693
|
+
o.env Core::Telemetry::Ext::ENV_AGENTLESS_URL_OVERRIDE
|
694
|
+
end
|
695
|
+
|
666
696
|
# Enable metrics collection for telemetry. Metrics collection only works when telemetry is enabled and
|
667
697
|
# metrics are enabled.
|
668
698
|
# @default `DD_TELEMETRY_METRICS_ENABLED` environment variable, otherwise `true`.
|
@@ -734,6 +764,14 @@ module Datadog
|
|
734
764
|
o.type :string, nilable: true
|
735
765
|
o.env Core::Telemetry::Ext::ENV_INSTALL_TIME
|
736
766
|
end
|
767
|
+
|
768
|
+
# Telemetry shutdown timeout in seconds
|
769
|
+
#
|
770
|
+
# @!visibility private
|
771
|
+
option :shutdown_timeout_seconds do |o|
|
772
|
+
o.type :float
|
773
|
+
o.default 1.0
|
774
|
+
end
|
737
775
|
end
|
738
776
|
|
739
777
|
# Remote configuration
|
@@ -794,6 +832,15 @@ module Datadog
|
|
794
832
|
option :service
|
795
833
|
end
|
796
834
|
|
835
|
+
settings :crashtracking do
|
836
|
+
# Enables reporting of information when Ruby VM crashes.
|
837
|
+
option :enabled do |o|
|
838
|
+
o.type :bool
|
839
|
+
o.default true
|
840
|
+
o.env 'DD_CRASHTRACKING_ENABLED'
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
797
844
|
# TODO: Tracing should manage its own settings.
|
798
845
|
# Keep this extension here for now to keep things working.
|
799
846
|
extend Datadog::Tracing::Configuration::Settings
|