datadog 2.4.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -1
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +3 -3
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +57 -18
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +93 -106
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +8 -2
- data/ext/datadog_profiling_native_extension/extconf.rb +8 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +174 -28
- data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +1 -1
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +14 -11
- data/ext/datadog_profiling_native_extension/stack_recorder.c +58 -22
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/libdatadog_api/crashtracker.c +3 -5
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +8 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -5
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -15
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +6 -18
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +5 -18
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +5 -18
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -10
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +7 -20
- data/lib/datadog/appsec/event.rb +24 -0
- data/lib/datadog/appsec/ext.rb +4 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +7 -20
- data/lib/datadog/appsec/processor/context.rb +107 -0
- data/lib/datadog/appsec/processor.rb +7 -71
- data/lib/datadog/appsec/scope.rb +1 -4
- data/lib/datadog/appsec/utils/trace_operation.rb +15 -0
- data/lib/datadog/appsec/utils.rb +2 -0
- data/lib/datadog/appsec.rb +1 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
- data/lib/datadog/core/configuration/settings.rb +12 -0
- data/lib/datadog/core/configuration.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +8 -5
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/remote/transport/http.rb +5 -0
- data/lib/datadog/core/remote/worker.rb +1 -1
- data/lib/datadog/core/runtime/ext.rb +1 -0
- data/lib/datadog/core/runtime/metrics.rb +4 -0
- data/lib/datadog/core/semaphore.rb +35 -0
- data/lib/datadog/core/telemetry/logging.rb +10 -10
- data/lib/datadog/core/transport/ext.rb +1 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/di/code_tracker.rb +11 -13
- data/lib/datadog/di/instrumenter.rb +301 -0
- data/lib/datadog/di/probe.rb +29 -0
- data/lib/datadog/di/probe_builder.rb +7 -1
- data/lib/datadog/di/probe_notification_builder.rb +207 -0
- data/lib/datadog/di/probe_notifier_worker.rb +244 -0
- data/lib/datadog/di/serializer.rb +23 -1
- data/lib/datadog/di/transport.rb +67 -0
- data/lib/datadog/di/utils.rb +39 -0
- data/lib/datadog/di.rb +43 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +9 -11
- data/lib/datadog/profiling/component.rb +1 -0
- data/lib/datadog/profiling/stack_recorder.rb +37 -9
- data/lib/datadog/tracing/component.rb +13 -0
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -2
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +9 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -0
- data/lib/datadog/tracing/sampling/rule_sampler.rb +6 -4
- data/lib/datadog/tracing/tracer.rb +15 -10
- data/lib/datadog/tracing/transport/http.rb +4 -0
- data/lib/datadog/tracing/workers.rb +1 -1
- data/lib/datadog/tracing/writer.rb +26 -28
- data/lib/datadog/version.rb +1 -1
- 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
|
-
|
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(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
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(
|
56
|
-
|
57
|
-
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
56
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
58
57
|
|
59
|
-
|
60
|
-
when :match
|
61
|
-
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
58
|
+
next if result.status != :match
|
62
59
|
|
63
|
-
|
64
|
-
|
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
|
-
|
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(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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(
|
40
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
41
41
|
|
42
|
-
|
42
|
+
next if result.status != :match
|
43
43
|
|
44
|
-
|
45
|
-
|
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.
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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(
|
40
|
+
result = waf_context.run(persistent_data, {}, waf_timeout)
|
41
41
|
|
42
|
-
|
42
|
+
next if result.status != :match
|
43
43
|
|
44
|
-
|
45
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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(
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -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
|
data/lib/datadog/appsec/ext.rb
CHANGED
@@ -35,11 +35,9 @@ module Datadog
|
|
35
35
|
actions: result.actions
|
36
36
|
}
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
class Processor
|
6
|
+
# Context manages a sequence of runs
|
7
|
+
class Context
|
8
|
+
LIBDDWAF_SUCCESSFUL_EXECUTION_CODES = [:ok, :match].freeze
|
9
|
+
|
10
|
+
attr_reader :time_ns, :time_ext_ns, :timeouts, :events
|
11
|
+
|
12
|
+
def initialize(handle, telemetry:)
|
13
|
+
@context = WAF::Context.new(handle)
|
14
|
+
@telemetry = telemetry
|
15
|
+
|
16
|
+
@time_ns = 0.0
|
17
|
+
@time_ext_ns = 0.0
|
18
|
+
@timeouts = 0
|
19
|
+
@events = []
|
20
|
+
@run_mutex = Mutex.new
|
21
|
+
|
22
|
+
@libddwaf_debug_tag = "libddwaf:#{WAF::VERSION::STRING}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
|
26
|
+
@run_mutex.lock
|
27
|
+
|
28
|
+
start_ns = Core::Utils::Time.get_time(:nanosecond)
|
29
|
+
|
30
|
+
persistent_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
|
+
ephemeral_data.reject! do |_, v|
|
37
|
+
next false if v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
38
|
+
|
39
|
+
v.nil? ? true : v.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
_code, result = try_run(persistent_data, ephemeral_data, timeout)
|
43
|
+
|
44
|
+
stop_ns = Core::Utils::Time.get_time(:nanosecond)
|
45
|
+
|
46
|
+
# these updates are not thread safe and should be protected
|
47
|
+
@time_ns += result.total_runtime
|
48
|
+
@time_ext_ns += (stop_ns - start_ns)
|
49
|
+
@timeouts += 1 if result.timeout
|
50
|
+
|
51
|
+
report_execution(result)
|
52
|
+
result
|
53
|
+
ensure
|
54
|
+
@run_mutex.unlock
|
55
|
+
end
|
56
|
+
|
57
|
+
def extract_schema
|
58
|
+
return unless extract_schema?
|
59
|
+
|
60
|
+
input = {
|
61
|
+
'waf.context.processor' => {
|
62
|
+
'extract-schema' => true
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
_code, result = try_run(input, {}, WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
|
67
|
+
|
68
|
+
report_execution(result)
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
def finalize
|
73
|
+
@context.finalize
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def try_run(persistent_data, ephemeral_data, timeout)
|
79
|
+
@context.run(persistent_data, ephemeral_data, timeout)
|
80
|
+
rescue WAF::LibDDWAF::Error => e
|
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')
|
83
|
+
|
84
|
+
[:err_internal, WAF::Result.new(:err_internal, [], 0.0, false, [], [])]
|
85
|
+
end
|
86
|
+
|
87
|
+
def report_execution(result)
|
88
|
+
Datadog.logger.debug { "#{@libddwaf_debug_tag} execution timed out: #{result.inspect}" } if result.timeout
|
89
|
+
|
90
|
+
if LIBDDWAF_SUCCESSFUL_EXECUTION_CODES.include?(result.status)
|
91
|
+
Datadog.logger.debug { "#{@libddwaf_debug_tag} execution result: #{result.inspect}" }
|
92
|
+
else
|
93
|
+
message = "#{@libddwaf_debug_tag} execution error: #{result.status.inspect}"
|
94
|
+
|
95
|
+
Datadog.logger.debug { message }
|
96
|
+
@telemetry.error(message)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def extract_schema?
|
101
|
+
Datadog.configuration.appsec.api_security.enabled &&
|
102
|
+
Datadog.configuration.appsec.api_security.sample_rate.sample?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
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
|
-
|
97
|
-
|
98
|
-
|
32
|
+
def new_context
|
33
|
+
Context.new(@handle, telemetry: @telemetry)
|
34
|
+
end
|
99
35
|
|
100
36
|
private
|
101
37
|
|