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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -1
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +60 -0
  4. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  5. data/ext/libdatadog_api/ddsketch.c +106 -0
  6. data/ext/libdatadog_api/init.c +3 -0
  7. data/ext/libdatadog_api/library_config.c +35 -27
  8. data/ext/libdatadog_api/process_discovery.c +19 -13
  9. data/ext/libdatadog_extconf_helpers.rb +1 -1
  10. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  11. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  12. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  13. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  14. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  15. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  16. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  17. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  18. data/lib/datadog/appsec/compressed_json.rb +1 -1
  19. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  20. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  21. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  22. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  23. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  24. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  25. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  26. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  27. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  28. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  29. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  30. data/lib/datadog/appsec/event.rb +12 -14
  31. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  32. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  33. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  34. data/lib/datadog/appsec/remote.rb +25 -13
  35. data/lib/datadog/appsec/security_engine/result.rb +28 -9
  36. data/lib/datadog/appsec/security_engine/runner.rb +17 -7
  37. data/lib/datadog/appsec/security_event.rb +5 -7
  38. data/lib/datadog/core/configuration/components.rb +14 -6
  39. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  40. data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
  41. data/lib/datadog/core/configuration.rb +1 -1
  42. data/lib/datadog/core/ddsketch.rb +21 -0
  43. data/lib/datadog/core/environment/yjit.rb +2 -1
  44. data/lib/datadog/core/pin.rb +4 -8
  45. data/lib/datadog/core/process_discovery.rb +4 -2
  46. data/lib/datadog/core/remote/component.rb +4 -6
  47. data/lib/datadog/core/telemetry/component.rb +11 -0
  48. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  49. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  50. data/lib/datadog/core/telemetry/event.rb +1 -0
  51. data/lib/datadog/core/transport/response.rb +4 -1
  52. data/lib/datadog/core/utils/network.rb +19 -0
  53. data/lib/datadog/di/boot.rb +1 -0
  54. data/lib/datadog/di/component.rb +14 -0
  55. data/lib/datadog/di/context.rb +70 -0
  56. data/lib/datadog/di/el/compiler.rb +164 -0
  57. data/lib/datadog/di/el/evaluator.rb +159 -0
  58. data/lib/datadog/di/el/expression.rb +42 -0
  59. data/lib/datadog/di/el.rb +5 -0
  60. data/lib/datadog/di/error.rb +25 -0
  61. data/lib/datadog/di/instrumenter.rb +101 -32
  62. data/lib/datadog/di/probe.rb +35 -15
  63. data/lib/datadog/di/probe_builder.rb +39 -1
  64. data/lib/datadog/di/probe_manager.rb +3 -2
  65. data/lib/datadog/di/probe_notification_builder.rb +50 -51
  66. data/lib/datadog/di/serializer.rb +151 -7
  67. data/lib/datadog/tracing/component.rb +6 -17
  68. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  69. data/lib/datadog/tracing/configuration/settings.rb +3 -3
  70. data/lib/datadog/tracing/contrib/component.rb +2 -2
  71. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  72. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  73. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +53 -28
  74. data/lib/datadog/tracing/metadata/ext.rb +8 -0
  75. data/lib/datadog/version.rb +1 -1
  76. metadata +22 -9
  77. 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.derivatives.empty?
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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 @oneshot_tags_sent
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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.derivatives.empty?
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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.tag_and_keep!(context, result)
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
 
@@ -9,8 +9,8 @@ module Datadog
9
9
  module AppSec
10
10
  # AppSec event
11
11
  module Event
12
- DERIVATIVE_SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
13
- DERIVATIVE_SCHEMA_MAX_COMPRESSED_SIZE = 25000
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 tag_and_keep!(context, waf_result)
44
- TraceKeeper.keep!(context.trace)
43
+ def tag(context, waf_result)
44
+ return if context.span.nil?
45
45
 
46
- if context.span
47
- if waf_result.actions.key?('block_request') || waf_result.actions.key?('redirect_request')
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.attack? || event.schema? }
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.derivatives.each do |key, value|
110
- next memo[key] = value unless key.start_with?(DERIVATIVE_SCHEMA_KEY_PREFIX)
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 >= DERIVATIVE_SCHEMA_MAX_COMPRESSED_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(:evals, :matches, :errors, :timeouts, :duration_ns, :duration_ext_ns, keyword_init: true)
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
- @waf = Store.new(evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0)
15
- @rasp = Store.new(evals: 0, matches: 0, errors: 0, timeouts: 0, duration_ns: 0, duration_ext_ns: 0)
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.derivatives.any?
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.tag_and_keep!(context, result)
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.derivatives.any?
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.tag_and_keep!(context, result)
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 # RESERVED
16
- CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
17
- CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
18
- CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
19
- CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
20
- CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
21
- CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
22
- CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
23
- CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
- CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
- CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
26
- CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
27
- CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
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, :derivatives, :duration_ns, :duration_ext_ns
10
+ attr_reader :events, :actions, :attributes, :duration_ns, :duration_ext_ns
11
11
 
12
- def initialize(events:, actions:, derivatives:, timeout:, duration_ns:, duration_ext_ns:)
12
+ def initialize(events:, actions:, attributes:, duration_ns:, duration_ext_ns:, timeout:, keep:, input_truncated:)
13
13
  @events = events
14
14
  @actions = actions
15
- @derivatives = derivatives
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
- !!@timeout
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, :derivatives, :duration_ns, :duration_ext_ns
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 = @derivatives = {}
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
- derivatives: result.derivatives,
53
- timeout: result.timeout,
54
- duration_ns: result.total_runtime,
55
- duration_ext_ns: (stop_ns - start_ns)
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(:err_internal, [], 0, false, [], [])
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 derivatives or an API
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 attack?
21
- return @is_attack if defined?(@is_attack)
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.derivatives.any? { |name, _| name.start_with?(SCHEMA_KEY_PREFIX) }
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.derivatives.any? { |name, _| name.start_with?(FINGERPRINT_KEY_PREFIX) }
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 = self.class.build_tracer(settings, agent_settings, logger: @logger)
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
- self.class.configure_tracing(settings)
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