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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +113 -43
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +49 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +65 -60
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  31. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  32. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  33. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  34. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  35. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
  36. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  37. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  38. data/ext/libdatadog_api/extconf.rb +108 -0
  39. data/ext/libdatadog_api/macos_development.md +26 -0
  40. data/ext/libdatadog_extconf_helpers.rb +130 -0
  41. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  42. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  44. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  45. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  46. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  47. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  48. data/lib/datadog/appsec/processor/actions.rb +1 -1
  49. data/lib/datadog/appsec/response.rb +15 -1
  50. data/lib/datadog/appsec.rb +1 -0
  51. data/lib/datadog/core/configuration/components.rb +14 -12
  52. data/lib/datadog/core/configuration/settings.rb +54 -7
  53. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  54. data/lib/datadog/core/crashtracking/component.rb +111 -0
  55. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  56. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  57. data/lib/datadog/core/telemetry/component.rb +49 -2
  58. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  59. data/lib/datadog/core/telemetry/event.rb +32 -1
  60. data/lib/datadog/core/telemetry/ext.rb +1 -0
  61. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  62. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  63. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  64. data/lib/datadog/core/telemetry/logging.rb +35 -0
  65. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  66. data/lib/datadog/kit/appsec/events.rb +2 -4
  67. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  68. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  69. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  70. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  71. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  72. data/lib/datadog/profiling/collectors/info.rb +3 -3
  73. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  74. data/lib/datadog/profiling/component.rb +69 -91
  75. data/lib/datadog/profiling/exporter.rb +3 -3
  76. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  77. data/lib/datadog/profiling/ext.rb +21 -21
  78. data/lib/datadog/profiling/flush.rb +1 -1
  79. data/lib/datadog/profiling/http_transport.rb +8 -6
  80. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  81. data/lib/datadog/profiling/preload.rb +1 -1
  82. data/lib/datadog/profiling/profiler.rb +5 -8
  83. data/lib/datadog/profiling/scheduler.rb +31 -25
  84. data/lib/datadog/profiling/tag_builder.rb +2 -2
  85. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  86. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  87. data/lib/datadog/profiling.rb +4 -5
  88. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  89. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  90. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
  91. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
  92. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  93. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  94. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  95. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  96. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  97. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  98. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  99. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  100. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  101. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  102. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  103. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  104. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  105. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  106. data/lib/datadog/tracing/span.rb +9 -2
  107. data/lib/datadog/tracing/span_event.rb +41 -0
  108. data/lib/datadog/tracing/span_operation.rb +6 -2
  109. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  110. data/lib/datadog/version.rb +1 -1
  111. metadata +28 -10
  112. data/lib/datadog/profiling/crashtracker.rb +0 -91
  113. 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.gsub(/^HTTP_/, '').downcase!.tr('_', '-')] = v if k =~ /^HTTP_/
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
@@ -11,7 +11,7 @@ module Datadog
11
11
  @actions ||= []
12
12
  end
13
13
 
14
- def fecth_configuration(action)
14
+ def fetch_configuration(action)
15
15
  actions.find { |action_configuration| action_configuration['id'] == action }
16
16
  end
17
17
 
@@ -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.fecth_configuration(action)
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)
@@ -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
- enabled = settings.telemetry.enabled
60
- if agent_settings.adapter != Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
61
- enabled = false
62
- logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
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
- Telemetry::Component.new(
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
- # Enables reporting of information when the Ruby VM crashes.
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.type :bool
450
- o.env 'DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED'
451
- o.default false
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