datadog 2.7.1 → 2.9.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +259 -132
  10. data/ext/datadog_profiling_native_extension/extconf.rb +0 -8
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
  13. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -1
  15. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
  16. data/ext/datadog_profiling_native_extension/profiling.c +10 -8
  17. data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
  18. data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -88
  19. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
  20. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
  21. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  22. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  23. data/ext/libdatadog_api/crashtracker.c +3 -0
  24. data/ext/libdatadog_extconf_helpers.rb +1 -1
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
  27. data/lib/datadog/appsec/component.rb +1 -8
  28. data/lib/datadog/appsec/context.rb +54 -0
  29. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +73 -0
  30. data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
  31. data/lib/datadog/appsec/contrib/active_record/patcher.rb +53 -0
  32. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  33. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
  35. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
  36. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
  39. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
  40. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
  41. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
  43. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
  44. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
  47. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
  48. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
  49. data/lib/datadog/appsec/event.rb +6 -6
  50. data/lib/datadog/appsec/ext.rb +3 -1
  51. data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
  52. data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
  53. data/lib/datadog/appsec/processor/context.rb +2 -2
  54. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  55. data/lib/datadog/appsec/remote.rb +1 -3
  56. data/lib/datadog/appsec/response.rb +7 -11
  57. data/lib/datadog/appsec.rb +6 -5
  58. data/lib/datadog/auto_instrument.rb +3 -0
  59. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  60. data/lib/datadog/core/configuration/components.rb +20 -2
  61. data/lib/datadog/core/configuration/settings.rb +10 -0
  62. data/lib/datadog/core/configuration.rb +10 -2
  63. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  64. data/lib/datadog/core/crashtracking/component.rb +1 -3
  65. data/lib/datadog/core/remote/client/capabilities.rb +6 -0
  66. data/lib/datadog/core/remote/client.rb +65 -59
  67. data/lib/datadog/core/telemetry/component.rb +9 -3
  68. data/lib/datadog/core/telemetry/event.rb +87 -3
  69. data/lib/datadog/core/telemetry/ext.rb +1 -0
  70. data/lib/datadog/core/telemetry/logging.rb +2 -2
  71. data/lib/datadog/core/telemetry/metric.rb +22 -0
  72. data/lib/datadog/core/telemetry/worker.rb +33 -0
  73. data/lib/datadog/di/base.rb +115 -0
  74. data/lib/datadog/di/code_tracker.rb +11 -7
  75. data/lib/datadog/di/component.rb +21 -11
  76. data/lib/datadog/di/configuration/settings.rb +11 -1
  77. data/lib/datadog/di/contrib/active_record.rb +1 -0
  78. data/lib/datadog/di/contrib/railtie.rb +15 -0
  79. data/lib/datadog/di/contrib.rb +26 -0
  80. data/lib/datadog/di/error.rb +5 -0
  81. data/lib/datadog/di/instrumenter.rb +111 -20
  82. data/lib/datadog/di/preload.rb +18 -0
  83. data/lib/datadog/di/probe.rb +11 -1
  84. data/lib/datadog/di/probe_builder.rb +1 -0
  85. data/lib/datadog/di/probe_manager.rb +8 -5
  86. data/lib/datadog/di/probe_notification_builder.rb +27 -7
  87. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  88. data/lib/datadog/di/remote.rb +124 -0
  89. data/lib/datadog/di/serializer.rb +14 -7
  90. data/lib/datadog/di/transport.rb +3 -5
  91. data/lib/datadog/di/utils.rb +7 -0
  92. data/lib/datadog/di.rb +23 -62
  93. data/lib/datadog/kit/appsec/events.rb +3 -3
  94. data/lib/datadog/kit/identity.rb +4 -4
  95. data/lib/datadog/profiling/component.rb +59 -69
  96. data/lib/datadog/profiling/http_transport.rb +1 -26
  97. data/lib/datadog/tracing/configuration/settings.rb +4 -8
  98. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  99. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  100. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  101. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  102. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  103. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  104. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  105. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  106. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +16 -4
  107. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  108. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  109. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  110. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  111. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  112. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
  113. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  114. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  115. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  116. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  117. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  118. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  119. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  120. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  121. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  122. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  123. data/lib/datadog/tracing/span.rb +12 -4
  124. data/lib/datadog/tracing/span_event.rb +123 -3
  125. data/lib/datadog/tracing/span_operation.rb +6 -0
  126. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  127. data/lib/datadog/version.rb +2 -2
  128. data/lib/datadog.rb +3 -0
  129. metadata +30 -17
  130. data/lib/datadog/appsec/processor/actions.rb +0 -49
  131. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  132. data/lib/datadog/appsec/scope.rb +0 -58
  133. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -15,18 +15,18 @@ module Datadog
15
15
  ].freeze
16
16
  private_constant :ADDRESSES
17
17
 
18
- def self.publish(op, gateway_request)
18
+ def self.publish(engine, gateway_request)
19
19
  catch(:block) do
20
20
  # params have been parsed from the request body
21
- op.publish('rails.request.body', gateway_request.parsed_body)
22
- op.publish('rails.request.route_params', gateway_request.route_params)
21
+ engine.publish('rails.request.body', gateway_request.parsed_body)
22
+ engine.publish('rails.request.route_params', gateway_request.route_params)
23
23
 
24
24
  nil
25
25
  end
26
26
  end
27
27
 
28
- def self.subscribe(op, waf_context)
29
- op.subscribe(*ADDRESSES) do |*values|
28
+ def self.subscribe(engine, context)
29
+ engine.subscribe(*ADDRESSES) do |*values|
30
30
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
31
31
  body = values[0]
32
32
  path_params = values[1]
@@ -37,7 +37,7 @@ module Datadog
37
37
  }
38
38
 
39
39
  waf_timeout = Datadog.configuration.appsec.waf_timeout
40
- result = waf_context.run(persistent_data, {}, waf_timeout)
40
+ result = context.run_waf(persistent_data, {}, waf_timeout)
41
41
 
42
42
  next if result.status != :match
43
43
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../../instrumentation/gateway'
4
- require_relative '../../../reactive/operation'
4
+ require_relative '../../../reactive/engine'
5
5
  require_relative '../../rack/reactive/request_body'
6
6
  require_relative '../reactive/routed'
7
7
  require_relative '../../../event'
@@ -23,85 +23,63 @@ module Datadog
23
23
 
24
24
  def watch_request_dispatch(gateway = Instrumentation.gateway)
25
25
  gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
26
- block = false
27
-
28
26
  event = nil
29
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
30
-
31
- AppSec::Reactive::Operation.new('sinatra.request.dispatch') do |op|
32
- Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result|
33
- if result.status == :match
34
- # TODO: should this hash be an Event instance instead?
35
- event = {
36
- waf_result: result,
37
- trace: scope.trace,
38
- span: scope.service_entry_span,
39
- request: gateway_request,
40
- actions: result.actions
41
- }
42
-
43
- # We want to keep the trace in case of security event
44
- scope.trace.keep! if scope.trace
45
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
46
- scope.processor_context.events << event
47
- end
27
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
28
+ engine = AppSec::Reactive::Engine.new
29
+
30
+ Rack::Reactive::RequestBody.subscribe(engine, context) do |result|
31
+ if result.status == :match
32
+ # TODO: should this hash be an Event instance instead?
33
+ event = {
34
+ waf_result: result,
35
+ trace: context.trace,
36
+ span: context.span,
37
+ request: gateway_request,
38
+ actions: result.actions
39
+ }
40
+
41
+ # We want to keep the trace in case of security event
42
+ context.trace.keep! if context.trace
43
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
44
+ context.waf_runner.events << event
48
45
  end
49
-
50
- block = Rack::Reactive::RequestBody.publish(op, gateway_request)
51
46
  end
52
47
 
48
+ block = Rack::Reactive::RequestBody.publish(engine, gateway_request)
53
49
  next [nil, [[:block, event]]] if block
54
50
 
55
- ret, res = stack.call(gateway_request.request)
56
-
57
- if event
58
- res ||= []
59
- res << [:monitor, event]
60
- end
61
-
62
- [ret, res]
51
+ stack.call(gateway_request.request)
63
52
  end
64
53
  end
65
54
 
66
55
  def watch_request_routed(gateway = Instrumentation.gateway)
67
56
  gateway.watch('sinatra.request.routed', :appsec) do |stack, (gateway_request, gateway_route_params)|
68
- block = false
69
-
70
57
  event = nil
71
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
72
-
73
- AppSec::Reactive::Operation.new('sinatra.request.routed') do |op|
74
- Sinatra::Reactive::Routed.subscribe(op, scope.processor_context) do |result|
75
- if result.status == :match
76
- # TODO: should this hash be an Event instance instead?
77
- event = {
78
- waf_result: result,
79
- trace: scope.trace,
80
- span: scope.service_entry_span,
81
- request: gateway_request,
82
- actions: result.actions
83
- }
84
-
85
- # We want to keep the trace in case of security event
86
- scope.trace.keep! if scope.trace
87
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
88
- scope.processor_context.events << event
89
- end
58
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
59
+ engine = AppSec::Reactive::Engine.new
60
+
61
+ Sinatra::Reactive::Routed.subscribe(engine, context) do |result|
62
+ if result.status == :match
63
+ # TODO: should this hash be an Event instance instead?
64
+ event = {
65
+ waf_result: result,
66
+ trace: context.trace,
67
+ span: context.span,
68
+ request: gateway_request,
69
+ actions: result.actions
70
+ }
71
+
72
+ # We want to keep the trace in case of security event
73
+ context.trace.keep! if context.trace
74
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
75
+ context.waf_runner.events << event
90
76
  end
91
-
92
- block = Sinatra::Reactive::Routed.publish(op, [gateway_request, gateway_route_params])
93
77
  end
94
78
 
79
+ block = Sinatra::Reactive::Routed.publish(engine, [gateway_request, gateway_route_params])
95
80
  next [nil, [[:block, event]]] if block
96
81
 
97
- ret, res = stack.call(gateway_request.request)
98
-
99
- if event
100
- res ||= []
101
- res << [:monitor, event]
102
- end
103
-
104
- [ret, res]
82
+ stack.call(gateway_request.request)
105
83
  end
106
84
  end
107
85
  end
@@ -54,7 +54,7 @@ module Datadog
54
54
  def dispatch!
55
55
  env = @request.env
56
56
 
57
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
57
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
58
58
 
59
59
  return super unless context
60
60
 
@@ -86,7 +86,7 @@ module Datadog
86
86
  def process_route(*)
87
87
  env = @request.env
88
88
 
89
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
89
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
90
90
 
91
91
  return super unless context
92
92
 
@@ -12,18 +12,18 @@ module Datadog
12
12
  ].freeze
13
13
  private_constant :ADDRESSES
14
14
 
15
- def self.publish(op, data)
15
+ def self.publish(engine, data)
16
16
  _request, route_params = data
17
17
 
18
18
  catch(:block) do
19
- op.publish('sinatra.request.route_params', route_params.params)
19
+ engine.publish('sinatra.request.route_params', route_params.params)
20
20
 
21
21
  nil
22
22
  end
23
23
  end
24
24
 
25
- def self.subscribe(op, waf_context)
26
- op.subscribe(*ADDRESSES) do |*values|
25
+ def self.subscribe(engine, context)
26
+ engine.subscribe(*ADDRESSES) do |*values|
27
27
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
28
28
  path_params = values[0]
29
29
 
@@ -32,7 +32,7 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog.configuration.appsec.waf_timeout
35
- result = waf_context.run(persistent_data, {}, waf_timeout)
35
+ result = context.run_waf(persistent_data, {}, waf_timeout)
36
36
 
37
37
  next if result.status != :match
38
38
 
@@ -137,16 +137,16 @@ module Datadog
137
137
  end
138
138
  # rubocop:enable Metrics/MethodLength
139
139
 
140
- def tag_and_keep!(scope, waf_result)
140
+ def tag_and_keep!(context, waf_result)
141
141
  # We want to keep the trace in case of security event
142
- scope.trace.keep! if scope.trace
142
+ context.trace.keep! if context.trace
143
143
 
144
- if scope.service_entry_span
145
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if waf_result.actions.include?('block')
146
- scope.service_entry_span.set_tag('appsec.event', 'true')
144
+ if context.span
145
+ context.span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
146
+ context.span.set_tag('appsec.event', 'true')
147
147
  end
148
148
 
149
- add_distributed_tags(scope.trace)
149
+ add_distributed_tags(context.trace)
150
150
  end
151
151
 
152
152
  private
@@ -3,8 +3,10 @@
3
3
  module Datadog
4
4
  module AppSec
5
5
  module Ext
6
+ RASP_SQLI = :sql_injection
6
7
  INTERRUPT = :datadog_appsec_interrupt
7
- SCOPE_KEY = 'datadog.appsec.scope'
8
+ CONTEXT_KEY = 'datadog.appsec.context'
9
+ ACTIVE_CONTEXT_KEY = :datadog_appsec_active_context
8
10
 
9
11
  TAG_APPSEC_ENABLED = '_dd.appsec.enabled'
10
12
  TAG_APM_ENABLED = '_dd.apm.enabled'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../instrumentation/gateway'
4
- require_relative '../../reactive/operation'
4
+ require_relative '../../reactive/engine'
5
5
  require_relative '../reactive/set_user'
6
6
 
7
7
  module Datadog
@@ -19,42 +19,32 @@ module Datadog
19
19
 
20
20
  def watch_user_id(gateway = Instrumentation.gateway)
21
21
  gateway.watch('identity.set_user', :appsec) do |stack, user|
22
- block = false
23
22
  event = nil
24
- scope = Datadog::AppSec.active_scope
25
-
26
- AppSec::Reactive::Operation.new('identity.set_user') do |op|
27
- Monitor::Reactive::SetUser.subscribe(op, scope.processor_context) do |result|
28
- if result.status == :match
29
- # TODO: should this hash be an Event instance instead?
30
- event = {
31
- waf_result: result,
32
- trace: scope.trace,
33
- span: scope.service_entry_span,
34
- user: user,
35
- actions: result.actions
36
- }
37
-
38
- # We want to keep the trace in case of security event
39
- scope.trace.keep! if scope.trace
40
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
41
- scope.processor_context.events << event
42
- end
23
+ 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
43
41
  end
44
-
45
- block = Monitor::Reactive::SetUser.publish(op, user)
46
42
  end
47
43
 
48
- throw(Datadog::AppSec::Ext::INTERRUPT, [nil, [[:block, event]]]) if block
49
-
50
- ret, res = stack.call(user)
51
-
52
- if event
53
- res ||= []
54
- res << [:monitor, event]
55
- end
44
+ block = Monitor::Reactive::SetUser.publish(engine, user)
45
+ throw(Datadog::AppSec::Ext::INTERRUPT, event[:actions]) if block
56
46
 
57
- [ret, res]
47
+ stack.call(user)
58
48
  end
59
49
  end
60
50
  end
@@ -11,16 +11,16 @@ module Datadog
11
11
  ].freeze
12
12
  private_constant :ADDRESSES
13
13
 
14
- def self.publish(op, user)
14
+ def self.publish(engine, user)
15
15
  catch(:block) do
16
- op.publish('usr.id', user.id)
16
+ engine.publish('usr.id', user.id)
17
17
 
18
18
  nil
19
19
  end
20
20
  end
21
21
 
22
- def self.subscribe(op, waf_context)
23
- op.subscribe(*ADDRESSES) do |*values|
22
+ def self.subscribe(engine, context)
23
+ engine.subscribe(*ADDRESSES) do |*values|
24
24
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
25
25
 
26
26
  user_id = values[0]
@@ -30,7 +30,7 @@ module Datadog
30
30
  }
31
31
 
32
32
  waf_timeout = Datadog.configuration.appsec.waf_timeout
33
- result = waf_context.run(persistent_data, {}, waf_timeout)
33
+ result = context.run_waf(persistent_data, {}, waf_timeout)
34
34
 
35
35
  next if result.status != :match
36
36
 
@@ -19,7 +19,7 @@ module Datadog
19
19
  @events = []
20
20
  @run_mutex = Mutex.new
21
21
 
22
- @libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING}"
22
+ @libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING} method:ddwaf_run"
23
23
  end
24
24
 
25
25
  def run(persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
@@ -79,7 +79,7 @@ module Datadog
79
79
  @context.run(persistent_data, ephemeral_data, timeout)
80
80
  rescue WAF::LibDDWAF::Error => e
81
81
  Datadog.logger.debug { "#{@libddwaf_debug_tag} execution error: #{e} backtrace: #{e.backtrace&.first(3)}" }
82
- @telemetry.report(e, description: 'libddwaf internal low-level error')
82
+ @telemetry.report(e, description: 'libddwaf-rb internal low-level error')
83
83
 
84
84
  [:err_internal, WAF::Result.new(:err_internal, [], 0.0, false, [], [])]
85
85
  end
@@ -74,9 +74,6 @@ module Datadog
74
74
  when Hash
75
75
  pass = ip_passlist[:pass]
76
76
  monitor = ip_passlist[:monitor]
77
- else
78
- pass = []
79
- monitor = []
80
77
  end
81
78
 
82
79
  exclusions = []
@@ -67,7 +67,6 @@ module Datadog
67
67
  data = []
68
68
  overrides = []
69
69
  exclusions = []
70
- actions = []
71
70
 
72
71
  repository.contents.each do |content|
73
72
  parsed_content = parse_content(content)
@@ -81,7 +80,6 @@ module Datadog
81
80
  overrides << parsed_content['rules_override'] if parsed_content['rules_override']
82
81
  exclusions << parsed_content['exclusions'] if parsed_content['exclusions']
83
82
  custom_rules << parsed_content['custom_rules'] if parsed_content['custom_rules']
84
- actions.concat(parsed_content['actions']) if parsed_content['actions']
85
83
  end
86
84
  end
87
85
 
@@ -105,7 +103,7 @@ module Datadog
105
103
  telemetry: telemetry
106
104
  )
107
105
 
108
- Datadog::AppSec.reconfigure(ruleset: ruleset, actions: actions, telemetry: telemetry)
106
+ Datadog::AppSec.reconfigure(ruleset: ruleset, telemetry: telemetry)
109
107
  end
110
108
 
111
109
  [receiver]
@@ -31,19 +31,16 @@ module Datadog
31
31
  def negotiate(env, actions)
32
32
  # @type var configured_response: Response?
33
33
  configured_response = nil
34
- actions.each do |action|
34
+ actions.each do |type, parameters|
35
35
  # Need to use next to make steep happy :(
36
36
  # I rather use break to stop the execution
37
37
  next if configured_response
38
38
 
39
- action_configuration = AppSec::Processor::Actions.fetch_configuration(action)
40
- next unless action_configuration
41
-
42
- configured_response = case action_configuration['type']
39
+ configured_response = case type
43
40
  when 'block_request'
44
- block_response(env, action_configuration['parameters'])
41
+ block_response(env, parameters)
45
42
  when 'redirect_request'
46
- redirect_response(env, action_configuration['parameters'])
43
+ redirect_response(env, parameters)
47
44
  end
48
45
  end
49
46
 
@@ -90,7 +87,7 @@ module Datadog
90
87
  body << content(content_type)
91
88
 
92
89
  Response.new(
93
- status: options['status_code'] || 403,
90
+ status: options['status_code']&.to_i || 403,
94
91
  headers: { 'Content-Type' => content_type },
95
92
  body: body,
96
93
  )
@@ -100,15 +97,14 @@ module Datadog
100
97
  if options['location'] && !options['location'].empty?
101
98
  content_type = content_type(env)
102
99
 
103
- status = options['status_code'] >= 300 && options['status_code'] < 400 ? options['status_code'] : 303
104
-
105
100
  headers = {
106
101
  'Content-Type' => content_type,
107
102
  'Location' => options['location']
108
103
  }
109
104
 
105
+ status_code = options['status_code'].to_i
110
106
  Response.new(
111
- status: status,
107
+ status: (status_code >= 300 && status_code < 400 ? status_code : 303),
112
108
  headers: headers,
113
109
  body: [],
114
110
  )
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'appsec/configuration'
4
4
  require_relative 'appsec/extensions'
5
- require_relative 'appsec/scope'
5
+ require_relative 'appsec/context'
6
6
  require_relative 'appsec/ext'
7
7
  require_relative 'appsec/utils'
8
8
 
@@ -14,8 +14,8 @@ module Datadog
14
14
  Datadog.configuration.appsec.enabled
15
15
  end
16
16
 
17
- def active_scope
18
- Datadog::AppSec::Scope.active_scope
17
+ def active_context
18
+ Datadog::AppSec::Context.active
19
19
  end
20
20
 
21
21
  def processor
@@ -24,12 +24,12 @@ module Datadog
24
24
  appsec_component.processor if appsec_component
25
25
  end
26
26
 
27
- def reconfigure(ruleset:, actions:, telemetry:)
27
+ def reconfigure(ruleset:, telemetry:)
28
28
  appsec_component = components.appsec
29
29
 
30
30
  return unless appsec_component
31
31
 
32
- appsec_component.reconfigure(ruleset: ruleset, actions: actions, telemetry: telemetry)
32
+ appsec_component.reconfigure(ruleset: ruleset, telemetry: telemetry)
33
33
  end
34
34
 
35
35
  def reconfigure_lock(&block)
@@ -56,6 +56,7 @@ end
56
56
  require_relative 'appsec/contrib/rack/integration'
57
57
  require_relative 'appsec/contrib/sinatra/integration'
58
58
  require_relative 'appsec/contrib/rails/integration'
59
+ require_relative 'appsec/contrib/active_record/integration'
59
60
  require_relative 'appsec/contrib/devise/integration'
60
61
  require_relative 'appsec/contrib/graphql/integration'
61
62
 
@@ -6,6 +6,9 @@
6
6
  require_relative '../datadog'
7
7
  require_relative 'tracing/contrib/auto_instrument'
8
8
 
9
+ # DI is not loaded on Ruby 2.5 and JRuby
10
+ Datadog::DI::Contrib.load_now_or_later if defined?(Datadog::DI::Contrib)
11
+
9
12
  Datadog::Profiling.start_if_enabled
10
13
 
11
14
  module Datadog
@@ -19,21 +19,49 @@ module Datadog
19
19
  # Whenever there is a conflict (different configurations are provided in different orders), it MUST warn the users
20
20
  # about it and pick a value based on the following priority: code > environment variable > defaults.
21
21
  class AgentSettingsResolver
22
- AgentSettings = Struct.new(
23
- :adapter,
24
- :ssl,
25
- :hostname,
26
- :port,
27
- :uds_path,
28
- :timeout_seconds,
29
- keyword_init: true
30
- ) do
31
- def initialize(*)
32
- super
22
+ # Immutable container for the resulting settings
23
+ class AgentSettings
24
+ attr_reader :adapter, :ssl, :hostname, :port, :uds_path, :timeout_seconds
25
+
26
+ def initialize(adapter: nil, ssl: nil, hostname: nil, port: nil, uds_path: nil, timeout_seconds: nil)
27
+ @adapter = adapter
28
+ @ssl = ssl
29
+ @hostname = hostname
30
+ @port = port
31
+ @uds_path = uds_path
32
+ @timeout_seconds = timeout_seconds
33
33
  freeze
34
34
  end
35
+
36
+ def url
37
+ case adapter
38
+ when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
39
+ hostname = self.hostname
40
+ hostname = "[#{hostname}]" if hostname =~ IPV6_REGEXP
41
+ "#{ssl ? 'https' : 'http'}://#{hostname}:#{port}/"
42
+ when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
43
+ "unix://#{uds_path}"
44
+ else
45
+ raise ArgumentError, "Unexpected adapter: #{adapter}"
46
+ end
47
+ end
48
+
49
+ def ==(other)
50
+ self.class == other.class &&
51
+ adapter == other.adapter &&
52
+ ssl == other.ssl &&
53
+ hostname == other.hostname &&
54
+ port == other.port &&
55
+ uds_path == other.uds_path &&
56
+ timeout_seconds == other.timeout_seconds
57
+ end
35
58
  end
36
59
 
60
+ # IPv6 regular expression from
61
+ # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
62
+ # Does not match IPv4 addresses.
63
+ IPV6_REGEXP = /\A(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\z)/.freeze # rubocop:disable Layout/LineLength
64
+
37
65
  def self.call(settings, logger: Datadog.logger)
38
66
  new(settings, logger: logger).send(:call)
39
67
  end
@@ -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 '../../di/component'
16
17
  require_relative '../crashtracking/component'
17
18
 
18
19
  module Datadog
@@ -83,6 +84,7 @@ module Datadog
83
84
  :telemetry,
84
85
  :tracer,
85
86
  :crashtracker,
87
+ :dynamic_instrumentation,
86
88
  :appsec
87
89
 
88
90
  def initialize(settings)
@@ -103,19 +105,22 @@ module Datadog
103
105
  @profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
104
106
  settings: settings,
105
107
  agent_settings: agent_settings,
106
- optional_tracer: @tracer
108
+ optional_tracer: @tracer,
109
+ logger: @logger,
107
110
  )
108
111
  @environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
109
112
 
110
113
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings)
111
114
  @health_metrics = self.class.build_health_metrics(settings)
112
115
  @appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
116
+ @dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
117
+ @environment_logger_extra[:dynamic_instrumentation_enabled] = !!@dynamic_instrumentation
113
118
 
114
119
  self.class.configure_tracing(settings)
115
120
  end
116
121
 
117
122
  # Starts up components
118
- def startup!(settings)
123
+ def startup!(settings, old_state: nil)
119
124
  if settings.profiling.enabled
120
125
  if profiler
121
126
  profiler.start
@@ -126,6 +131,16 @@ module Datadog
126
131
  end
127
132
  end
128
133
 
134
+ if settings.remote.enabled && old_state&.[](:remote_started)
135
+ # The library was reconfigured and previously it already started
136
+ # the remote component (i.e., it received at least one request
137
+ # through the installed Rack middleware which started the remote).
138
+ # If the new configuration also has remote enabled, start the
139
+ # new remote right away.
140
+ # remote should always be not nil here but steep doesn't know this.
141
+ remote&.start
142
+ end
143
+
129
144
  Core::Diagnostics::EnvironmentLogger.collect_and_log!(@environment_logger_extra)
130
145
  end
131
146
 
@@ -136,6 +151,9 @@ module Datadog
136
151
  # Shutdown remote configuration
137
152
  remote.shutdown! if remote
138
153
 
154
+ # Shutdown DI after remote, since remote config triggers DI operations.
155
+ dynamic_instrumentation&.shutdown!
156
+
139
157
  # Decommission AppSec
140
158
  appsec.shutdown! if appsec
141
159