datadog 2.21.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 +48 -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 +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/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/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/components.rb +14 -6
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/ddsketch.rb +21 -0
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery.rb +4 -2
- data/lib/datadog/core/remote/component.rb +4 -6
- 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.rb +1 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/network.rb +19 -0
- data/lib/datadog/di/boot.rb +1 -0
- 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_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/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/settings.rb +3 -3
- 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 +53 -28
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/version.rb +1 -1
- metadata +22 -9
- data/ext/libdatadog_api/macos_development.md +0 -26
@@ -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}" }
|
@@ -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
|
@@ -26,8 +26,6 @@ module Datadog
|
|
26
26
|
# Global components for the trace library.
|
27
27
|
class Components
|
28
28
|
class << self
|
29
|
-
include Datadog::Tracing::Component
|
30
|
-
|
31
29
|
def build_health_metrics(settings, logger, telemetry)
|
32
30
|
settings = settings.health_metrics
|
33
31
|
options = {enabled: settings.enabled}
|
@@ -79,10 +77,9 @@ module Datadog
|
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
82
|
-
include Datadog::Tracing::Component::InstanceMethods
|
83
|
-
|
84
80
|
attr_reader \
|
85
81
|
:health_metrics,
|
82
|
+
:settings,
|
86
83
|
:logger,
|
87
84
|
:remote,
|
88
85
|
:profiler,
|
@@ -96,8 +93,10 @@ module Datadog
|
|
96
93
|
:agent_info
|
97
94
|
|
98
95
|
def initialize(settings)
|
96
|
+
@settings = settings
|
99
97
|
@logger = self.class.build_logger(settings)
|
100
98
|
@environment_logger_extra = {}
|
99
|
+
StableConfig.log_result(@logger)
|
101
100
|
Deprecations.log_deprecations_from_all_sources(@logger)
|
102
101
|
|
103
102
|
# This agent_settings is intended for use within Core. If you require
|
@@ -111,7 +110,7 @@ module Datadog
|
|
111
110
|
@telemetry = self.class.build_telemetry(settings, agent_settings, @logger)
|
112
111
|
|
113
112
|
@remote = Remote::Component.build(settings, agent_settings, logger: @logger, telemetry: telemetry)
|
114
|
-
@tracer =
|
113
|
+
@tracer = Datadog::Tracing::Component.build_tracer(settings, agent_settings, logger: @logger)
|
115
114
|
@crashtracker = self.class.build_crashtracker(settings, agent_settings, logger: @logger)
|
116
115
|
|
117
116
|
@profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
|
@@ -129,7 +128,16 @@ module Datadog
|
|
129
128
|
@error_tracking = Datadog::ErrorTracking::Component.build(settings, @tracer, @logger)
|
130
129
|
@environment_logger_extra[:dynamic_instrumentation_enabled] = !!@dynamic_instrumentation
|
131
130
|
|
132
|
-
|
131
|
+
# Configure non-privileged components.
|
132
|
+
Datadog::Tracing::Contrib::Component.configure(settings)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Hot-swaps with a new sampler.
|
136
|
+
# This operation acquires the Components lock to ensure
|
137
|
+
# there is no concurrent modification of the sampler.
|
138
|
+
def reconfigure_sampler(settings = Datadog.configuration)
|
139
|
+
sampler = Datadog::Tracing::Component.build_sampler(settings)
|
140
|
+
Datadog.send(:safely_synchronize) { tracer.sampler.sampler = sampler }
|
133
141
|
end
|
134
142
|
|
135
143
|
# Starts up components
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../utils/only_once'
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module Core
|
5
7
|
module Configuration
|
6
8
|
# Import config from config files (fleet automation)
|
7
9
|
module StableConfig
|
10
|
+
LOG_ONLY_ONCE = Utils::OnlyOnce.new
|
11
|
+
|
8
12
|
def self.extract_configuration
|
9
13
|
if (libdatadog_api_failure = Datadog::Core::LIBDATADOG_API_FAILURE)
|
10
14
|
Datadog.config_init_logger.debug("Cannot enable stable config: #{libdatadog_api_failure}")
|
@@ -16,6 +20,12 @@ module Datadog
|
|
16
20
|
def self.configuration
|
17
21
|
@configuration ||= StableConfig.extract_configuration
|
18
22
|
end
|
23
|
+
|
24
|
+
def self.log_result(logger)
|
25
|
+
LOG_ONLY_ONCE.run do
|
26
|
+
logger.debug(configuration[:logs]) if configuration[:logs]
|
27
|
+
end
|
28
|
+
end
|
19
29
|
end
|
20
30
|
end
|
21
31
|
end
|