datadog 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -2
  3. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +3 -3
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +57 -18
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +93 -106
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +8 -2
  7. data/ext/datadog_profiling_native_extension/extconf.rb +8 -8
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +174 -28
  9. data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
  10. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
  11. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +1 -1
  12. data/ext/datadog_profiling_native_extension/ruby_helpers.c +14 -11
  13. data/ext/datadog_profiling_native_extension/stack_recorder.c +58 -22
  14. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  15. data/ext/libdatadog_extconf_helpers.rb +1 -1
  16. data/lib/datadog/appsec/configuration/settings.rb +8 -0
  17. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -5
  18. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
  19. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -15
  20. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +6 -18
  21. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +7 -20
  22. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +5 -18
  23. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -1
  24. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -5
  25. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +5 -18
  26. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -10
  27. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +7 -20
  28. data/lib/datadog/appsec/event.rb +24 -0
  29. data/lib/datadog/appsec/ext.rb +4 -0
  30. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -5
  31. data/lib/datadog/appsec/monitor/reactive/set_user.rb +7 -20
  32. data/lib/datadog/appsec/processor/context.rb +109 -0
  33. data/lib/datadog/appsec/processor.rb +7 -71
  34. data/lib/datadog/appsec/scope.rb +1 -4
  35. data/lib/datadog/appsec/utils/trace_operation.rb +15 -0
  36. data/lib/datadog/appsec/utils.rb +2 -0
  37. data/lib/datadog/appsec.rb +1 -0
  38. data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
  39. data/lib/datadog/core/configuration/settings.rb +12 -0
  40. data/lib/datadog/core/configuration.rb +1 -3
  41. data/lib/datadog/core/crashtracking/component.rb +8 -5
  42. data/lib/datadog/core/environment/yjit.rb +5 -0
  43. data/lib/datadog/core/remote/transport/http.rb +5 -0
  44. data/lib/datadog/core/remote/worker.rb +1 -1
  45. data/lib/datadog/core/runtime/ext.rb +1 -0
  46. data/lib/datadog/core/runtime/metrics.rb +4 -0
  47. data/lib/datadog/core/semaphore.rb +35 -0
  48. data/lib/datadog/core/telemetry/logging.rb +10 -10
  49. data/lib/datadog/core/transport/ext.rb +1 -0
  50. data/lib/datadog/core/workers/async.rb +1 -1
  51. data/lib/datadog/di/code_tracker.rb +11 -13
  52. data/lib/datadog/di/instrumenter.rb +301 -0
  53. data/lib/datadog/di/probe.rb +29 -0
  54. data/lib/datadog/di/probe_builder.rb +7 -1
  55. data/lib/datadog/di/probe_notification_builder.rb +207 -0
  56. data/lib/datadog/di/probe_notifier_worker.rb +244 -0
  57. data/lib/datadog/di/serializer.rb +23 -1
  58. data/lib/datadog/di/transport.rb +67 -0
  59. data/lib/datadog/di/utils.rb +39 -0
  60. data/lib/datadog/di.rb +43 -0
  61. data/lib/datadog/profiling/collectors/thread_context.rb +9 -11
  62. data/lib/datadog/profiling/component.rb +1 -0
  63. data/lib/datadog/profiling/stack_recorder.rb +37 -9
  64. data/lib/datadog/tracing/component.rb +13 -0
  65. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -0
  66. data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -0
  67. data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -0
  68. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -2
  69. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +9 -0
  70. data/lib/datadog/tracing/contrib/http/instrumentation.rb +4 -0
  71. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +4 -0
  72. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -0
  73. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  74. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -0
  75. data/lib/datadog/tracing/sampling/rule_sampler.rb +6 -4
  76. data/lib/datadog/tracing/tracer.rb +15 -10
  77. data/lib/datadog/tracing/transport/http.rb +4 -0
  78. data/lib/datadog/tracing/workers.rb +1 -1
  79. data/lib/datadog/tracing/writer.rb +26 -28
  80. data/lib/datadog/version.rb +1 -1
  81. metadata +22 -14
@@ -25,30 +25,17 @@ module Datadog
25
25
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
26
26
  arguments = values[0]
27
27
 
28
- waf_args = {
28
+ persistent_data = {
29
29
  'graphql.server.all_resolvers' => arguments
30
30
  }
31
31
 
32
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
33
+ result = waf_context.run(persistent_data, {}, waf_timeout)
34
+
35
+ next if result.status != :match
36
+
37
+ yield result
38
+ throw(:block, true) unless result.actions.empty?
52
39
  end
53
40
  end
54
41
  end
@@ -41,11 +41,9 @@ module Datadog
41
41
  actions: result.actions
42
42
  }
43
43
 
44
- if scope.service_entry_span
45
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
46
- scope.service_entry_span.set_tag('appsec.event', 'true')
47
- end
48
-
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)
49
47
  scope.processor_context.events << event
50
48
  end
51
49
  end
@@ -85,11 +83,9 @@ module Datadog
85
83
  actions: result.actions
86
84
  }
87
85
 
88
- if scope.service_entry_span
89
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
90
- scope.service_entry_span.set_tag('appsec.event', 'true')
91
- end
92
-
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)
93
89
  scope.processor_context.events << event
94
90
  end
95
91
  end
@@ -129,11 +125,9 @@ module Datadog
129
125
  actions: result.actions
130
126
  }
131
127
 
132
- if scope.service_entry_span
133
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
134
- scope.service_entry_span.set_tag('appsec.event', 'true')
135
- end
136
-
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)
137
131
  scope.processor_context.events << event
138
132
  end
139
133
  end
@@ -33,6 +33,7 @@ module Datadog
33
33
  def self.subscribe(op, waf_context)
34
34
  op.subscribe(*ADDRESSES) do |*values|
35
35
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
36
+
36
37
  headers = values[0]
37
38
  headers_no_cookies = headers.dup.tap { |h| h.delete('cookie') }
38
39
  uri_raw = values[1]
@@ -41,7 +42,7 @@ module Datadog
41
42
  client_ip = values[4]
42
43
  request_method = values[5]
43
44
 
44
- waf_args = {
45
+ persistent_data = {
45
46
  'server.request.cookies' => cookies,
46
47
  'server.request.query' => query,
47
48
  'server.request.uri.raw' => uri_raw,
@@ -52,25 +53,12 @@ module Datadog
52
53
  }
53
54
 
54
55
  waf_timeout = Datadog.configuration.appsec.waf_timeout
55
- result = waf_context.run(waf_args, waf_timeout)
56
-
57
- Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
56
+ result = waf_context.run(persistent_data, {}, waf_timeout)
58
57
 
59
- case result.status
60
- when :match
61
- Datadog.logger.debug { "WAF: #{result.inspect}" }
58
+ next if result.status != :match
62
59
 
63
- yield result
64
- throw(:block, true) unless result.actions.empty?
65
- when :ok
66
- Datadog.logger.debug { "WAF OK: #{result.inspect}" }
67
- when :invalid_call
68
- Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
69
- when :invalid_rule, :invalid_flow, :no_rule
70
- Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
71
- else
72
- Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
73
- end
60
+ yield result
61
+ throw(:block, true) unless result.actions.empty?
74
62
  end
75
63
  end
76
64
  end
@@ -26,30 +26,17 @@ module Datadog
26
26
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
27
27
  body = values[0]
28
28
 
29
- waf_args = {
29
+ persistent_data = {
30
30
  'server.request.body' => body,
31
31
  }
32
32
 
33
33
  waf_timeout = Datadog.configuration.appsec.waf_timeout
34
- result = waf_context.run(waf_args, waf_timeout)
35
-
36
- Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
37
-
38
- case result.status
39
- when :match
40
- Datadog.logger.debug { "WAF: #{result.inspect}" }
41
-
42
- yield result
43
- throw(:block, true) unless result.actions.empty?
44
- when :ok
45
- Datadog.logger.debug { "WAF OK: #{result.inspect}" }
46
- when :invalid_call
47
- Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
48
- when :invalid_rule, :invalid_flow, :no_rule
49
- Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
50
- else
51
- Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
52
- end
34
+ result = waf_context.run(persistent_data, {}, waf_timeout)
35
+
36
+ next if result.status != :match
37
+
38
+ yield result
39
+ throw(:block, true) unless result.actions.empty?
53
40
  end
54
41
  end
55
42
  end
@@ -30,32 +30,19 @@ module Datadog
30
30
  response_headers = values[1]
31
31
  response_headers_no_cookies = response_headers.dup.tap { |h| h.delete('set-cookie') }
32
32
 
33
- waf_args = {
33
+ persistent_data = {
34
34
  'server.response.status' => response_status.to_s,
35
35
  'server.response.headers' => response_headers,
36
36
  'server.response.headers.no_cookies' => response_headers_no_cookies,
37
37
  }
38
38
 
39
39
  waf_timeout = Datadog.configuration.appsec.waf_timeout
40
- result = waf_context.run(waf_args, waf_timeout)
40
+ result = waf_context.run(persistent_data, {}, waf_timeout)
41
41
 
42
- Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
42
+ next if result.status != :match
43
43
 
44
- case result.status
45
- when :match
46
- Datadog.logger.debug { "WAF: #{result.inspect}" }
47
-
48
- yield result
49
- throw(:block, true) unless result.actions.empty?
50
- when :ok
51
- Datadog.logger.debug { "WAF OK: #{result.inspect}" }
52
- when :invalid_call
53
- Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
54
- when :invalid_rule, :invalid_flow, :no_rule
55
- Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
56
- else
57
- Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
58
- end
44
+ yield result
45
+ throw(:block, true) unless result.actions.empty?
59
46
  end
60
47
  end
61
48
  end
@@ -150,7 +150,9 @@ module Datadog
150
150
 
151
151
  return unless trace && span
152
152
 
153
- span.set_tag('_dd.appsec.enabled', 1)
153
+ span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
154
+ # We add this tag when ASM standalone is enabled to make sure we don't bill APM
155
+ span.set_metric(Datadog::AppSec::Ext::TAG_APM_ENABLED, 0) if Datadog.configuration.appsec.standalone.enabled
154
156
  span.set_tag('_dd.runtime_family', 'ruby')
155
157
  span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
156
158
 
@@ -38,11 +38,9 @@ module Datadog
38
38
  actions: result.actions
39
39
  }
40
40
 
41
- if scope.service_entry_span
42
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
43
- scope.service_entry_span.set_tag('appsec.event', 'true')
44
- end
45
-
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)
46
44
  scope.processor_context.events << event
47
45
  end
48
46
  end
@@ -31,31 +31,18 @@ module Datadog
31
31
  body = values[0]
32
32
  path_params = values[1]
33
33
 
34
- waf_args = {
34
+ persistent_data = {
35
35
  'server.request.body' => body,
36
36
  'server.request.path_params' => path_params,
37
37
  }
38
38
 
39
39
  waf_timeout = Datadog.configuration.appsec.waf_timeout
40
- result = waf_context.run(waf_args, waf_timeout)
40
+ result = waf_context.run(persistent_data, {}, waf_timeout)
41
41
 
42
- Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
42
+ next if result.status != :match
43
43
 
44
- case result.status
45
- when :match
46
- Datadog.logger.debug { "WAF: #{result.inspect}" }
47
-
48
- yield result
49
- throw(:block, true) unless result.actions.empty?
50
- when :ok
51
- Datadog.logger.debug { "WAF OK: #{result.inspect}" }
52
- when :invalid_call
53
- Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
54
- when :invalid_rule, :invalid_flow, :no_rule
55
- Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
56
- else
57
- Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
58
- end
44
+ yield result
45
+ throw(:block, true) unless result.actions.empty?
59
46
  end
60
47
  end
61
48
  end
@@ -40,11 +40,9 @@ module Datadog
40
40
  actions: result.actions
41
41
  }
42
42
 
43
- if scope.service_entry_span
44
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
45
- scope.service_entry_span.set_tag('appsec.event', 'true')
46
- end
47
-
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)
48
46
  scope.processor_context.events << event
49
47
  end
50
48
  end
@@ -84,11 +82,9 @@ module Datadog
84
82
  actions: result.actions
85
83
  }
86
84
 
87
- if scope.service_entry_span
88
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
89
- scope.service_entry_span.set_tag('appsec.event', 'true')
90
- end
91
-
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)
92
88
  scope.processor_context.events << event
93
89
  end
94
90
  end
@@ -27,30 +27,17 @@ module Datadog
27
27
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
28
28
  path_params = values[0]
29
29
 
30
- waf_args = {
30
+ persistent_data = {
31
31
  'server.request.path_params' => path_params,
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog.configuration.appsec.waf_timeout
35
- result = waf_context.run(waf_args, waf_timeout)
36
-
37
- Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
38
-
39
- case result.status
40
- when :match
41
- Datadog.logger.debug { "WAF: #{result.inspect}" }
42
-
43
- yield result
44
- throw(:block, true) unless result.actions.empty?
45
- when :ok
46
- Datadog.logger.debug { "WAF OK: #{result.inspect}" }
47
- when :invalid_call
48
- Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
49
- when :invalid_rule, :invalid_flow, :no_rule
50
- Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
51
- else
52
- Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
53
- end
35
+ result = waf_context.run(persistent_data, {}, waf_timeout)
36
+
37
+ next if result.status != :match
38
+
39
+ yield result
40
+ throw(:block, true) unless result.actions.empty?
54
41
  end
55
42
  end
56
43
  end
@@ -137,6 +137,18 @@ module Datadog
137
137
  end
138
138
  # rubocop:enable Metrics/MethodLength
139
139
 
140
+ def tag_and_keep!(scope, waf_result)
141
+ # We want to keep the trace in case of security event
142
+ scope.trace.keep! if scope.trace
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')
147
+ end
148
+
149
+ add_distributed_tags(scope.trace)
150
+ end
151
+
140
152
  private
141
153
 
142
154
  def compressed_and_base64_encoded(value)
@@ -165,6 +177,18 @@ module Datadog
165
177
  gz.close
166
178
  sio.string
167
179
  end
180
+
181
+ # Propagate to downstream services the information that the current distributed trace is
182
+ # containing at least one ASM security event.
183
+ def add_distributed_tags(trace)
184
+ return unless trace
185
+
186
+ trace.set_tag(
187
+ Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
188
+ Datadog::Tracing::Sampling::Ext::Decision::ASM
189
+ )
190
+ trace.set_tag(Datadog::AppSec::Ext::TAG_DISTRIBUTED_APPSEC_EVENT, '1')
191
+ end
168
192
  end
169
193
  end
170
194
  end
@@ -5,6 +5,10 @@ module Datadog
5
5
  module Ext
6
6
  INTERRUPT = :datadog_appsec_interrupt
7
7
  SCOPE_KEY = 'datadog.appsec.scope'
8
+
9
+ TAG_APPSEC_ENABLED = '_dd.appsec.enabled'
10
+ TAG_APM_ENABLED = '_dd.apm.enabled'
11
+ TAG_DISTRIBUTED_APPSEC_EVENT = '_dd.p.appsec'
8
12
  end
9
13
  end
10
14
  end
@@ -35,11 +35,9 @@ module Datadog
35
35
  actions: result.actions
36
36
  }
37
37
 
38
- if scope.service_entry_span
39
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
40
- scope.service_entry_span.set_tag('appsec.event', 'true')
41
- end
42
-
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)
43
41
  scope.processor_context.events << event
44
42
  end
45
43
  end
@@ -25,30 +25,17 @@ module Datadog
25
25
 
26
26
  user_id = values[0]
27
27
 
28
- waf_args = {
28
+ persistent_data = {
29
29
  'usr.id' => user_id,
30
30
  }
31
31
 
32
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
33
+ result = waf_context.run(persistent_data, {}, waf_timeout)
34
+
35
+ next if result.status != :match
36
+
37
+ yield result
38
+ throw(:block, true) unless result.actions.empty?
52
39
  end
53
40
  end
54
41
  end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'libddwaf'
4
+
5
+ module Datadog
6
+ module AppSec
7
+ class Processor
8
+ # Context manages a sequence of runs
9
+ class Context
10
+ LIBDDWAF_SUCCESSFUL_EXECUTION_CODES = [:ok, :match].freeze
11
+
12
+ attr_reader :time_ns, :time_ext_ns, :timeouts, :events
13
+
14
+ def initialize(handle, telemetry:)
15
+ @context = WAF::Context.new(handle)
16
+ @telemetry = telemetry
17
+
18
+ @time_ns = 0.0
19
+ @time_ext_ns = 0.0
20
+ @timeouts = 0
21
+ @events = []
22
+ @run_mutex = Mutex.new
23
+
24
+ @libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING}"
25
+ end
26
+
27
+ def run(persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
28
+ @run_mutex.lock
29
+
30
+ start_ns = Core::Utils::Time.get_time(:nanosecond)
31
+
32
+ persistent_data.reject! do |_, v|
33
+ next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
34
+
35
+ v.nil? ? true : v.empty?
36
+ end
37
+
38
+ ephemeral_data.reject! do |_, v|
39
+ next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
40
+
41
+ v.nil? ? true : v.empty?
42
+ end
43
+
44
+ _code, result = try_run(persistent_data, ephemeral_data, timeout)
45
+
46
+ stop_ns = Core::Utils::Time.get_time(:nanosecond)
47
+
48
+ # these updates are not thread safe and should be protected
49
+ @time_ns += result.total_runtime
50
+ @time_ext_ns += (stop_ns - start_ns)
51
+ @timeouts += 1 if result.timeout
52
+
53
+ report_execution(result)
54
+ result
55
+ ensure
56
+ @run_mutex.unlock
57
+ end
58
+
59
+ def extract_schema
60
+ return unless extract_schema?
61
+
62
+ input = {
63
+ 'waf.context.processor' => {
64
+ 'extract-schema' => true
65
+ }
66
+ }
67
+
68
+ _code, result = try_run(input, {}, WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
69
+
70
+ report_execution(result)
71
+ result
72
+ end
73
+
74
+ def finalize
75
+ @context.finalize
76
+ end
77
+
78
+ private
79
+
80
+ def try_run(persistent_data, ephemeral_data, timeout)
81
+ @context.run(persistent_data, ephemeral_data, timeout)
82
+ rescue WAF::LibDDWAF::Error => e
83
+ Datadog.logger.debug { "#{@libddwaf_debug_tag} execution error: #{e} backtrace: #{e.backtrace&.first(3)}" }
84
+ @telemetry.report(e, description: 'libddwaf internal low-level error')
85
+
86
+ [:err_internal, WAF::Result.new(:err_internal, [], 0.0, false, [], [])]
87
+ end
88
+
89
+ def report_execution(result)
90
+ Datadog.logger.debug { "#{@libddwaf_debug_tag} execution timed out: #{result.inspect}" } if result.timeout
91
+
92
+ if LIBDDWAF_SUCCESSFUL_EXECUTION_CODES.include?(result.status)
93
+ Datadog.logger.debug { "#{@libddwaf_debug_tag} execution result: #{result.inspect}" }
94
+ else
95
+ message = "#{@libddwaf_debug_tag} execution error: #{result.status.inspect}"
96
+
97
+ Datadog.logger.debug { message }
98
+ @telemetry.error(message)
99
+ end
100
+ end
101
+
102
+ def extract_schema?
103
+ Datadog.configuration.appsec.api_security.enabled &&
104
+ Datadog.configuration.appsec.api_security.sample_rate.sample?
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,83 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'processor/context'
4
+
3
5
  module Datadog
4
6
  module AppSec
5
7
  # Processor integrates libddwaf into datadog/appsec
6
8
  class Processor
7
- # Context manages a sequence of runs
8
- class Context
9
- attr_reader :time_ns, :time_ext_ns, :timeouts, :events
10
-
11
- def initialize(processor)
12
- @context = Datadog::AppSec::WAF::Context.new(processor.send(:handle))
13
- @time_ns = 0.0
14
- @time_ext_ns = 0.0
15
- @timeouts = 0
16
- @events = []
17
- @run_mutex = Mutex.new
18
- end
19
-
20
- def run(input, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
21
- @run_mutex.lock
22
-
23
- start_ns = Core::Utils::Time.get_time(:nanosecond)
24
-
25
- input.reject! do |_, v|
26
- case v
27
- when TrueClass, FalseClass
28
- false
29
- else
30
- v.nil? ? true : v.empty?
31
- end
32
- end
33
-
34
- _code, res = @context.run(input, timeout)
35
-
36
- stop_ns = Core::Utils::Time.get_time(:nanosecond)
37
-
38
- # these updates are not thread safe and should be protected
39
- @time_ns += res.total_runtime
40
- @time_ext_ns += (stop_ns - start_ns)
41
- @timeouts += 1 if res.timeout
42
-
43
- res
44
- ensure
45
- @run_mutex.unlock
46
- end
47
-
48
- def extract_schema
49
- return unless extract_schema?
50
-
51
- input = {
52
- 'waf.context.processor' => {
53
- 'extract-schema' => true
54
- }
55
- }
56
-
57
- _code, res = @context.run(input, WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
58
-
59
- res
60
- end
61
-
62
- def finalize
63
- @context.finalize
64
- end
65
-
66
- private
67
-
68
- def extract_schema?
69
- Datadog.configuration.appsec.api_security.enabled &&
70
- Datadog.configuration.appsec.api_security.sample_rate.sample?
71
- end
72
- end
73
-
74
9
  attr_reader :diagnostics, :addresses
75
10
 
76
11
  def initialize(ruleset:, telemetry:)
12
+ @telemetry = telemetry
77
13
  @diagnostics = nil
78
14
  @addresses = []
15
+
79
16
  settings = Datadog.configuration.appsec
80
- @telemetry = telemetry
81
17
 
82
18
  # TODO: Refactor to make it easier to test
83
19
  unless require_libddwaf && libddwaf_provides_waf? && create_waf_handle(settings, ruleset)
@@ -93,9 +29,9 @@ module Datadog
93
29
  @handle.finalize
94
30
  end
95
31
 
96
- protected
97
-
98
- attr_reader :handle
32
+ def new_context
33
+ Context.new(@handle, telemetry: @telemetry)
34
+ end
99
35
 
100
36
  private
101
37