datadog 2.9.0 → 2.11.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +3 -3
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +46 -6
  7. data/ext/datadog_profiling_native_extension/extconf.rb +4 -0
  8. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
  9. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
  10. data/ext/datadog_profiling_native_extension/heap_recorder.c +51 -93
  11. data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
  12. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +56 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +7 -0
  14. data/ext/datadog_profiling_native_extension/profiling.c +7 -0
  15. data/ext/datadog_profiling_native_extension/stack_recorder.c +9 -22
  16. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
  17. data/ext/libdatadog_api/crashtracker.c +4 -4
  18. data/ext/libdatadog_extconf_helpers.rb +1 -1
  19. data/lib/datadog/appsec/actions_handler.rb +27 -0
  20. data/lib/datadog/appsec/component.rb +14 -8
  21. data/lib/datadog/appsec/configuration/settings.rb +73 -11
  22. data/lib/datadog/appsec/context.rb +28 -8
  23. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +6 -2
  24. data/lib/datadog/appsec/contrib/active_record/patcher.rb +0 -3
  25. data/lib/datadog/appsec/contrib/devise/configuration.rb +76 -0
  26. data/lib/datadog/appsec/contrib/devise/event.rb +4 -7
  27. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +16 -21
  28. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +8 -15
  29. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +1 -1
  30. data/lib/datadog/appsec/contrib/devise/patcher.rb +0 -3
  31. data/lib/datadog/appsec/contrib/devise/tracking.rb +1 -1
  32. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  33. data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
  34. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +43 -0
  35. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  36. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  37. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  38. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  39. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +42 -0
  40. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
  41. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +11 -14
  42. data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
  43. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +65 -70
  44. data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
  45. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -3
  46. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +11 -22
  47. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +20 -24
  48. data/lib/datadog/appsec/contrib/rails/patcher.rb +3 -16
  49. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +38 -47
  50. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +3 -29
  51. data/lib/datadog/appsec/ext.rb +6 -1
  52. data/lib/datadog/appsec/metrics/collector.rb +38 -0
  53. data/lib/datadog/appsec/metrics/exporter.rb +35 -0
  54. data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
  55. data/lib/datadog/appsec/metrics.rb +13 -0
  56. data/lib/datadog/appsec/monitor/gateway/watcher.rb +19 -24
  57. data/lib/datadog/appsec/processor.rb +4 -3
  58. data/lib/datadog/appsec/remote.rb +4 -0
  59. data/lib/datadog/appsec/response.rb +18 -80
  60. data/lib/datadog/appsec/security_engine/result.rb +67 -0
  61. data/lib/datadog/appsec/security_engine/runner.rb +88 -0
  62. data/lib/datadog/appsec/security_engine.rb +9 -0
  63. data/lib/datadog/appsec.rb +16 -5
  64. data/lib/datadog/core/configuration/components.rb +7 -1
  65. data/lib/datadog/core/configuration/ext.rb +1 -1
  66. data/lib/datadog/core/configuration/option_definition.rb +2 -0
  67. data/lib/datadog/core/configuration/settings.rb +22 -6
  68. data/lib/datadog/core/encoding.rb +16 -0
  69. data/lib/datadog/core/environment/agent_info.rb +77 -0
  70. data/lib/datadog/core/remote/transport/http/api.rb +13 -18
  71. data/lib/datadog/core/remote/transport/http/config.rb +0 -18
  72. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -18
  73. data/lib/datadog/core/remote/transport/http.rb +7 -12
  74. data/lib/datadog/core/remote/transport/negotiation.rb +13 -1
  75. data/lib/datadog/core/telemetry/event.rb +5 -0
  76. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
  77. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +1 -1
  78. data/lib/datadog/{tracing → core}/transport/http/api/spec.rb +1 -1
  79. data/lib/datadog/{tracing → core}/transport/http/builder.rb +37 -17
  80. data/lib/datadog/core/transport/response.rb +4 -0
  81. data/lib/datadog/di/code_tracker.rb +15 -8
  82. data/lib/datadog/di/component.rb +3 -0
  83. data/lib/datadog/di/configuration/settings.rb +14 -0
  84. data/lib/datadog/di/contrib.rb +2 -0
  85. data/lib/datadog/di/logger.rb +30 -0
  86. data/lib/datadog/di/probe.rb +3 -6
  87. data/lib/datadog/di/probe_manager.rb +5 -2
  88. data/lib/datadog/di/probe_notification_builder.rb +6 -0
  89. data/lib/datadog/di/probe_notifier_worker.rb +15 -4
  90. data/lib/datadog/di/redactor.rb +0 -1
  91. data/lib/datadog/di/remote.rb +29 -8
  92. data/lib/datadog/di/utils.rb +91 -0
  93. data/lib/datadog/di.rb +3 -0
  94. data/lib/datadog/profiling/component.rb +2 -8
  95. data/lib/datadog/profiling/load_native_extension.rb +1 -33
  96. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  97. data/lib/datadog/tracing/contrib/aws/integration.rb +1 -1
  98. data/lib/datadog/tracing/contrib/extensions.rb +29 -3
  99. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  100. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
  101. data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
  102. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
  103. data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
  104. data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
  105. data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
  106. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
  107. data/lib/datadog/tracing/transport/http/api.rb +11 -2
  108. data/lib/datadog/tracing/transport/http/traces.rb +0 -3
  109. data/lib/datadog/tracing/transport/http.rb +12 -7
  110. data/lib/datadog/tracing/transport/serializable_trace.rb +8 -4
  111. data/lib/datadog/tracing/transport/traces.rb +25 -8
  112. data/lib/datadog/version.rb +1 -1
  113. metadata +51 -42
  114. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  115. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  116. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  117. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  118. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  119. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  120. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  121. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  122. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  123. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  124. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  125. data/lib/datadog/appsec/processor/context.rb +0 -107
  126. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  127. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  128. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  129. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  130. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  131. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ # This namespace contains classes related to metrics collection and exportation.
6
+ module Metrics
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative 'metrics/collector'
12
+ require_relative 'metrics/exporter'
13
+ require_relative 'metrics/telemetry'
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../instrumentation/gateway'
4
- require_relative '../../reactive/engine'
5
- require_relative '../reactive/set_user'
6
4
 
7
5
  module Datadog
8
6
  module AppSec
@@ -19,30 +17,27 @@ module Datadog
19
17
 
20
18
  def watch_user_id(gateway = Instrumentation.gateway)
21
19
  gateway.watch('identity.set_user', :appsec) do |stack, user|
22
- event = nil
23
20
  context = Datadog::AppSec.active_context
24
- engine = AppSec::Reactive::Engine.new
25
-
26
- Monitor::Reactive::SetUser.subscribe(engine, context) do |result|
27
- if result.status == :match
28
- # TODO: should this hash be an Event instance instead?
29
- event = {
30
- waf_result: result,
31
- trace: context.trace,
32
- span: context.span,
33
- user: user,
34
- actions: result.actions
35
- }
36
-
37
- # We want to keep the trace in case of security event
38
- context.trace.keep! if context.trace
39
- Datadog::AppSec::Event.tag_and_keep!(context, result)
40
- context.waf_runner.events << event
41
- end
42
- end
43
21
 
44
- block = Monitor::Reactive::SetUser.publish(engine, user)
45
- throw(Datadog::AppSec::Ext::INTERRUPT, event[:actions]) if block
22
+ persistent_data = {
23
+ 'usr.id' => user.id
24
+ }
25
+
26
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
27
+
28
+ if result.match?
29
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
30
+
31
+ context.events << {
32
+ waf_result: result,
33
+ trace: context.trace,
34
+ span: context.span,
35
+ user: user,
36
+ actions: result.actions
37
+ }
38
+
39
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
40
+ end
46
41
 
47
42
  stack.call(user)
48
43
  end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'processor/context'
3
+ require_relative 'security_engine/runner'
4
4
 
5
5
  module Datadog
6
6
  module AppSec
7
7
  # Processor integrates libddwaf into datadog/appsec
8
+ # NOTE: This class will be moved under AppSec::SecurityEngine namespace
8
9
  class Processor
9
10
  attr_reader :diagnostics, :addresses
10
11
 
@@ -29,8 +30,8 @@ module Datadog
29
30
  @handle.finalize
30
31
  end
31
32
 
32
- def new_context
33
- Context.new(@handle, telemetry: @telemetry)
33
+ def new_runner
34
+ SecurityEngine::Runner.new(@handle, telemetry: @telemetry)
34
35
  end
35
36
 
36
37
  private
@@ -104,6 +104,10 @@ module Datadog
104
104
  )
105
105
 
106
106
  Datadog::AppSec.reconfigure(ruleset: ruleset, telemetry: telemetry)
107
+
108
+ repository.contents.each do |content|
109
+ content.applied if ASM_PRODUCTS.include?(content.path.product)
110
+ end
107
111
  end
108
112
 
109
113
  [receiver]
@@ -19,100 +19,38 @@ module Datadog
19
19
  [status, headers, body]
20
20
  end
21
21
 
22
- def to_sinatra_response
23
- ::Sinatra::Response.new(body, status, headers)
24
- end
25
-
26
- def to_action_dispatch_response
27
- ::ActionDispatch::Response.new(status, headers, body)
28
- end
29
-
30
22
  class << self
31
- def negotiate(env, actions)
32
- # @type var configured_response: Response?
33
- configured_response = nil
34
- actions.each do |type, parameters|
35
- # Need to use next to make steep happy :(
36
- # I rather use break to stop the execution
37
- next if configured_response
38
-
39
- configured_response = case type
40
- when 'block_request'
41
- block_response(env, parameters)
42
- when 'redirect_request'
43
- redirect_response(env, parameters)
44
- end
45
- end
46
-
47
- configured_response || default_response(env)
48
- end
49
-
50
- def graphql_response(gateway_multiplex)
51
- multiplex_return = []
52
- gateway_multiplex.queries.each do |query|
53
- # This method is only called in places where GraphQL-Ruby is already required
54
- query_result = ::GraphQL::Query::Result.new(
55
- query: query,
56
- values: JSON.parse(content('application/json'))
57
- )
58
- multiplex_return << query_result
59
- end
23
+ def from_interrupt_params(interrupt_params, http_accept_header)
24
+ return redirect_response(interrupt_params) if interrupt_params['location']
60
25
 
61
- multiplex_return
26
+ block_response(interrupt_params, http_accept_header)
62
27
  end
63
28
 
64
29
  private
65
30
 
66
- def default_response(env)
67
- content_type = content_type(env)
68
-
69
- body = []
70
- body << content(content_type)
31
+ def block_response(interrupt_params, http_accept_header)
32
+ content_type = case interrupt_params['type']
33
+ when nil, 'auto' then content_type(http_accept_header)
34
+ else FORMAT_TO_CONTENT_TYPE.fetch(interrupt_params['type'], DEFAULT_CONTENT_TYPE)
35
+ end
71
36
 
72
37
  Response.new(
73
- status: 403,
38
+ status: interrupt_params['status_code']&.to_i || 403,
74
39
  headers: { 'Content-Type' => content_type },
75
- body: body,
40
+ body: [content(content_type)],
76
41
  )
77
42
  end
78
43
 
79
- def block_response(env, options)
80
- content_type = if options['type'] == 'auto'
81
- content_type(env)
82
- else
83
- FORMAT_TO_CONTENT_TYPE[options['type']]
84
- end
85
-
86
- body = []
87
- body << content(content_type)
44
+ def redirect_response(interrupt_params)
45
+ status_code = interrupt_params['status_code'].to_i
88
46
 
89
47
  Response.new(
90
- status: options['status_code']&.to_i || 403,
91
- headers: { 'Content-Type' => content_type },
92
- body: body,
48
+ status: (status_code >= 300 && status_code < 400 ? status_code : 303),
49
+ headers: { 'Location' => interrupt_params.fetch('location') },
50
+ body: [],
93
51
  )
94
52
  end
95
53
 
96
- def redirect_response(env, options)
97
- if options['location'] && !options['location'].empty?
98
- content_type = content_type(env)
99
-
100
- headers = {
101
- 'Content-Type' => content_type,
102
- 'Location' => options['location']
103
- }
104
-
105
- status_code = options['status_code'].to_i
106
- Response.new(
107
- status: (status_code >= 300 && status_code < 400 ? status_code : 303),
108
- headers: headers,
109
- body: [],
110
- )
111
- else
112
- default_response(env)
113
- end
114
- end
115
-
116
54
  CONTENT_TYPE_TO_FORMAT = {
117
55
  'application/json' => :json,
118
56
  'text/html' => :html,
@@ -126,10 +64,10 @@ module Datadog
126
64
 
127
65
  DEFAULT_CONTENT_TYPE = 'application/json'
128
66
 
129
- def content_type(env)
130
- return DEFAULT_CONTENT_TYPE unless env.key?('HTTP_ACCEPT')
67
+ def content_type(http_accept_header)
68
+ return DEFAULT_CONTENT_TYPE if http_accept_header.nil?
131
69
 
132
- accept_types = env['HTTP_ACCEPT'].split(',').map(&:strip)
70
+ accept_types = http_accept_header.split(',').map(&:strip)
133
71
 
134
72
  accepted = accept_types.map { |m| Utils::HTTP::MediaRange.new(m) }.sort!.reverse!
135
73
 
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module SecurityEngine
6
+ # A namespace for value-objects representing the result of WAF check.
7
+ module Result
8
+ # A generic result without indication of its type.
9
+ class Base
10
+ attr_reader :events, :actions, :derivatives, :duration_ns, :duration_ext_ns
11
+
12
+ def initialize(events:, actions:, derivatives:, timeout:, duration_ns:, duration_ext_ns:)
13
+ @events = events
14
+ @actions = actions
15
+ @derivatives = derivatives
16
+
17
+ @timeout = timeout
18
+ @duration_ns = duration_ns
19
+ @duration_ext_ns = duration_ext_ns
20
+ end
21
+
22
+ def timeout?
23
+ !!@timeout
24
+ end
25
+
26
+ def match?
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+
31
+ # A result that indicates a security rule match
32
+ class Match < Base
33
+ def match?
34
+ true
35
+ end
36
+ end
37
+
38
+ # A result that indicates a successful security rules check without a match
39
+ class Ok < Base
40
+ def match?
41
+ false
42
+ end
43
+ end
44
+
45
+ # A result that indicates an internal security library error
46
+ class Error
47
+ attr_reader :events, :actions, :derivatives, :duration_ns, :duration_ext_ns
48
+
49
+ def initialize(duration_ext_ns:)
50
+ @events = []
51
+ @actions = @derivatives = {}
52
+ @duration_ns = 0
53
+ @duration_ext_ns = duration_ext_ns
54
+ end
55
+
56
+ def timeout?
57
+ false
58
+ end
59
+
60
+ def match?
61
+ false
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'result'
4
+
5
+ module Datadog
6
+ module AppSec
7
+ module SecurityEngine
8
+ # A class that check input via security engine (WAF) and respond with result.
9
+ class Runner
10
+ SUCCESSFUL_EXECUTION_CODES = [:ok, :match].freeze
11
+
12
+ def initialize(handle, telemetry:)
13
+ @mutex = Mutex.new
14
+ @context = WAF::Context.new(handle)
15
+ @telemetry = telemetry
16
+
17
+ @debug_tag = "libddwaf:#{WAF::VERSION::STRING} method:ddwaf_run"
18
+ end
19
+
20
+ def run(persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
21
+ @mutex.lock
22
+
23
+ start_ns = Core::Utils::Time.get_time(:nanosecond)
24
+ persistent_data.reject! do |_, v|
25
+ next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
26
+
27
+ v.nil? ? true : v.empty?
28
+ end
29
+
30
+ ephemeral_data.reject! do |_, v|
31
+ next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
32
+
33
+ v.nil? ? true : v.empty?
34
+ end
35
+
36
+ _code, result = try_run(persistent_data, ephemeral_data, timeout)
37
+ stop_ns = Core::Utils::Time.get_time(:nanosecond)
38
+
39
+ report_execution(result)
40
+
41
+ unless SUCCESSFUL_EXECUTION_CODES.include?(result.status)
42
+ return Result::Error.new(duration_ext_ns: stop_ns - start_ns)
43
+ end
44
+
45
+ klass = result.status == :match ? Result::Match : Result::Ok
46
+ klass.new(
47
+ events: result.events,
48
+ actions: result.actions,
49
+ derivatives: result.derivatives,
50
+ timeout: result.timeout,
51
+ duration_ns: result.total_runtime,
52
+ duration_ext_ns: (stop_ns - start_ns)
53
+ )
54
+ ensure
55
+ @mutex.unlock
56
+ end
57
+
58
+ def finalize
59
+ @context.finalize
60
+ end
61
+
62
+ private
63
+
64
+ def try_run(persistent_data, ephemeral_data, timeout)
65
+ @context.run(persistent_data, ephemeral_data, timeout)
66
+ rescue WAF::LibDDWAF::Error => e
67
+ Datadog.logger.debug { "#{@debug_tag} execution error: #{e} backtrace: #{e.backtrace&.first(3)}" }
68
+ @telemetry.report(e, description: 'libddwaf-rb internal low-level error')
69
+
70
+ [:err_internal, WAF::Result.new(:err_internal, [], 0, false, [], [])]
71
+ end
72
+
73
+ def report_execution(result)
74
+ Datadog.logger.debug { "#{@debug_tag} execution timed out: #{result.inspect}" } if result.timeout
75
+
76
+ if SUCCESSFUL_EXECUTION_CODES.include?(result.status)
77
+ Datadog.logger.debug { "#{@debug_tag} execution result: #{result.inspect}" }
78
+ else
79
+ message = "#{@debug_tag} execution error: #{result.status.inspect}"
80
+
81
+ Datadog.logger.debug { message }
82
+ @telemetry.error(message)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ # A namespace for secutiry library we use to detect and prevent threats.
6
+ module SecurityEngine
7
+ end
8
+ end
9
+ end
@@ -14,19 +14,24 @@ module Datadog
14
14
  Datadog.configuration.appsec.enabled
15
15
  end
16
16
 
17
+ def rasp_enabled?
18
+ Datadog.configuration.appsec.rasp_enabled
19
+ end
20
+
17
21
  def active_context
18
22
  Datadog::AppSec::Context.active
19
23
  end
20
24
 
21
- def processor
22
- appsec_component = components.appsec
25
+ def telemetry
26
+ components.appsec&.telemetry
27
+ end
23
28
 
24
- appsec_component.processor if appsec_component
29
+ def processor
30
+ components.appsec&.processor
25
31
  end
26
32
 
27
33
  def reconfigure(ruleset:, telemetry:)
28
34
  appsec_component = components.appsec
29
-
30
35
  return unless appsec_component
31
36
 
32
37
  appsec_component.reconfigure(ruleset: ruleset, telemetry: telemetry)
@@ -34,12 +39,16 @@ module Datadog
34
39
 
35
40
  def reconfigure_lock(&block)
36
41
  appsec_component = components.appsec
37
-
38
42
  return unless appsec_component
39
43
 
40
44
  appsec_component.reconfigure_lock(&block)
41
45
  end
42
46
 
47
+ def api_security_enabled?
48
+ Datadog.configuration.appsec.api_security.enabled &&
49
+ Datadog.configuration.appsec.api_security.sample_rate.sample?
50
+ end
51
+
43
52
  private
44
53
 
45
54
  def components
@@ -59,5 +68,7 @@ require_relative 'appsec/contrib/rails/integration'
59
68
  require_relative 'appsec/contrib/active_record/integration'
60
69
  require_relative 'appsec/contrib/devise/integration'
61
70
  require_relative 'appsec/contrib/graphql/integration'
71
+ require_relative 'appsec/contrib/faraday/integration'
72
+ require_relative 'appsec/contrib/excon/integration'
62
73
 
63
74
  require_relative 'appsec/autoload'
@@ -16,6 +16,8 @@ require_relative '../../appsec/component'
16
16
  require_relative '../../di/component'
17
17
  require_relative '../crashtracking/component'
18
18
 
19
+ require_relative '../environment/agent_info'
20
+
19
21
  module Datadog
20
22
  module Core
21
23
  module Configuration
@@ -85,7 +87,8 @@ module Datadog
85
87
  :tracer,
86
88
  :crashtracker,
87
89
  :dynamic_instrumentation,
88
- :appsec
90
+ :appsec,
91
+ :agent_info
89
92
 
90
93
  def initialize(settings)
91
94
  @logger = self.class.build_logger(settings)
@@ -96,6 +99,9 @@ module Datadog
96
99
  # the Core resolver from within your product/component's namespace.
97
100
  agent_settings = AgentSettingsResolver.call(settings, logger: @logger)
98
101
 
102
+ # Exposes agent capability information for detection by any components
103
+ @agent_info = Core::Environment::AgentInfo.new(agent_settings)
104
+
99
105
  @telemetry = self.class.build_telemetry(settings, agent_settings, @logger)
100
106
 
101
107
  @remote = Remote::Component.build(settings, agent_settings, telemetry: telemetry)
@@ -37,7 +37,7 @@ module Datadog
37
37
  module UnixSocket
38
38
  ADAPTER = :unix
39
39
  DEFAULT_PATH = '/var/run/datadog/apm.socket'
40
- DEFAULT_TIMEOUT_SECONDS = 1
40
+ DEFAULT_TIMEOUT_SECONDS = 30
41
41
  end
42
42
  end
43
43
  end
@@ -79,6 +79,8 @@ module Datadog
79
79
  @deprecated_env = value
80
80
  end
81
81
 
82
+ # Invoked when the option is first read, and {#env} is defined.
83
+ # The block provided is only invoked if the environment variable is present (not-nil).
82
84
  def env_parser(&block)
83
85
  @env_parser = block
84
86
  end
@@ -461,15 +461,31 @@ module Datadog
461
461
  end
462
462
  end
463
463
 
464
- # Enables GVL profiling. This will show when threads are waiting for GVL in the timeline view.
465
- #
466
- # This is a preview feature and disabled by default. It requires Ruby 3.2+.
467
- #
468
- # @default `DD_PROFILING_PREVIEW_GVL_ENABLED` environment variable as a boolean, otherwise `false`
464
+ # @deprecated Use {:gvl_enabled} instead.
469
465
  option :preview_gvl_enabled do |o|
470
466
  o.type :bool
471
- o.env 'DD_PROFILING_PREVIEW_GVL_ENABLED'
472
467
  o.default false
468
+ o.after_set do |_, _, precedence|
469
+ unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
470
+ Datadog.logger.warn(
471
+ 'The profiling.advanced.preview_gvl_enabled setting has been deprecated for removal and ' \
472
+ 'no longer does anything. Please remove it from your Datadog.configure block. ' \
473
+ 'GVL profiling is now controlled by the profiling.advanced.gvl_enabled setting instead.'
474
+ )
475
+ end
476
+ end
477
+ end
478
+
479
+ # Controls GVL profiling. This will show when threads are waiting for GVL in the timeline view.
480
+ #
481
+ # This feature requires Ruby 3.2+.
482
+ #
483
+ # @default `DD_PROFILING_GVL_ENABLED` environment variable as a boolean, otherwise `true`
484
+ option :gvl_enabled do |o|
485
+ o.type :bool
486
+ o.deprecated_env 'DD_PROFILING_PREVIEW_GVL_ENABLED'
487
+ o.env 'DD_PROFILING_GVL_ENABLED'
488
+ o.default true
473
489
  end
474
490
 
475
491
  # Controls the smallest time period the profiler will report a thread waiting for the GVL.
@@ -10,6 +10,7 @@ module Datadog
10
10
  # Encoder interface that provides the logic to encode traces and service
11
11
  # @abstract
12
12
  module Encoder
13
+ # :nocov:
13
14
  def content_type
14
15
  raise NotImplementedError
15
16
  end
@@ -23,6 +24,13 @@ module Datadog
23
24
  def encode(_)
24
25
  raise NotImplementedError
25
26
  end
27
+
28
+ # Deserializes a value serialized with {#encode}.
29
+ # This method is used for debugging purposes.
30
+ def decode(_)
31
+ raise NotImplementedError
32
+ end
33
+ # :nocov:
26
34
  end
27
35
 
28
36
  # Encoder for the JSON format
@@ -41,6 +49,10 @@ module Datadog
41
49
  JSON.dump(obj)
42
50
  end
43
51
 
52
+ def decode(obj)
53
+ JSON.parse(obj)
54
+ end
55
+
44
56
  def join(encoded_data)
45
57
  "[#{encoded_data.join(',')}]"
46
58
  end
@@ -62,6 +74,10 @@ module Datadog
62
74
  MessagePack.pack(obj)
63
75
  end
64
76
 
77
+ def decode(obj)
78
+ MessagePack.unpack(obj)
79
+ end
80
+
65
81
  def join(encoded_data)
66
82
  packer = MessagePack::Packer.new
67
83
  packer.write_array_header(encoded_data.size)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Environment
6
+ # Retrieves the agent's `/info` endpoint data.
7
+ # This data can be used to determine the capabilities of the local Datadog agent.
8
+ #
9
+ # @example Example response payload
10
+ # {
11
+ # "version" : "7.57.2",
12
+ # "git_commit" : "38ba0c7",
13
+ # "endpoints" : [ "/v0.4/traces", "/v0.4/services", "/v0.7/traces", "/v0.7/config" ],
14
+ # "client_drop_p0s" : true,
15
+ # "span_meta_structs" : true,
16
+ # "long_running_spans" : true,
17
+ # "evp_proxy_allowed_headers" : [ "Content-Type", "Accept-Encoding", "Content-Encoding", "User-Agent" ],
18
+ # "config" : {
19
+ # "default_env" : "none",
20
+ # "target_tps" : 10,
21
+ # "max_eps" : 200,
22
+ # "receiver_port" : 8126,
23
+ # "receiver_socket" : "/var/run/datadog/apm.socket",
24
+ # "connection_limit" : 0,
25
+ # "receiver_timeout" : 0,
26
+ # "max_request_bytes" : 26214400,
27
+ # "statsd_port" : 8125,
28
+ # "analyzed_spans_by_service" : { },
29
+ # "obfuscation" : {
30
+ # "elastic_search" : true,
31
+ # "mongo" : true,
32
+ # "sql_exec_plan" : false,
33
+ # "sql_exec_plan_normalize" : false,
34
+ # "http" : {
35
+ # "remove_query_string" : false,
36
+ # "remove_path_digits" : false
37
+ # },
38
+ # "remove_stack_traces" : false,
39
+ # "redis" : {
40
+ # "Enabled" : true,
41
+ # "RemoveAllArgs" : false
42
+ # },
43
+ # "memcached" : {
44
+ # "Enabled" : true,
45
+ # "KeepCommand" : false
46
+ # }
47
+ # }
48
+ # },
49
+ # "peer_tags" : null
50
+ # }
51
+ #
52
+ # @see https://github.com/DataDog/datadog-agent/blob/f07df0a3c1fca0c83b5a15f553bd994091b0c8ac/pkg/trace/api/info.go#L20
53
+ class AgentInfo
54
+ attr_reader :agent_settings
55
+
56
+ def initialize(agent_settings)
57
+ @agent_settings = agent_settings
58
+ @client = Remote::Transport::HTTP.root(agent_settings: agent_settings)
59
+ end
60
+
61
+ # Fetches the information from the agent.
62
+ # @return [Datadog::Core::Remote::Transport::HTTP::Negotiation::Response] the response from the agent
63
+ # @return [nil] if an error occurred while fetching the information
64
+ def fetch
65
+ res = @client.send_info
66
+ return unless res.ok?
67
+
68
+ res
69
+ end
70
+
71
+ def ==(other)
72
+ other.is_a?(self.class) && other.agent_settings == agent_settings
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end