datadog 2.3.0 → 2.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +64 -2
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
- data/ext/datadog_profiling_loader/extconf.rb +10 -22
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +3 -3
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +198 -41
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +89 -46
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +645 -107
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +15 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +0 -27
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -4
- data/ext/datadog_profiling_native_extension/extconf.rb +42 -25
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +194 -34
- data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
- data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +53 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/datadog_profiling_native_extension/profiling.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 +2 -0
- data/ext/libdatadog_api/crashtracker.c +20 -18
- data/ext/libdatadog_api/datadog_ruby_common.c +0 -27
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
- data/lib/datadog/appsec/component.rb +29 -8
- data/lib/datadog/appsec/configuration/settings.rb +10 -2
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
- data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
- data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +0 -14
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +67 -31
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +14 -15
- data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
- 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 +25 -1
- 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 +109 -0
- data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
- data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
- data/lib/datadog/appsec/processor.rb +42 -107
- data/lib/datadog/appsec/rate_limiter.rb +25 -40
- data/lib/datadog/appsec/remote.rb +7 -3
- 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 +3 -2
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
- data/lib/datadog/core/configuration/components.rb +4 -3
- data/lib/datadog/core/configuration/settings.rb +96 -5
- data/lib/datadog/core/configuration.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +9 -6
- data/lib/datadog/core/environment/execution.rb +5 -5
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/metrics/client.rb +7 -0
- data/lib/datadog/core/rate_limiter.rb +183 -0
- data/lib/datadog/core/remote/client/capabilities.rb +4 -3
- data/lib/datadog/core/remote/component.rb +4 -2
- data/lib/datadog/core/remote/negotiation.rb +4 -4
- data/lib/datadog/core/remote/tie.rb +2 -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 +5 -1
- data/lib/datadog/core/semaphore.rb +35 -0
- data/lib/datadog/core/telemetry/component.rb +2 -0
- data/lib/datadog/core/telemetry/event.rb +12 -7
- data/lib/datadog/core/telemetry/logger.rb +51 -0
- data/lib/datadog/core/telemetry/logging.rb +50 -14
- data/lib/datadog/core/telemetry/request.rb +13 -1
- data/lib/datadog/core/transport/ext.rb +1 -0
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/di/code_tracker.rb +166 -0
- data/lib/datadog/di/configuration/settings.rb +163 -0
- data/lib/datadog/di/configuration.rb +11 -0
- data/lib/datadog/di/error.rb +31 -0
- data/lib/datadog/di/extensions.rb +16 -0
- data/lib/datadog/di/instrumenter.rb +301 -0
- data/lib/datadog/di/probe.rb +162 -0
- data/lib/datadog/di/probe_builder.rb +47 -0
- data/lib/datadog/di/probe_notification_builder.rb +207 -0
- data/lib/datadog/di/probe_notifier_worker.rb +244 -0
- data/lib/datadog/di/redactor.rb +188 -0
- data/lib/datadog/di/serializer.rb +215 -0
- data/lib/datadog/di/transport.rb +67 -0
- data/lib/datadog/di/utils.rb +39 -0
- data/lib/datadog/di.rb +57 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -10
- data/lib/datadog/profiling/collectors/info.rb +12 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +32 -8
- data/lib/datadog/profiling/component.rb +21 -4
- data/lib/datadog/profiling/http_transport.rb +6 -1
- data/lib/datadog/profiling/scheduler.rb +2 -0
- data/lib/datadog/profiling/stack_recorder.rb +40 -9
- data/lib/datadog/single_step_instrument.rb +12 -0
- data/lib/datadog/tracing/component.rb +13 -0
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- 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 +12 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +24 -2
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
- data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +13 -9
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +6 -3
- data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +9 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +22 -15
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -5
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +9 -0
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -2
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
- data/lib/datadog/tracing/contrib/patcher.rb +2 -1
- data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
- data/lib/datadog/tracing/distributed/propagation.rb +7 -0
- data/lib/datadog/tracing/metadata/ext.rb +2 -0
- data/lib/datadog/tracing/remote.rb +5 -2
- data/lib/datadog/tracing/sampling/matcher.rb +6 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/rule.rb +2 -0
- data/lib/datadog/tracing/sampling/rule_sampler.rb +15 -9
- data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
- data/lib/datadog/tracing/trace_operation.rb +26 -2
- data/lib/datadog/tracing/tracer.rb +29 -22
- data/lib/datadog/tracing/transport/http/client.rb +1 -0
- data/lib/datadog/tracing/transport/http.rb +4 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
- data/lib/datadog/tracing/workers.rb +2 -2
- data/lib/datadog/tracing/writer.rb +26 -28
- data/lib/datadog/version.rb +1 -1
- metadata +40 -15
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -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
@@ -52,7 +52,7 @@ module Datadog
|
|
52
52
|
# ensure rate limiter is called only when there are events to record
|
53
53
|
return if events.empty? || span.nil?
|
54
54
|
|
55
|
-
Datadog::AppSec::RateLimiter.limit
|
55
|
+
Datadog::AppSec::RateLimiter.thread_local.limit do
|
56
56
|
record_via_span(span, *events)
|
57
57
|
end
|
58
58
|
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
|
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,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
|
@@ -9,7 +9,7 @@ module Datadog
|
|
9
9
|
# that load appsec rules and data from settings
|
10
10
|
module RuleLoader
|
11
11
|
class << self
|
12
|
-
def load_rules(ruleset:)
|
12
|
+
def load_rules(ruleset:, telemetry:)
|
13
13
|
begin
|
14
14
|
case ruleset
|
15
15
|
when :recommended, :strict
|
@@ -35,6 +35,8 @@ module Datadog
|
|
35
35
|
"libddwaf ruleset failed to load, ruleset: #{ruleset.inspect} error: #{e.inspect}"
|
36
36
|
end
|
37
37
|
|
38
|
+
telemetry.report(e, description: 'libddwaf ruleset failed to load')
|
39
|
+
|
38
40
|
nil
|
39
41
|
end
|
40
42
|
end
|
@@ -18,25 +18,35 @@ module Datadog
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
DEFAULT_WAF_PROCESSORS = begin
|
22
|
-
JSON.parse(Datadog::AppSec::Assets.waf_processors)
|
23
|
-
rescue StandardError => e
|
24
|
-
Datadog.logger.error { "libddwaf rulemerger failed to parse default waf processors. Error: #{e.inspect}" }
|
25
|
-
[]
|
26
|
-
end
|
27
|
-
|
28
|
-
DEFAULT_WAF_SCANNERS = begin
|
29
|
-
JSON.parse(Datadog::AppSec::Assets.waf_scanners)
|
30
|
-
rescue StandardError => e
|
31
|
-
Datadog.logger.error { "libddwaf rulemerger failed to parse default waf scanners. Error: #{e.inspect}" }
|
32
|
-
[]
|
33
|
-
end
|
34
|
-
|
35
21
|
class << self
|
22
|
+
# TODO: `processors` and `scanners` are not provided by the caller, consider removing them
|
36
23
|
def merge(
|
24
|
+
telemetry:,
|
37
25
|
rules:, data: [], overrides: [], exclusions: [], custom_rules: [],
|
38
|
-
processors:
|
26
|
+
processors: nil, scanners: nil
|
39
27
|
)
|
28
|
+
processors ||= begin
|
29
|
+
default_waf_processors
|
30
|
+
rescue StandardError => e
|
31
|
+
Datadog.logger.error("libddwaf rulemerger failed to parse default waf processors. Error: #{e.inspect}")
|
32
|
+
telemetry.report(
|
33
|
+
e,
|
34
|
+
description: 'libddwaf rulemerger failed to parse default waf processors'
|
35
|
+
)
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
|
39
|
+
scanners ||= begin
|
40
|
+
default_waf_scanners
|
41
|
+
rescue StandardError => e
|
42
|
+
Datadog.logger.error("libddwaf rulemerger failed to parse default waf scanners. Error: #{e.inspect}")
|
43
|
+
telemetry.report(
|
44
|
+
e,
|
45
|
+
description: 'libddwaf rulemerger failed to parse default waf scanners'
|
46
|
+
)
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
40
50
|
combined_rules = combine_rules(rules)
|
41
51
|
|
42
52
|
combined_data = combine_data(data) if data.any?
|
@@ -53,6 +63,14 @@ module Datadog
|
|
53
63
|
combined_rules
|
54
64
|
end
|
55
65
|
|
66
|
+
def default_waf_processors
|
67
|
+
@default_waf_processors ||= JSON.parse(Datadog::AppSec::Assets.waf_processors)
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_waf_scanners
|
71
|
+
@default_waf_scanners ||= JSON.parse(Datadog::AppSec::Assets.waf_scanners)
|
72
|
+
end
|
73
|
+
|
56
74
|
private
|
57
75
|
|
58
76
|
def combine_rules(rules)
|
@@ -1,85 +1,23 @@
|
|
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
|
-
def initialize(ruleset:)
|
11
|
+
def initialize(ruleset:, telemetry:)
|
12
|
+
@telemetry = telemetry
|
77
13
|
@diagnostics = nil
|
78
14
|
@addresses = []
|
15
|
+
|
79
16
|
settings = Datadog.configuration.appsec
|
80
17
|
|
81
|
-
|
82
|
-
|
18
|
+
# TODO: Refactor to make it easier to test
|
19
|
+
unless require_libddwaf && libddwaf_provides_waf? && create_waf_handle(settings, ruleset)
|
20
|
+
Datadog.logger.warn('AppSec is disabled, see logged errors above')
|
83
21
|
end
|
84
22
|
end
|
85
23
|
|
@@ -91,14 +29,33 @@ module Datadog
|
|
91
29
|
@handle.finalize
|
92
30
|
end
|
93
31
|
|
94
|
-
|
95
|
-
|
96
|
-
|
32
|
+
def new_context
|
33
|
+
Context.new(@handle, telemetry: @telemetry)
|
34
|
+
end
|
97
35
|
|
98
36
|
private
|
99
37
|
|
100
|
-
|
101
|
-
|
38
|
+
# libddwaf raises a LoadError on unsupported platforms; it may at some
|
39
|
+
# point succeed in being required yet not provide a specific needed feature.
|
40
|
+
def require_libddwaf
|
41
|
+
Datadog.logger.debug { "libddwaf platform: #{libddwaf_platform}" }
|
42
|
+
|
43
|
+
require 'libddwaf'
|
44
|
+
|
45
|
+
true
|
46
|
+
rescue LoadError => e
|
47
|
+
Datadog.logger.error do
|
48
|
+
'libddwaf failed to load,' \
|
49
|
+
"installed platform: #{libddwaf_platform} ruby platforms: #{ruby_platforms} error: #{e.inspect}"
|
50
|
+
end
|
51
|
+
@telemetry.report(e, description: 'libddwaf failed to load')
|
52
|
+
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# check whether libddwaf is required *and* able to provide the needed feature
|
57
|
+
def libddwaf_provides_waf?
|
58
|
+
defined?(Datadog::AppSec::WAF) ? true : false
|
102
59
|
end
|
103
60
|
|
104
61
|
def create_waf_handle(settings, ruleset)
|
@@ -119,6 +76,7 @@ module Datadog
|
|
119
76
|
Datadog.logger.error do
|
120
77
|
"libddwaf failed to initialize, error: #{e.inspect}"
|
121
78
|
end
|
79
|
+
@telemetry.report(e, description: 'libddwaf failed to initialize')
|
122
80
|
|
123
81
|
@diagnostics = e.diagnostics if e.diagnostics
|
124
82
|
|
@@ -127,44 +85,21 @@ module Datadog
|
|
127
85
|
Datadog.logger.error do
|
128
86
|
"libddwaf failed to initialize, error: #{e.inspect}"
|
129
87
|
end
|
88
|
+
@telemetry.report(e, description: 'libddwaf failed to initialize')
|
130
89
|
|
131
90
|
false
|
132
91
|
end
|
133
92
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# libddwaf raises a LoadError on unsupported platforms; it may at some
|
141
|
-
# point succeed in being required yet not provide a specific needed feature.
|
142
|
-
def require_libddwaf
|
143
|
-
Datadog.logger.debug { "libddwaf platform: #{libddwaf_platform}" }
|
144
|
-
|
145
|
-
require 'libddwaf'
|
146
|
-
|
147
|
-
true
|
148
|
-
rescue LoadError => e
|
149
|
-
Datadog.logger.error do
|
150
|
-
'libddwaf failed to load,' \
|
151
|
-
"installed platform: #{libddwaf_platform} ruby platforms: #{ruby_platforms} error: #{e.inspect}"
|
152
|
-
end
|
153
|
-
|
154
|
-
false
|
155
|
-
end
|
156
|
-
|
157
|
-
def libddwaf_spec
|
158
|
-
Gem.loaded_specs['libddwaf']
|
159
|
-
end
|
160
|
-
|
161
|
-
def libddwaf_platform
|
162
|
-
libddwaf_spec ? libddwaf_spec.platform.to_s : 'unknown'
|
93
|
+
def libddwaf_platform
|
94
|
+
if Gem.loaded_specs['libddwaf']
|
95
|
+
Gem.loaded_specs['libddwaf'].platform.to_s
|
96
|
+
else
|
97
|
+
'unknown'
|
163
98
|
end
|
99
|
+
end
|
164
100
|
|
165
|
-
|
166
|
-
|
167
|
-
end
|
101
|
+
def ruby_platforms
|
102
|
+
Gem.platforms.map(&:to_s)
|
168
103
|
end
|
169
104
|
end
|
170
105
|
end
|