datadog 2.8.0 → 2.10.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -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 +66 -56
  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_stack.h +2 -2
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +221 -127
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +50 -92
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
  13. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
  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 +63 -76
  19. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
  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/lib/datadog/appsec/actions_handler.rb +27 -0
  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 +14 -8
  28. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  29. data/lib/datadog/appsec/context.rb +74 -0
  30. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +12 -8
  31. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  32. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  33. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +20 -30
  35. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +6 -6
  36. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +67 -96
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
  39. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +6 -6
  40. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +7 -7
  41. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -60
  43. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +23 -33
  44. data/lib/datadog/appsec/contrib/rails/patcher.rb +4 -14
  45. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +7 -7
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +45 -65
  47. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -28
  48. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +6 -6
  49. data/lib/datadog/appsec/event.rb +6 -6
  50. data/lib/datadog/appsec/ext.rb +8 -1
  51. data/lib/datadog/appsec/metrics/collector.rb +38 -0
  52. data/lib/datadog/appsec/metrics/exporter.rb +35 -0
  53. data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
  54. data/lib/datadog/appsec/metrics.rb +13 -0
  55. data/lib/datadog/appsec/monitor/gateway/watcher.rb +23 -32
  56. data/lib/datadog/appsec/monitor/reactive/set_user.rb +6 -6
  57. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  58. data/lib/datadog/appsec/processor.rb +4 -3
  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 +17 -8
  64. data/lib/datadog/auto_instrument.rb +3 -0
  65. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  66. data/lib/datadog/core/configuration/components.rb +4 -2
  67. data/lib/datadog/core/configuration.rb +1 -1
  68. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  69. data/lib/datadog/core/crashtracking/component.rb +1 -3
  70. data/lib/datadog/core/telemetry/event.rb +87 -3
  71. data/lib/datadog/core/telemetry/logging.rb +2 -2
  72. data/lib/datadog/core/telemetry/metric.rb +22 -0
  73. data/lib/datadog/core/telemetry/worker.rb +33 -0
  74. data/lib/datadog/di/base.rb +115 -0
  75. data/lib/datadog/di/code_tracker.rb +7 -4
  76. data/lib/datadog/di/component.rb +19 -11
  77. data/lib/datadog/di/configuration/settings.rb +11 -1
  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 +39 -18
  82. data/lib/datadog/di/{init.rb → preload.rb} +2 -4
  83. data/lib/datadog/di/probe_manager.rb +4 -4
  84. data/lib/datadog/di/probe_notification_builder.rb +22 -2
  85. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  86. data/lib/datadog/di/redactor.rb +0 -1
  87. data/lib/datadog/di/remote.rb +30 -9
  88. data/lib/datadog/di/transport.rb +2 -4
  89. data/lib/datadog/di.rb +5 -108
  90. data/lib/datadog/kit/appsec/events.rb +3 -3
  91. data/lib/datadog/kit/identity.rb +4 -4
  92. data/lib/datadog/profiling/component.rb +55 -53
  93. data/lib/datadog/profiling/http_transport.rb +1 -26
  94. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  95. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  96. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  97. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  98. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  99. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  100. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  101. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  102. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  103. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  104. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  105. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  106. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  107. data/lib/datadog/tracing/contrib/extensions.rb +15 -3
  108. data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
  109. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  110. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  111. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  112. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  113. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  114. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  115. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  116. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  117. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  118. data/lib/datadog/tracing/span.rb +12 -4
  119. data/lib/datadog/tracing/span_event.rb +123 -3
  120. data/lib/datadog/tracing/span_operation.rb +6 -0
  121. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  122. data/lib/datadog/version.rb +1 -1
  123. metadata +40 -17
  124. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  125. data/lib/datadog/appsec/processor/context.rb +0 -107
  126. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  127. data/lib/datadog/appsec/scope.rb +0 -58
  128. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -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/request'
6
6
  require_relative '../reactive/request_body'
7
7
  require_relative '../reactive/response'
@@ -25,126 +25,97 @@ module Datadog
25
25
 
26
26
  def watch_request(gateway = Instrumentation.gateway)
27
27
  gateway.watch('rack.request', :appsec) do |stack, gateway_request|
28
- block = false
29
28
  event = nil
30
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
31
-
32
- AppSec::Reactive::Operation.new('rack.request') do |op|
33
- Rack::Reactive::Request.subscribe(op, scope.processor_context) do |result|
34
- if result.status == :match
35
- # TODO: should this hash be an Event instance instead?
36
- event = {
37
- waf_result: result,
38
- trace: scope.trace,
39
- span: scope.service_entry_span,
40
- request: gateway_request,
41
- actions: result.actions
42
- }
43
-
44
- # We want to keep the trace in case of security event
45
- scope.trace.keep! if scope.trace
46
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
47
- scope.processor_context.events << event
48
- end
29
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
30
+ engine = AppSec::Reactive::Engine.new
31
+
32
+ Rack::Reactive::Request.subscribe(engine, context) do |result|
33
+ if result.match?
34
+ # TODO: should this hash be an Event instance instead?
35
+ event = {
36
+ waf_result: result,
37
+ trace: context.trace,
38
+ span: context.span,
39
+ request: gateway_request,
40
+ actions: result.actions
41
+ }
42
+
43
+ # We want to keep the trace in case of security event
44
+ context.trace.keep! if context.trace
45
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
46
+ context.events << event
47
+
48
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
49
49
  end
50
-
51
- block = Rack::Reactive::Request.publish(op, gateway_request)
52
50
  end
53
51
 
54
- next [nil, [[:block, event]]] if block
55
-
56
- ret, res = stack.call(gateway_request.request)
57
-
58
- if event
59
- res ||= []
60
- res << [:monitor, event]
61
- end
52
+ Rack::Reactive::Request.publish(engine, gateway_request)
62
53
 
63
- [ret, res]
54
+ stack.call(gateway_request.request)
64
55
  end
65
56
  end
66
57
 
67
58
  def watch_response(gateway = Instrumentation.gateway)
68
59
  gateway.watch('rack.response', :appsec) do |stack, gateway_response|
69
- block = false
70
-
71
60
  event = nil
72
- scope = gateway_response.scope
73
-
74
- AppSec::Reactive::Operation.new('rack.response') do |op|
75
- Rack::Reactive::Response.subscribe(op, scope.processor_context) do |result|
76
- if result.status == :match
77
- # TODO: should this hash be an Event instance instead?
78
- event = {
79
- waf_result: result,
80
- trace: scope.trace,
81
- span: scope.service_entry_span,
82
- response: gateway_response,
83
- actions: result.actions
84
- }
85
-
86
- # We want to keep the trace in case of security event
87
- scope.trace.keep! if scope.trace
88
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
89
- scope.processor_context.events << event
90
- end
61
+ context = gateway_response.context
62
+ engine = AppSec::Reactive::Engine.new
63
+
64
+ Rack::Reactive::Response.subscribe(engine, context) do |result|
65
+ if result.match?
66
+ # TODO: should this hash be an Event instance instead?
67
+ event = {
68
+ waf_result: result,
69
+ trace: context.trace,
70
+ span: context.span,
71
+ response: gateway_response,
72
+ actions: result.actions
73
+ }
74
+
75
+ # We want to keep the trace in case of security event
76
+ context.trace.keep! if context.trace
77
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
78
+ context.events << event
79
+
80
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
91
81
  end
92
-
93
- block = Rack::Reactive::Response.publish(op, gateway_response)
94
82
  end
95
83
 
96
- next [nil, [[:block, event]]] if block
97
-
98
- ret, res = stack.call(gateway_response.response)
84
+ Rack::Reactive::Response.publish(engine, gateway_response)
99
85
 
100
- if event
101
- res ||= []
102
- res << [:monitor, event]
103
- end
104
-
105
- [ret, res]
86
+ stack.call(gateway_response.response)
106
87
  end
107
88
  end
108
89
 
109
90
  def watch_request_body(gateway = Instrumentation.gateway)
110
91
  gateway.watch('rack.request.body', :appsec) do |stack, gateway_request|
111
- block = false
112
-
113
92
  event = nil
114
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
115
-
116
- AppSec::Reactive::Operation.new('rack.request.body') do |op|
117
- Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result|
118
- if result.status == :match
119
- # TODO: should this hash be an Event instance instead?
120
- event = {
121
- waf_result: result,
122
- trace: scope.trace,
123
- span: scope.service_entry_span,
124
- request: gateway_request,
125
- actions: result.actions
126
- }
127
-
128
- # We want to keep the trace in case of security event
129
- scope.trace.keep! if scope.trace
130
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
131
- scope.processor_context.events << event
132
- end
93
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
94
+ engine = AppSec::Reactive::Engine.new
95
+
96
+ Rack::Reactive::RequestBody.subscribe(engine, context) do |result|
97
+ if result.match?
98
+ # TODO: should this hash be an Event instance instead?
99
+ event = {
100
+ waf_result: result,
101
+ trace: context.trace,
102
+ span: context.span,
103
+ request: gateway_request,
104
+ actions: result.actions
105
+ }
106
+
107
+ # We want to keep the trace in case of security event
108
+ context.trace.keep! if context.trace
109
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
110
+ context.events << event
111
+
112
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
133
113
  end
134
-
135
- block = Rack::Reactive::RequestBody.publish(op, gateway_request)
136
114
  end
137
115
 
138
- next [nil, [[:block, event]]] if block
139
-
140
- ret, res = stack.call(gateway_request.request)
141
-
142
- if event
143
- res ||= []
144
- res << [:monitor, event]
145
- end
116
+ Rack::Reactive::RequestBody.publish(engine, gateway_request)
146
117
 
147
- [ret, res]
118
+ stack.call(gateway_request.request)
148
119
  end
149
120
  end
150
121
  end
@@ -17,21 +17,21 @@ module Datadog
17
17
  ].freeze
18
18
  private_constant :ADDRESSES
19
19
 
20
- def self.publish(op, gateway_request)
20
+ def self.publish(engine, gateway_request)
21
21
  catch(:block) do
22
- op.publish('request.query', gateway_request.query)
23
- op.publish('request.headers', gateway_request.headers)
24
- op.publish('request.uri.raw', gateway_request.fullpath)
25
- op.publish('request.cookies', gateway_request.cookies)
26
- op.publish('request.client_ip', gateway_request.client_ip)
27
- op.publish('server.request.method', gateway_request.method)
22
+ engine.publish('request.query', gateway_request.query)
23
+ engine.publish('request.headers', gateway_request.headers)
24
+ engine.publish('request.uri.raw', gateway_request.fullpath)
25
+ engine.publish('request.cookies', gateway_request.cookies)
26
+ engine.publish('request.client_ip', gateway_request.client_ip)
27
+ engine.publish('server.request.method', gateway_request.method)
28
28
 
29
29
  nil
30
30
  end
31
31
  end
32
32
 
33
- def self.subscribe(op, waf_context)
34
- op.subscribe(*ADDRESSES) do |*values|
33
+ def self.subscribe(engine, context)
34
+ engine.subscribe(*ADDRESSES) do |*values|
35
35
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
36
36
 
37
37
  headers = values[0]
@@ -53,9 +53,9 @@ module Datadog
53
53
  }
54
54
 
55
55
  waf_timeout = Datadog.configuration.appsec.waf_timeout
56
- result = waf_context.run(persistent_data, {}, waf_timeout)
56
+ result = context.run_waf(persistent_data, {}, waf_timeout)
57
57
 
58
- next if result.status != :match
58
+ next unless result.match?
59
59
 
60
60
  yield result
61
61
  throw(:block, true) unless result.actions.empty?
@@ -12,17 +12,17 @@ module Datadog
12
12
  ].freeze
13
13
  private_constant :ADDRESSES
14
14
 
15
- def self.publish(op, gateway_request)
15
+ def self.publish(engine, gateway_request)
16
16
  catch(:block) do
17
17
  # params have been parsed from the request body
18
- op.publish('request.body', gateway_request.form_hash)
18
+ engine.publish('request.body', gateway_request.form_hash)
19
19
 
20
20
  nil
21
21
  end
22
22
  end
23
23
 
24
- def self.subscribe(op, waf_context)
25
- op.subscribe(*ADDRESSES) do |*values|
24
+ def self.subscribe(engine, context)
25
+ engine.subscribe(*ADDRESSES) do |*values|
26
26
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
27
27
  body = values[0]
28
28
 
@@ -31,9 +31,9 @@ module Datadog
31
31
  }
32
32
 
33
33
  waf_timeout = Datadog.configuration.appsec.waf_timeout
34
- result = waf_context.run(persistent_data, {}, waf_timeout)
34
+ result = context.run_waf(persistent_data, {}, waf_timeout)
35
35
 
36
- next if result.status != :match
36
+ next unless result.match?
37
37
 
38
38
  yield result
39
39
  throw(:block, true) unless result.actions.empty?
@@ -13,17 +13,17 @@ module Datadog
13
13
  ].freeze
14
14
  private_constant :ADDRESSES
15
15
 
16
- def self.publish(op, gateway_response)
16
+ def self.publish(engine, gateway_response)
17
17
  catch(:block) do
18
- op.publish('response.status', gateway_response.status)
19
- op.publish('response.headers', gateway_response.headers)
18
+ engine.publish('response.status', gateway_response.status)
19
+ engine.publish('response.headers', gateway_response.headers)
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
 
29
29
  response_status = values[0]
@@ -37,9 +37,9 @@ 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
- next if result.status != :match
42
+ next unless result.match?
43
43
 
44
44
  yield result
45
45
  throw(:block, true) unless result.actions.empty?
@@ -17,25 +17,24 @@ module Datadog
17
17
  end
18
18
 
19
19
  def call(env)
20
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
20
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
21
21
 
22
22
  return @app.call(env) unless context
23
23
 
24
24
  # TODO: handle exceptions, except for @app.call
25
25
 
26
- request_return, request_response = Instrumentation.gateway.push(
27
- 'rack.request.body',
28
- Gateway::Request.new(env)
29
- ) do
30
- @app.call(env)
31
- end
26
+ http_response = nil
27
+ interrupt_params = catch(::Datadog::AppSec::Ext::INTERRUPT) do
28
+ http_response, _request = Instrumentation.gateway.push('rack.request.body', Gateway::Request.new(env)) do
29
+ @app.call(env)
30
+ end
32
31
 
33
- if request_response
34
- blocked_event = request_response.find { |action, _event| action == :block }
35
- request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
32
+ nil
36
33
  end
37
34
 
38
- request_return
35
+ return AppSec::Response.from_interrupt_params(interrupt_params, env['HTTP_ACCEPT']).to_rack if interrupt_params
36
+
37
+ http_response
39
38
  end
40
39
  end
41
40
  end
@@ -36,7 +36,7 @@ module Datadog
36
36
  @rack_headers = {}
37
37
  end
38
38
 
39
- # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
39
+ # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
40
40
  def call(env)
41
41
  return @app.call(env) unless Datadog::AppSec.enabled?
42
42
 
@@ -45,19 +45,22 @@ module Datadog
45
45
 
46
46
  processor = nil
47
47
  ready = false
48
- scope = nil
48
+ ctx = nil
49
49
 
50
50
  # For a given request, keep using the first Rack stack scope for
51
51
  # nested apps. Don't set `context` local variable so that on popping
52
52
  # out of this nested stack we don't finalize the parent's context
53
- return @app.call(env) if active_scope(env)
53
+ return @app.call(env) if active_context(env)
54
54
 
55
55
  Datadog::AppSec.reconfigure_lock do
56
56
  processor = Datadog::AppSec.processor
57
57
 
58
58
  if !processor.nil? && processor.ready?
59
- scope = Datadog::AppSec::Scope.activate_scope(active_trace, active_span, processor)
60
- env[Datadog::AppSec::Ext::SCOPE_KEY] = scope
59
+ ctx = Datadog::AppSec::Context.activate(
60
+ Datadog::AppSec::Context.new(active_trace, active_span, processor)
61
+ )
62
+
63
+ env[Datadog::AppSec::Ext::CONTEXT_KEY] = ctx
61
64
  ready = true
62
65
  end
63
66
  end
@@ -66,66 +69,59 @@ module Datadog
66
69
 
67
70
  return @app.call(env) unless ready
68
71
 
69
- gateway_request = Gateway::Request.new(env)
72
+ add_appsec_tags(processor, ctx)
73
+ add_request_tags(ctx, env)
70
74
 
71
- add_appsec_tags(processor, scope)
72
- add_request_tags(scope, env)
75
+ http_response = nil
76
+ gateway_request = Gateway::Request.new(env)
77
+ gateway_response = nil
73
78
 
74
- request_return, request_response = catch(::Datadog::AppSec::Ext::INTERRUPT) do
75
- Instrumentation.gateway.push('rack.request', gateway_request) do
79
+ interrupt_params = catch(::Datadog::AppSec::Ext::INTERRUPT) do
80
+ http_response, _gateway_request = Instrumentation.gateway.push('rack.request', gateway_request) do
76
81
  @app.call(env)
77
82
  end
78
- end
79
83
 
80
- if request_response
81
- blocked_event = request_response.find { |action, _options| action == :block }
82
- request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
83
- end
84
+ gateway_response = Gateway::Response.new(
85
+ http_response[2], http_response[0], http_response[1], context: ctx
86
+ )
84
87
 
85
- gateway_response = Gateway::Response.new(
86
- request_return[2],
87
- request_return[0],
88
- request_return[1],
89
- scope: scope,
90
- )
88
+ Instrumentation.gateway.push('rack.response', gateway_response)
91
89
 
92
- _response_return, response_response = Instrumentation.gateway.push('rack.response', gateway_response)
90
+ nil
91
+ end
93
92
 
94
- result = scope.processor_context.extract_schema
93
+ if interrupt_params
94
+ http_response = AppSec::Response.from_interrupt_params(interrupt_params, env['HTTP_ACCEPT']).to_rack
95
+ end
95
96
 
96
- if result
97
- scope.processor_context.events << {
98
- trace: scope.trace,
99
- span: scope.service_entry_span,
100
- waf_result: result,
97
+ if AppSec.api_security_enabled?
98
+ ctx.events << {
99
+ trace: ctx.trace,
100
+ span: ctx.span,
101
+ waf_result: ctx.extract_schema,
101
102
  }
102
103
  end
103
104
 
104
- scope.processor_context.events.each do |e|
105
+ ctx.events.each do |e|
105
106
  e[:response] ||= gateway_response
106
107
  e[:request] ||= gateway_request
107
108
  end
108
109
 
109
- AppSec::Event.record(scope.service_entry_span, *scope.processor_context.events)
110
-
111
- if response_response
112
- blocked_event = response_response.find { |action, _options| action == :block }
113
- request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
114
- end
110
+ AppSec::Event.record(ctx.span, *ctx.events)
115
111
 
116
- request_return
112
+ http_response
117
113
  ensure
118
- if scope
119
- add_waf_runtime_tags(scope)
120
- Datadog::AppSec::Scope.deactivate_scope
114
+ if ctx
115
+ ctx.export_metrics
116
+ Datadog::AppSec::Context.deactivate
121
117
  end
122
118
  end
123
- # rubocop:enable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
119
+ # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
124
120
 
125
121
  private
126
122
 
127
- def active_scope(env)
128
- env[Datadog::AppSec::Ext::SCOPE_KEY]
123
+ def active_context(env)
124
+ env[Datadog::AppSec::Ext::CONTEXT_KEY]
129
125
  end
130
126
 
131
127
  def active_trace
@@ -144,9 +140,9 @@ module Datadog
144
140
  Datadog::Tracing.active_span
145
141
  end
146
142
 
147
- def add_appsec_tags(processor, scope)
148
- span = scope.service_entry_span
149
- trace = scope.trace
143
+ def add_appsec_tags(processor, context)
144
+ span = context.span
145
+ trace = context.trace
150
146
 
151
147
  return unless trace && span
152
148
 
@@ -181,8 +177,8 @@ module Datadog
181
177
  end
182
178
  end
183
179
 
184
- def add_request_tags(scope, env)
185
- span = scope.service_entry_span
180
+ def add_request_tags(context, env)
181
+ span = context.span
186
182
 
187
183
  return unless span
188
184
 
@@ -204,19 +200,6 @@ module Datadog
204
200
  end
205
201
  end
206
202
 
207
- def add_waf_runtime_tags(scope)
208
- span = scope.service_entry_span
209
- context = scope.processor_context
210
-
211
- return unless span && context
212
-
213
- span.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
214
-
215
- # these tags expect time in us
216
- span.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
217
- span.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
218
- end
219
-
220
203
  def to_rack_header(header)
221
204
  @rack_headers[header] ||= Datadog::Tracing::Contrib::Rack::Header.to_rack_header(header)
222
205
  end
@@ -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/action'
6
6
  require_relative '../../../event'
7
7
 
@@ -21,43 +21,33 @@ module Datadog
21
21
 
22
22
  def watch_request_action(gateway = Instrumentation.gateway)
23
23
  gateway.watch('rails.request.action', :appsec) do |stack, gateway_request|
24
- block = false
25
-
26
24
  event = nil
27
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
28
-
29
- AppSec::Reactive::Operation.new('rails.request.action') do |op|
30
- Rails::Reactive::Action.subscribe(op, scope.processor_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: scope.trace,
36
- span: scope.service_entry_span,
37
- request: gateway_request,
38
- actions: result.actions
39
- }
40
-
41
- # We want to keep the trace in case of security event
42
- scope.trace.keep! if scope.trace
43
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
44
- scope.processor_context.events << event
45
- end
25
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
26
+ engine = AppSec::Reactive::Engine.new
27
+
28
+ Rails::Reactive::Action.subscribe(engine, context) do |result|
29
+ if result.match?
30
+ # TODO: should this hash be an Event instance instead?
31
+ event = {
32
+ waf_result: result,
33
+ trace: context.trace,
34
+ span: context.span,
35
+ request: gateway_request,
36
+ actions: result.actions
37
+ }
38
+
39
+ # We want to keep the trace in case of security event
40
+ context.trace.keep! if context.trace
41
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
42
+ context.events << event
43
+
44
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
46
45
  end
47
-
48
- block = Rails::Reactive::Action.publish(op, gateway_request)
49
46
  end
50
47
 
51
- next [nil, [[:block, event]]] if block
52
-
53
- ret, res = stack.call(gateway_request.request)
54
-
55
- if event
56
- res ||= []
57
- res << [:monitor, event]
58
- end
48
+ Rails::Reactive::Action.publish(engine, gateway_request)
59
49
 
60
- [ret, res]
50
+ stack.call(gateway_request.request)
61
51
  end
62
52
  end
63
53
  end
@@ -73,29 +73,19 @@ module Datadog
73
73
  def process_action(*args)
74
74
  env = request.env
75
75
 
76
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
76
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
77
77
 
78
78
  return super unless context
79
79
 
80
80
  # TODO: handle exceptions, except for super
81
81
 
82
82
  gateway_request = Gateway::Request.new(request)
83
- request_return, request_response = Instrumentation.gateway.push('rails.request.action', gateway_request) do
84
- super
85
- end
86
83
 
87
- if request_response
88
- blocked_event = request_response.find { |action, _options| action == :block }
89
- if blocked_event
90
- @_response = AppSec::Response.negotiate(
91
- env,
92
- blocked_event.last[:actions]
93
- ).to_action_dispatch_response
94
- request_return = @_response.body
95
- end
84
+ http_response, _gateway_request = Instrumentation.gateway.push('rails.request.action', gateway_request) do
85
+ super
96
86
  end
97
87
 
98
- request_return
88
+ http_response
99
89
  end
100
90
  end
101
91