datadog 2.21.0 → 2.23.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 +106 -2
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/init.c +5 -0
- data/ext/libdatadog_api/library_config.c +34 -25
- data/ext/libdatadog_api/process_discovery.c +19 -13
- 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 +23 -6
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- data/lib/datadog/appsec/assets/blocked.html +8 -0
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- 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/assets.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 +29 -13
- data/lib/datadog/appsec/response.rb +18 -4
- 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/components.rb +44 -9
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +14 -0
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/environment/ext.rb +6 -0
- data/lib/datadog/core/environment/process.rb +79 -0
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery.rb +4 -2
- data/lib/datadog/core/remote/client/capabilities.rb +7 -0
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/remote/transport/config.rb +2 -10
- data/lib/datadog/core/remote/transport/http/config.rb +9 -9
- data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
- data/lib/datadog/core/remote/transport/http.rb +2 -0
- data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +18 -3
- 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 +52 -49
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -2
- data/lib/datadog/core/telemetry/logging.rb +2 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
- data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
- data/lib/datadog/core/transport/http/client.rb +69 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/array.rb +29 -0
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once_successful.rb +6 -2
- data/lib/datadog/core/utils.rb +2 -0
- data/lib/datadog/data_streams/configuration/settings.rb +49 -0
- data/lib/datadog/data_streams/configuration.rb +11 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +509 -0
- data/lib/datadog/data_streams/transport/http/api.rb +33 -0
- data/lib/datadog/data_streams/transport/http/client.rb +21 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
- data/lib/datadog/data_streams/transport/http.rb +41 -0
- data/lib/datadog/data_streams/transport/stats.rb +60 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/component.rb +14 -16
- 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 +29 -0
- data/lib/datadog/di/instrumenter.rb +163 -48
- data/lib/datadog/di/probe.rb +55 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_manager.rb +13 -4
- data/lib/datadog/di/probe_notification_builder.rb +105 -67
- data/lib/datadog/di/proc_responder.rb +32 -0
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/di/transport/diagnostics.rb +2 -2
- data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
- data/lib/datadog/di/transport/http/input.rb +2 -4
- data/lib/datadog/di/transport/http.rb +6 -2
- data/lib/datadog/di/transport/input.rb +64 -4
- data/lib/datadog/open_feature/component.rb +60 -0
- data/lib/datadog/open_feature/configuration.rb +27 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +14 -0
- data/lib/datadog/open_feature/native_evaluator.rb +38 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +141 -0
- data/lib/datadog/open_feature/remote.rb +74 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +72 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
- data/lib/datadog/opentelemetry/metrics.rb +110 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +4 -0
- data/lib/datadog/profiling/tag_builder.rb +36 -3
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +9 -0
- data/lib/datadog/tracing/configuration/settings.rb +77 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- 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 +74 -44
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +9 -1
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +3 -5
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +92 -16
- data/ext/libdatadog_api/macos_development.md +0 -26
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/di/transport/http/client.rb +0 -47
|
@@ -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,27 @@ 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
|
-
|
|
27
|
-
|
|
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_PROCESSOR_OVERRIDES = 1 << 16
|
|
27
|
+
CAP_ASM_CUSTOM_DATA_SCANNERS = 1 << 17
|
|
28
|
+
CAP_ASM_RASP_SSRF = 1 << 23
|
|
29
|
+
CAP_ASM_RASP_SQLI = 1 << 21
|
|
30
|
+
CAP_ASM_AUTO_USER_INSTRUM_MODE = 1 << 31
|
|
31
|
+
CAP_ASM_ENDPOINT_FINGERPRINT = 1 << 32
|
|
32
|
+
CAP_ASM_SESSION_FINGERPRINT = 1 << 33
|
|
33
|
+
CAP_ASM_NETWORK_FINGERPRINT = 1 << 34
|
|
34
|
+
CAP_ASM_HEADER_FINGERPRINT = 1 << 35
|
|
35
|
+
CAP_ASM_TRACE_TAGGING_RULES = 1 << 43
|
|
28
36
|
|
|
29
37
|
# TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
|
|
30
38
|
ASM_CAPABILITIES = [
|
|
@@ -37,8 +45,16 @@ module Datadog
|
|
|
37
45
|
CAP_ASM_CUSTOM_RULES,
|
|
38
46
|
CAP_ASM_CUSTOM_BLOCKING_RESPONSE,
|
|
39
47
|
CAP_ASM_TRUSTED_IPS,
|
|
48
|
+
CAP_ASM_PROCESSOR_OVERRIDES,
|
|
49
|
+
CAP_ASM_CUSTOM_DATA_SCANNERS,
|
|
40
50
|
CAP_ASM_RASP_SSRF,
|
|
41
51
|
CAP_ASM_RASP_SQLI,
|
|
52
|
+
CAP_ASM_AUTO_USER_INSTRUM_MODE,
|
|
53
|
+
CAP_ASM_ENDPOINT_FINGERPRINT,
|
|
54
|
+
CAP_ASM_SESSION_FINGERPRINT,
|
|
55
|
+
CAP_ASM_NETWORK_FINGERPRINT,
|
|
56
|
+
CAP_ASM_HEADER_FINGERPRINT,
|
|
57
|
+
CAP_ASM_TRACE_TAGGING_RULES,
|
|
42
58
|
].freeze
|
|
43
59
|
|
|
44
60
|
ASM_PRODUCTS = [
|
|
@@ -7,6 +7,8 @@ module Datadog
|
|
|
7
7
|
module AppSec
|
|
8
8
|
# AppSec response
|
|
9
9
|
class Response
|
|
10
|
+
SECURITY_RESPONSE_ID_PLACEHOLDER = '[security_response_id]'
|
|
11
|
+
|
|
10
12
|
attr_reader :status, :headers, :body
|
|
11
13
|
|
|
12
14
|
def initialize(status:, headers: {}, body: [])
|
|
@@ -37,16 +39,26 @@ module Datadog
|
|
|
37
39
|
Response.new(
|
|
38
40
|
status: interrupt_params['status_code']&.to_i || 403,
|
|
39
41
|
headers: {'Content-Type' => content_type},
|
|
40
|
-
body: [
|
|
42
|
+
body: [
|
|
43
|
+
content(
|
|
44
|
+
security_response_id: interrupt_params['security_response_id'],
|
|
45
|
+
content_type: content_type
|
|
46
|
+
)
|
|
47
|
+
],
|
|
41
48
|
)
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
def redirect_response(interrupt_params)
|
|
45
52
|
status_code = interrupt_params['status_code'].to_i
|
|
53
|
+
location = interrupt_params.fetch('location')
|
|
54
|
+
|
|
55
|
+
if (security_response_id = interrupt_params.fetch('security_response_id'))
|
|
56
|
+
location.gsub!(SECURITY_RESPONSE_ID_PLACEHOLDER, security_response_id)
|
|
57
|
+
end
|
|
46
58
|
|
|
47
59
|
Response.new(
|
|
48
60
|
status: ((status_code >= 300 && status_code < 400) ? status_code : 303),
|
|
49
|
-
headers: {'Location' =>
|
|
61
|
+
headers: {'Location' => location},
|
|
50
62
|
body: [],
|
|
51
63
|
)
|
|
52
64
|
end
|
|
@@ -82,16 +94,18 @@ module Datadog
|
|
|
82
94
|
DEFAULT_CONTENT_TYPE
|
|
83
95
|
end
|
|
84
96
|
|
|
85
|
-
def content(content_type)
|
|
97
|
+
def content(security_response_id:, content_type:)
|
|
86
98
|
content_format = CONTENT_TYPE_TO_FORMAT[content_type]
|
|
87
99
|
|
|
88
100
|
using_default = Datadog.configuration.appsec.block.templates.using_default?(content_format)
|
|
89
101
|
|
|
90
|
-
if using_default
|
|
102
|
+
template = if using_default
|
|
91
103
|
Datadog::AppSec::Assets.blocked(format: content_format)
|
|
92
104
|
else
|
|
93
105
|
Datadog.configuration.appsec.block.templates.send(content_format)
|
|
94
106
|
end
|
|
107
|
+
|
|
108
|
+
template.gsub(SECURITY_RESPONSE_ID_PLACEHOLDER, security_response_id.to_s)
|
|
95
109
|
end
|
|
96
110
|
end
|
|
97
111
|
end
|
|
@@ -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}" }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Datadog
|
|
4
4
|
module AppSec
|
|
5
5
|
# A class that represents a security event of any kind. It could be an event
|
|
6
|
-
# representing an attack or fingerprinting results as
|
|
6
|
+
# representing an attack or fingerprinting results as attributes or an API
|
|
7
7
|
# security check with extracted schema.
|
|
8
8
|
class SecurityEvent
|
|
9
9
|
SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
|
|
@@ -17,22 +17,20 @@ module Datadog
|
|
|
17
17
|
@span = span
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@is_attack = @waf_result.is_a?(SecurityEngine::Result::Match)
|
|
20
|
+
def keep?
|
|
21
|
+
@waf_result.keep?
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
def schema?
|
|
27
25
|
return @has_schema if defined?(@has_schema)
|
|
28
26
|
|
|
29
|
-
@has_schema = @waf_result.
|
|
27
|
+
@has_schema = @waf_result.attributes.any? { |name, _| name.start_with?(SCHEMA_KEY_PREFIX) }
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
def fingerprint?
|
|
33
31
|
return @has_fingerprint if defined?(@has_fingerprint)
|
|
34
32
|
|
|
35
|
-
@has_fingerprint = @waf_result.
|
|
33
|
+
@has_fingerprint = @waf_result.attributes.any? { |name, _| name.start_with?(FINGERPRINT_KEY_PREFIX) }
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
36
|
end
|