datadog 2.20.0 → 2.22.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 +73 -1
- data/README.md +0 -1
- data/ext/LIBDATADOG_DEVELOPMENT.md +60 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/init.c +3 -0
- data/ext/libdatadog_api/library_config.c +35 -27
- data/ext/libdatadog_api/process_discovery.c +24 -18
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +6 -2
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
- data/lib/datadog/appsec/event.rb +12 -14
- data/lib/datadog/appsec/metrics/collector.rb +19 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/remote.rb +25 -13
- data/lib/datadog/appsec/security_engine/result.rb +28 -9
- data/lib/datadog/appsec/security_engine/runner.rb +17 -7
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
- data/lib/datadog/core/configuration/components.rb +22 -8
- data/lib/datadog/core/configuration/config_helper.rb +100 -0
- data/lib/datadog/core/configuration/deprecations.rb +36 -0
- data/lib/datadog/core/configuration/ext.rb +0 -1
- data/lib/datadog/core/configuration/option.rb +38 -43
- data/lib/datadog/core/configuration/option_definition.rb +0 -9
- data/lib/datadog/core/configuration/options.rb +1 -5
- data/lib/datadog/core/configuration/settings.rb +10 -6
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +337 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/ddsketch.rb +21 -0
- data/lib/datadog/core/deprecations.rb +2 -2
- data/lib/datadog/core/environment/ext.rb +0 -2
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/variable_helpers.rb +3 -3
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/metrics/client.rb +2 -2
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
- data/lib/datadog/core/process_discovery.rb +48 -23
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/runtime/ext.rb +0 -1
- data/lib/datadog/core/telemetry/component.rb +11 -0
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/network.rb +19 -0
- data/lib/datadog/core.rb +2 -0
- data/lib/datadog/di/boot.rb +5 -3
- data/lib/datadog/di/component.rb +14 -0
- data/lib/datadog/di/context.rb +70 -0
- data/lib/datadog/di/el/compiler.rb +164 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +25 -0
- data/lib/datadog/di/instrumenter.rb +101 -32
- data/lib/datadog/di/probe.rb +35 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_file_loader.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +3 -2
- data/lib/datadog/di/probe_notification_builder.rb +50 -51
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
- data/lib/datadog/profiling/collectors/info.rb +1 -1
- data/lib/datadog/profiling/ext.rb +2 -1
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/profiling/tasks/exec.rb +2 -2
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +0 -3
- data/lib/datadog/tracing/configuration/settings.rb +15 -10
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +63 -27
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
- data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/version.rb +1 -1
- metadata +25 -9
- data/ext/libdatadog_api/macos_development.md +0 -26
@@ -352,6 +352,15 @@ module Datadog
|
|
352
352
|
o.default true
|
353
353
|
end
|
354
354
|
|
355
|
+
settings :endpoint_collection do
|
356
|
+
# Enables reporting of application routes at application start via telemetry
|
357
|
+
option :enabled do |o|
|
358
|
+
o.type :bool, nilable: true
|
359
|
+
o.env 'DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED'
|
360
|
+
o.default true
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
355
364
|
# NOTE: Unfortunately, we have to go with Float due to other libs
|
356
365
|
# setup, even tho we don't plan to support sub-second delays.
|
357
366
|
#
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../event'
|
4
|
+
require_relative '../../trace_keeper'
|
4
5
|
require_relative '../../security_event'
|
5
6
|
|
6
7
|
module Datadog
|
@@ -31,7 +32,8 @@ module Datadog
|
|
31
32
|
result = context.run_rasp(Ext::RASP_SQLI, {}, ephemeral_data, waf_timeout)
|
32
33
|
|
33
34
|
if result.match?
|
34
|
-
AppSec::Event.
|
35
|
+
AppSec::Event.tag(context, result)
|
36
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
35
37
|
|
36
38
|
context.events.push(
|
37
39
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'excon'
|
4
4
|
|
5
5
|
require_relative '../../event'
|
6
|
+
require_relative '../../trace_keeper'
|
6
7
|
require_relative '../../security_event'
|
7
8
|
|
8
9
|
module Datadog
|
@@ -22,7 +23,8 @@ module Datadog
|
|
22
23
|
result = context.run_rasp(Ext::RASP_SSRF, {}, ephemeral_data, Datadog.configuration.appsec.waf_timeout)
|
23
24
|
|
24
25
|
if result.match?
|
25
|
-
AppSec::Event.
|
26
|
+
AppSec::Event.tag(context, result)
|
27
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
26
28
|
|
27
29
|
context.events.push(
|
28
30
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -38,4 +40,3 @@ module Datadog
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
41
|
-
# rubocop:enable Naming/FileName
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../event'
|
4
|
+
require_relative '../../trace_keeper'
|
4
5
|
require_relative '../../security_event'
|
5
6
|
|
6
7
|
module Datadog
|
@@ -21,7 +22,8 @@ module Datadog
|
|
21
22
|
result = context.run_rasp(Ext::RASP_SSRF, {}, ephemeral_data, Datadog.configuration.appsec.waf_timeout)
|
22
23
|
|
23
24
|
if result.match?
|
24
|
-
AppSec::Event.
|
25
|
+
AppSec::Event.tag(context, result)
|
26
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
25
27
|
|
26
28
|
context.events.push(
|
27
29
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
require_relative '../../../event'
|
6
|
+
require_relative '../../../trace_keeper'
|
6
7
|
require_relative '../../../security_event'
|
7
8
|
require_relative '../../../instrumentation/gateway'
|
8
9
|
|
@@ -32,7 +33,8 @@ module Datadog
|
|
32
33
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
33
34
|
|
34
35
|
if result.match?
|
35
|
-
AppSec::Event.
|
36
|
+
AppSec::Event.tag(context, result)
|
37
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
36
38
|
|
37
39
|
context.events.push(
|
38
40
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../ext'
|
4
4
|
require_relative '../../../event'
|
5
|
+
require_relative '../../../trace_keeper'
|
5
6
|
require_relative '../../../security_event'
|
6
7
|
require_relative '../../../instrumentation/gateway'
|
7
8
|
|
@@ -38,14 +39,16 @@ module Datadog
|
|
38
39
|
|
39
40
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
40
41
|
|
41
|
-
if result.match? || !result.
|
42
|
+
if result.match? || !result.attributes.empty?
|
42
43
|
context.events.push(
|
43
44
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
44
45
|
)
|
45
46
|
end
|
46
47
|
|
47
48
|
if result.match?
|
48
|
-
AppSec::Event.
|
49
|
+
AppSec::Event.tag(context, result)
|
50
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
51
|
+
|
49
52
|
AppSec::ActionsHandler.handle(result.actions)
|
50
53
|
end
|
51
54
|
|
@@ -66,7 +69,8 @@ module Datadog
|
|
66
69
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
67
70
|
|
68
71
|
if result.match?
|
69
|
-
AppSec::Event.
|
72
|
+
AppSec::Event.tag(context, result)
|
73
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
70
74
|
|
71
75
|
context.events.push(
|
72
76
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -90,7 +94,8 @@ module Datadog
|
|
90
94
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
91
95
|
|
92
96
|
if result.match?
|
93
|
-
AppSec::Event.
|
97
|
+
AppSec::Event.tag(context, result)
|
98
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
94
99
|
|
95
100
|
context.events.push(
|
96
101
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -161,7 +161,7 @@ module Datadog
|
|
161
161
|
if context.waf_runner_ruleset_version
|
162
162
|
span.set_tag('_dd.appsec.event_rules.version', context.waf_runner_ruleset_version)
|
163
163
|
|
164
|
-
unless
|
164
|
+
unless oneshot_tags_sent?
|
165
165
|
# Small race condition, but it's inoccuous: worst case the tags
|
166
166
|
# are sent a couple of times more than expected
|
167
167
|
@oneshot_tags_sent = true
|
@@ -204,6 +204,10 @@ module Datadog
|
|
204
204
|
end
|
205
205
|
# standard:enable Metrics/MethodLength
|
206
206
|
|
207
|
+
def oneshot_tags_sent?
|
208
|
+
@oneshot_tags_sent
|
209
|
+
end
|
210
|
+
|
207
211
|
def to_rack_header(header)
|
208
212
|
@rack_headers[header] ||= Datadog::Tracing::Contrib::Rack::Header.to_rack_header(header)
|
209
213
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../../event'
|
4
|
+
require_relative '../../../trace_keeper'
|
4
5
|
require_relative '../../../security_event'
|
5
6
|
require_relative '../../../instrumentation/gateway'
|
6
7
|
|
@@ -35,7 +36,9 @@ module Datadog
|
|
35
36
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
36
37
|
)
|
37
38
|
|
38
|
-
AppSec::Event.
|
39
|
+
AppSec::Event.tag(context, result)
|
40
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
41
|
+
|
39
42
|
AppSec::ActionsHandler.handle(result.actions)
|
40
43
|
end
|
41
44
|
|
@@ -57,7 +60,9 @@ module Datadog
|
|
57
60
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
58
61
|
)
|
59
62
|
|
60
|
-
AppSec::Event.
|
63
|
+
AppSec::Event.tag(context, result)
|
64
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
65
|
+
|
61
66
|
AppSec::ActionsHandler.handle(result.actions)
|
62
67
|
end
|
63
68
|
|
@@ -10,6 +10,7 @@ require_relative 'gateway/watcher'
|
|
10
10
|
require_relative 'gateway/request'
|
11
11
|
require_relative 'patches/render_to_body_patch'
|
12
12
|
require_relative 'patches/process_action_patch'
|
13
|
+
require_relative '../../api_security/endpoint_collection/rails_collector'
|
13
14
|
|
14
15
|
require_relative '../../../tracing/contrib/rack/middlewares'
|
15
16
|
|
@@ -20,6 +21,7 @@ module Datadog
|
|
20
21
|
# Patcher for AppSec on Rails
|
21
22
|
module Patcher
|
22
23
|
GUARD_ACTION_CONTROLLER_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
|
24
|
+
GUARD_ROUTES_REPORTING_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
|
23
25
|
BEFORE_INITIALIZE_ONLY_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
|
24
26
|
AFTER_INITIALIZE_ONLY_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
|
25
27
|
|
@@ -38,6 +40,7 @@ module Datadog
|
|
38
40
|
patch_before_initialize
|
39
41
|
patch_after_initialize
|
40
42
|
patch_action_controller
|
43
|
+
subscribe_to_routes_loaded
|
41
44
|
|
42
45
|
Patcher.instance_variable_set(:@patched, true)
|
43
46
|
end
|
@@ -128,7 +131,34 @@ module Datadog
|
|
128
131
|
GUARD_ACTION_CONTROLLER_ONCE_PER_APP[self].run do
|
129
132
|
::ActionController::Base.prepend(Patches::RenderToBodyPatch)
|
130
133
|
end
|
134
|
+
|
135
|
+
# Rails 7.1 adds `after_routes_loaded` hook
|
136
|
+
if Datadog::AppSec::Contrib::Rails::Patcher.target_version < Gem::Version.new('7.1')
|
137
|
+
Datadog::AppSec::Contrib::Rails::Patcher.report_routes_via_telemetry(::Rails.application.routes.routes)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def subscribe_to_routes_loaded
|
143
|
+
::ActiveSupport.on_load(:after_routes_loaded) do |app|
|
144
|
+
Datadog::AppSec::Contrib::Rails::Patcher.report_routes_via_telemetry(app.routes.routes)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def report_routes_via_telemetry(routes)
|
149
|
+
# We do not support Rails 4.x for Endpoint Collection,
|
150
|
+
# mainly because the Route#verb was a Regexp before Rails 5.0
|
151
|
+
return if target_version < Gem::Version.new('5.0')
|
152
|
+
return unless Datadog.configuration.appsec.api_security.endpoint_collection.enabled
|
153
|
+
return unless AppSec.telemetry
|
154
|
+
|
155
|
+
GUARD_ROUTES_REPORTING_ONCE_PER_APP[::Rails.application].run do
|
156
|
+
AppSec.telemetry.app_endpoints_loaded(
|
157
|
+
APISecurity::EndpointCollection::RailsCollector.new(routes).to_enum
|
158
|
+
)
|
131
159
|
end
|
160
|
+
rescue => e
|
161
|
+
AppSec.telemetry&.report(e, description: 'failed to report application endpoints')
|
132
162
|
end
|
133
163
|
|
134
164
|
def setup_security
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../event'
|
4
|
+
require_relative '../../trace_keeper'
|
4
5
|
require_relative '../../security_event'
|
5
6
|
|
6
7
|
module Datadog
|
@@ -18,7 +19,8 @@ module Datadog
|
|
18
19
|
result = context.run_rasp(Ext::RASP_SSRF, {}, ephemeral_data, Datadog.configuration.appsec.waf_timeout)
|
19
20
|
|
20
21
|
if result.match?
|
21
|
-
AppSec::Event.
|
22
|
+
AppSec::Event.tag(context, result)
|
23
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
22
24
|
|
23
25
|
context.events.push(
|
24
26
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../../../event'
|
4
|
+
require_relative '../../../trace_keeper'
|
4
5
|
require_relative '../../../security_event'
|
5
6
|
require_relative '../../../instrumentation/gateway'
|
6
7
|
|
@@ -30,14 +31,16 @@ module Datadog
|
|
30
31
|
|
31
32
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
32
33
|
|
33
|
-
if result.match? || !result.
|
34
|
+
if result.match? || !result.attributes.empty?
|
34
35
|
context.events.push(
|
35
36
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
36
37
|
)
|
37
38
|
end
|
38
39
|
|
39
40
|
if result.match?
|
40
|
-
AppSec::Event.
|
41
|
+
AppSec::Event.tag(context, result)
|
42
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
43
|
+
|
41
44
|
AppSec::ActionsHandler.handle(result.actions)
|
42
45
|
end
|
43
46
|
|
@@ -56,7 +59,8 @@ module Datadog
|
|
56
59
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
57
60
|
|
58
61
|
if result.match?
|
59
|
-
AppSec::Event.
|
62
|
+
AppSec::Event.tag(context, result)
|
63
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
60
64
|
|
61
65
|
context.events.push(
|
62
66
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
@@ -83,7 +87,9 @@ module Datadog
|
|
83
87
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
84
88
|
)
|
85
89
|
|
86
|
-
AppSec::Event.
|
90
|
+
AppSec::Event.tag(context, result)
|
91
|
+
TraceKeeper.keep!(context.trace) if result.keep?
|
92
|
+
|
87
93
|
AppSec::ActionsHandler.handle(result.actions)
|
88
94
|
end
|
89
95
|
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -9,8 +9,8 @@ module Datadog
|
|
9
9
|
module AppSec
|
10
10
|
# AppSec event
|
11
11
|
module Event
|
12
|
-
|
13
|
-
|
12
|
+
ATTRIBUTES_SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
|
13
|
+
ATTRIBUTES_SCHEMA_MAX_COMPRESSED_SIZE = 25000
|
14
14
|
ALLOWED_REQUEST_HEADERS = %w[
|
15
15
|
x-forwarded-for
|
16
16
|
x-client-ip
|
@@ -40,16 +40,14 @@ module Datadog
|
|
40
40
|
].freeze
|
41
41
|
|
42
42
|
class << self
|
43
|
-
def
|
44
|
-
|
43
|
+
def tag(context, waf_result)
|
44
|
+
return if context.span.nil?
|
45
45
|
|
46
|
-
if
|
47
|
-
|
48
|
-
context.span.set_tag('appsec.blocked', 'true')
|
49
|
-
end
|
50
|
-
|
51
|
-
context.span.set_tag('appsec.event', 'true')
|
46
|
+
if waf_result.actions.key?('block_request') || waf_result.actions.key?('redirect_request')
|
47
|
+
context.span.set_tag('appsec.blocked', 'true')
|
52
48
|
end
|
49
|
+
|
50
|
+
context.span.set_tag('appsec.event', 'true')
|
53
51
|
end
|
54
52
|
|
55
53
|
def record(context, request: nil, response: nil)
|
@@ -63,7 +61,7 @@ module Datadog
|
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
66
|
-
if event_group.any? { |event| event.
|
64
|
+
if event_group.any? { |event| event.keep? || event.schema? }
|
67
65
|
TraceKeeper.keep!(trace)
|
68
66
|
|
69
67
|
context.span['_dd.origin'] = 'appsec'
|
@@ -106,13 +104,13 @@ module Datadog
|
|
106
104
|
tags = security_events.each_with_object({}) do |security_event, memo|
|
107
105
|
triggers.concat(security_event.waf_result.events)
|
108
106
|
|
109
|
-
security_event.waf_result.
|
110
|
-
next memo[key] = value unless key.start_with?(
|
107
|
+
security_event.waf_result.attributes.each do |key, value|
|
108
|
+
next memo[key] = value unless key.start_with?(ATTRIBUTES_SCHEMA_KEY_PREFIX)
|
111
109
|
|
112
110
|
value = CompressedJson.dump(value)
|
113
111
|
next if value.nil?
|
114
112
|
|
115
|
-
if value.size >=
|
113
|
+
if value.size >= ATTRIBUTES_SCHEMA_MAX_COMPRESSED_SIZE
|
116
114
|
Datadog.logger.debug { "AppSec: Schema key '#{key}' will not be included into span tags due to it's size" }
|
117
115
|
next
|
118
116
|
end
|
@@ -5,14 +5,28 @@ module Datadog
|
|
5
5
|
module Metrics
|
6
6
|
# A class responsible for collecting WAF and RASP call metrics.
|
7
7
|
class Collector
|
8
|
-
Store = Struct.new(
|
8
|
+
Store = Struct.new(
|
9
|
+
:evals,
|
10
|
+
:matches,
|
11
|
+
:errors,
|
12
|
+
:timeouts,
|
13
|
+
:duration_ns,
|
14
|
+
:duration_ext_ns,
|
15
|
+
:inputs_truncated,
|
16
|
+
keyword_init: true
|
17
|
+
)
|
9
18
|
|
10
19
|
attr_reader :waf, :rasp
|
11
20
|
|
12
21
|
def initialize
|
13
22
|
@mutex = Mutex.new
|
14
|
-
|
15
|
-
@
|
23
|
+
|
24
|
+
@waf = Store.new(
|
25
|
+
evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0, inputs_truncated: 0
|
26
|
+
)
|
27
|
+
@rasp = Store.new(
|
28
|
+
evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0, inputs_truncated: 0
|
29
|
+
)
|
16
30
|
end
|
17
31
|
|
18
32
|
def record_waf(result)
|
@@ -23,6 +37,7 @@ module Datadog
|
|
23
37
|
@waf.timeouts += 1 if result.timeout?
|
24
38
|
@waf.duration_ns += result.duration_ns
|
25
39
|
@waf.duration_ext_ns += result.duration_ext_ns
|
40
|
+
@waf.inputs_truncated += 1 if result.input_truncated?
|
26
41
|
end
|
27
42
|
end
|
28
43
|
|
@@ -34,6 +49,7 @@ module Datadog
|
|
34
49
|
@rasp.timeouts += 1 if result.timeout?
|
35
50
|
@rasp.duration_ns += result.duration_ns
|
36
51
|
@rasp.duration_ext_ns += result.duration_ext_ns
|
52
|
+
@rasp.inputs_truncated += 1 if result.input_truncated?
|
37
53
|
end
|
38
54
|
end
|
39
55
|
end
|
@@ -18,7 +18,8 @@ module Datadog
|
|
18
18
|
waf_timeout: metrics.timeouts.positive?.to_s,
|
19
19
|
request_blocked: context.interrupted?.to_s,
|
20
20
|
block_failure: 'false',
|
21
|
-
rate_limited: (!context.trace.sampled?).to_s
|
21
|
+
rate_limited: (!context.trace.sampled?).to_s,
|
22
|
+
input_truncated: metrics.inputs_truncated.positive?.to_s,
|
22
23
|
}
|
23
24
|
)
|
24
25
|
end
|
@@ -39,14 +39,14 @@ module Datadog
|
|
39
39
|
|
40
40
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
41
41
|
|
42
|
-
if result.match? || result.
|
42
|
+
if result.match? || result.attributes.any?
|
43
43
|
context.events.push(
|
44
44
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
45
45
|
)
|
46
46
|
end
|
47
47
|
|
48
48
|
if result.match?
|
49
|
-
AppSec::Event.
|
49
|
+
AppSec::Event.tag(context, result)
|
50
50
|
AppSec::ActionsHandler.handle(result.actions)
|
51
51
|
end
|
52
52
|
|
@@ -63,14 +63,14 @@ module Datadog
|
|
63
63
|
persistent_data = {"server.business_logic.#{kind}" => ARBITRARY_VALUE}
|
64
64
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
65
65
|
|
66
|
-
if result.match? || result.
|
66
|
+
if result.match? || result.attributes.any?
|
67
67
|
context.events.push(
|
68
68
|
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
69
69
|
)
|
70
70
|
end
|
71
71
|
|
72
72
|
if result.match?
|
73
|
-
AppSec::Event.
|
73
|
+
AppSec::Event.tag(context, result)
|
74
74
|
AppSec::ActionsHandler.handle(result.actions)
|
75
75
|
end
|
76
76
|
|
@@ -12,19 +12,25 @@ module Datadog
|
|
12
12
|
class NoRulesError < StandardError; end
|
13
13
|
|
14
14
|
class << self
|
15
|
-
CAP_ASM_RESERVED_1 = 1 << 0
|
16
|
-
CAP_ASM_ACTIVATION = 1 << 1
|
17
|
-
CAP_ASM_IP_BLOCKING = 1 << 2
|
18
|
-
CAP_ASM_DD_RULES = 1 << 3
|
19
|
-
CAP_ASM_EXCLUSIONS = 1 << 4
|
20
|
-
CAP_ASM_REQUEST_BLOCKING = 1 << 5
|
21
|
-
CAP_ASM_RESPONSE_BLOCKING = 1 << 6
|
22
|
-
CAP_ASM_USER_BLOCKING = 1 << 7
|
23
|
-
CAP_ASM_CUSTOM_RULES = 1 << 8
|
24
|
-
CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9
|
25
|
-
CAP_ASM_TRUSTED_IPS = 1 << 10
|
26
|
-
CAP_ASM_RASP_SSRF = 1 << 23
|
27
|
-
CAP_ASM_RASP_SQLI = 1 << 21
|
15
|
+
CAP_ASM_RESERVED_1 = 1 << 0
|
16
|
+
CAP_ASM_ACTIVATION = 1 << 1
|
17
|
+
CAP_ASM_IP_BLOCKING = 1 << 2
|
18
|
+
CAP_ASM_DD_RULES = 1 << 3
|
19
|
+
CAP_ASM_EXCLUSIONS = 1 << 4
|
20
|
+
CAP_ASM_REQUEST_BLOCKING = 1 << 5
|
21
|
+
CAP_ASM_RESPONSE_BLOCKING = 1 << 6
|
22
|
+
CAP_ASM_USER_BLOCKING = 1 << 7
|
23
|
+
CAP_ASM_CUSTOM_RULES = 1 << 8
|
24
|
+
CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9
|
25
|
+
CAP_ASM_TRUSTED_IPS = 1 << 10
|
26
|
+
CAP_ASM_RASP_SSRF = 1 << 23
|
27
|
+
CAP_ASM_RASP_SQLI = 1 << 21
|
28
|
+
CAP_ASM_AUTO_USER_INSTRUM_MODE = 1 << 31
|
29
|
+
CAP_ASM_ENDPOINT_FINGERPRINT = 1 << 32
|
30
|
+
CAP_ASM_SESSION_FINGERPRINT = 1 << 33
|
31
|
+
CAP_ASM_NETWORK_FINGERPRINT = 1 << 34
|
32
|
+
CAP_ASM_HEADER_FINGERPRINT = 1 << 35
|
33
|
+
CAP_ASM_TRACE_TAGGING_RULES = 1 << 43
|
28
34
|
|
29
35
|
# TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
|
30
36
|
ASM_CAPABILITIES = [
|
@@ -39,6 +45,12 @@ module Datadog
|
|
39
45
|
CAP_ASM_TRUSTED_IPS,
|
40
46
|
CAP_ASM_RASP_SSRF,
|
41
47
|
CAP_ASM_RASP_SQLI,
|
48
|
+
CAP_ASM_AUTO_USER_INSTRUM_MODE,
|
49
|
+
CAP_ASM_ENDPOINT_FINGERPRINT,
|
50
|
+
CAP_ASM_SESSION_FINGERPRINT,
|
51
|
+
CAP_ASM_NETWORK_FINGERPRINT,
|
52
|
+
CAP_ASM_HEADER_FINGERPRINT,
|
53
|
+
CAP_ASM_TRACE_TAGGING_RULES,
|
42
54
|
].freeze
|
43
55
|
|
44
56
|
ASM_PRODUCTS = [
|
@@ -7,20 +7,30 @@ module Datadog
|
|
7
7
|
module Result
|
8
8
|
# A generic result without indication of its type.
|
9
9
|
class Base
|
10
|
-
attr_reader :events, :actions, :
|
10
|
+
attr_reader :events, :actions, :attributes, :duration_ns, :duration_ext_ns
|
11
11
|
|
12
|
-
def initialize(events:, actions:,
|
12
|
+
def initialize(events:, actions:, attributes:, duration_ns:, duration_ext_ns:, timeout:, keep:, input_truncated:)
|
13
13
|
@events = events
|
14
14
|
@actions = actions
|
15
|
-
@
|
16
|
-
|
17
|
-
@timeout = timeout
|
15
|
+
@attributes = attributes
|
18
16
|
@duration_ns = duration_ns
|
19
17
|
@duration_ext_ns = duration_ext_ns
|
18
|
+
|
19
|
+
@keep = !!keep
|
20
|
+
@timeout = !!timeout
|
21
|
+
@input_truncated = !!input_truncated
|
20
22
|
end
|
21
23
|
|
22
24
|
def timeout?
|
23
|
-
|
25
|
+
@timeout
|
26
|
+
end
|
27
|
+
|
28
|
+
def keep?
|
29
|
+
@keep
|
30
|
+
end
|
31
|
+
|
32
|
+
def input_truncated?
|
33
|
+
@input_truncated
|
24
34
|
end
|
25
35
|
|
26
36
|
def match?
|
@@ -56,19 +66,28 @@ module Datadog
|
|
56
66
|
|
57
67
|
# A result that indicates an internal security library error
|
58
68
|
class Error
|
59
|
-
attr_reader :events, :actions, :
|
69
|
+
attr_reader :events, :actions, :attributes, :duration_ns, :duration_ext_ns
|
60
70
|
|
61
|
-
def initialize(duration_ext_ns:)
|
71
|
+
def initialize(duration_ext_ns:, input_truncated:)
|
62
72
|
@events = []
|
63
|
-
@actions = @
|
73
|
+
@actions = @attributes = {}
|
64
74
|
@duration_ns = 0
|
65
75
|
@duration_ext_ns = duration_ext_ns
|
76
|
+
@input_truncated = !!input_truncated
|
77
|
+
end
|
78
|
+
|
79
|
+
def keep?
|
80
|
+
false
|
66
81
|
end
|
67
82
|
|
68
83
|
def timeout?
|
69
84
|
false
|
70
85
|
end
|
71
86
|
|
87
|
+
def input_truncated?
|
88
|
+
@input_truncated
|
89
|
+
end
|
90
|
+
|
72
91
|
def match?
|
73
92
|
false
|
74
93
|
end
|
@@ -42,17 +42,19 @@ module Datadog
|
|
42
42
|
report_execution(result)
|
43
43
|
|
44
44
|
unless SUCCESSFUL_EXECUTION_CODES.include?(result.status)
|
45
|
-
return Result::Error.new(duration_ext_ns: stop_ns - start_ns)
|
45
|
+
return Result::Error.new(duration_ext_ns: stop_ns - start_ns, input_truncated: result.input_truncated?)
|
46
46
|
end
|
47
47
|
|
48
48
|
klass = (result.status == :match) ? Result::Match : Result::Ok
|
49
49
|
klass.new(
|
50
50
|
events: result.events,
|
51
51
|
actions: result.actions,
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
attributes: result.attributes,
|
53
|
+
keep: result.keep?,
|
54
|
+
timeout: result.timeout?,
|
55
|
+
duration_ns: result.duration,
|
56
|
+
duration_ext_ns: (stop_ns - start_ns),
|
57
|
+
input_truncated: result.input_truncated?
|
56
58
|
)
|
57
59
|
ensure
|
58
60
|
@mutex.unlock
|
@@ -80,11 +82,19 @@ module Datadog
|
|
80
82
|
Datadog.logger.debug { "#{@debug_tag} execution error: #{e} backtrace: #{e.backtrace&.first(3)}" }
|
81
83
|
AppSec.telemetry.report(e, description: 'libddwaf-rb internal low-level error')
|
82
84
|
|
83
|
-
WAF::Result.new(
|
85
|
+
WAF::Result.new(
|
86
|
+
status: :err_internal,
|
87
|
+
events: [],
|
88
|
+
actions: {},
|
89
|
+
attributes: {},
|
90
|
+
duration: 0,
|
91
|
+
keep: false,
|
92
|
+
timeout: false
|
93
|
+
)
|
84
94
|
end
|
85
95
|
|
86
96
|
def report_execution(result)
|
87
|
-
Datadog.logger.debug { "#{@debug_tag} execution timed out: #{result.inspect}" } if result.timeout
|
97
|
+
Datadog.logger.debug { "#{@debug_tag} execution timed out: #{result.inspect}" } if result.timeout?
|
88
98
|
|
89
99
|
if SUCCESSFUL_EXECUTION_CODES.include?(result.status)
|
90
100
|
Datadog.logger.debug { "#{@debug_tag} execution result: #{result.inspect}" }
|