datadog 2.15.0 → 2.17.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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -2
  3. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
  5. data/ext/datadog_profiling_native_extension/encoded_profile.c +22 -12
  6. data/ext/datadog_profiling_native_extension/encoded_profile.h +1 -0
  7. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
  9. data/ext/datadog_profiling_native_extension/http_transport.c +45 -72
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +4 -5
  11. data/ext/libdatadog_api/crashtracker.c +11 -12
  12. data/ext/libdatadog_api/crashtracker.h +5 -0
  13. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  14. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  15. data/ext/libdatadog_api/init.c +15 -0
  16. data/ext/libdatadog_api/library_config.c +122 -0
  17. data/ext/libdatadog_api/library_config.h +19 -0
  18. data/ext/libdatadog_api/macos_development.md +3 -3
  19. data/ext/libdatadog_api/process_discovery.c +117 -0
  20. data/ext/libdatadog_api/process_discovery.h +5 -0
  21. data/ext/libdatadog_extconf_helpers.rb +1 -1
  22. data/lib/datadog/appsec/actions_handler.rb +3 -2
  23. data/lib/datadog/appsec/api_security/lru_cache.rb +49 -0
  24. data/lib/datadog/appsec/api_security.rb +9 -0
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1344 -0
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +1344 -0
  27. data/lib/datadog/appsec/autoload.rb +1 -1
  28. data/lib/datadog/appsec/component.rb +11 -4
  29. data/lib/datadog/appsec/configuration/settings.rb +31 -18
  30. data/lib/datadog/appsec/context.rb +1 -1
  31. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  32. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  33. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  34. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +2 -3
  35. data/lib/datadog/appsec/contrib/devise/ext.rb +1 -0
  36. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/devise/patcher.rb +3 -5
  38. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +17 -4
  39. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  40. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  41. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  42. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  43. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  44. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +22 -32
  46. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +16 -16
  48. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  49. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  50. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  51. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  52. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  53. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  54. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  55. data/lib/datadog/appsec/event.rb +85 -95
  56. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +5 -2
  57. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  58. data/lib/datadog/appsec/monitor/gateway/watcher.rb +42 -12
  59. data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
  60. data/lib/datadog/appsec/processor/rule_merger.rb +5 -5
  61. data/lib/datadog/appsec/processor.rb +1 -1
  62. data/lib/datadog/appsec/remote.rb +14 -13
  63. data/lib/datadog/appsec/response.rb +6 -6
  64. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  65. data/lib/datadog/appsec/security_event.rb +39 -0
  66. data/lib/datadog/appsec.rb +1 -1
  67. data/lib/datadog/core/buffer/random.rb +18 -2
  68. data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -5
  69. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  70. data/lib/datadog/core/configuration/components.rb +48 -30
  71. data/lib/datadog/core/configuration/components_state.rb +23 -0
  72. data/lib/datadog/core/configuration/option.rb +79 -43
  73. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  74. data/lib/datadog/core/configuration/options.rb +1 -1
  75. data/lib/datadog/core/configuration/settings.rb +20 -10
  76. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  77. data/lib/datadog/core/configuration.rb +40 -16
  78. data/lib/datadog/core/crashtracking/component.rb +3 -10
  79. data/lib/datadog/core/encoding.rb +1 -1
  80. data/lib/datadog/core/environment/cgroup.rb +10 -12
  81. data/lib/datadog/core/environment/container.rb +38 -40
  82. data/lib/datadog/core/environment/ext.rb +6 -6
  83. data/lib/datadog/core/environment/git.rb +1 -0
  84. data/lib/datadog/core/environment/identity.rb +3 -3
  85. data/lib/datadog/core/environment/platform.rb +3 -3
  86. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  87. data/lib/datadog/core/error.rb +11 -9
  88. data/lib/datadog/core/logger.rb +2 -2
  89. data/lib/datadog/core/metrics/client.rb +20 -21
  90. data/lib/datadog/core/metrics/logging.rb +5 -5
  91. data/lib/datadog/core/process_discovery.rb +32 -0
  92. data/lib/datadog/core/rate_limiter.rb +4 -2
  93. data/lib/datadog/core/remote/client.rb +39 -31
  94. data/lib/datadog/core/remote/component.rb +3 -3
  95. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  96. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  97. data/lib/datadog/core/remote/transport/http/client.rb +1 -1
  98. data/lib/datadog/core/remote/transport/http/config.rb +21 -5
  99. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -1
  100. data/lib/datadog/core/runtime/metrics.rb +4 -4
  101. data/lib/datadog/core/telemetry/component.rb +78 -53
  102. data/lib/datadog/core/telemetry/emitter.rb +23 -11
  103. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  104. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  105. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  106. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  107. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  108. data/lib/datadog/core/telemetry/event/app_started.rb +179 -0
  109. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  110. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  111. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  112. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  113. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  114. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  115. data/lib/datadog/core/telemetry/event.rb +17 -472
  116. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  117. data/lib/datadog/core/telemetry/logger.rb +1 -1
  118. data/lib/datadog/core/telemetry/metric.rb +3 -3
  119. data/lib/datadog/core/telemetry/request.rb +3 -3
  120. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  121. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  122. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  123. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  124. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  125. data/lib/datadog/core/telemetry/worker.rb +90 -24
  126. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  127. data/lib/datadog/core/transport/http/builder.rb +13 -13
  128. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  129. data/lib/datadog/core/utils/duration.rb +32 -32
  130. data/lib/datadog/core/utils/forking.rb +2 -2
  131. data/lib/datadog/core/utils/network.rb +6 -6
  132. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  133. data/lib/datadog/core/utils/time.rb +20 -0
  134. data/lib/datadog/core/utils/truncation.rb +21 -0
  135. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  136. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  137. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  138. data/lib/datadog/core/worker.rb +1 -1
  139. data/lib/datadog/core/workers/async.rb +29 -12
  140. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  141. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  142. data/lib/datadog/core.rb +8 -0
  143. data/lib/datadog/di/boot.rb +34 -0
  144. data/lib/datadog/di/remote.rb +2 -0
  145. data/lib/datadog/di.rb +5 -32
  146. data/lib/datadog/error_tracking/collector.rb +87 -0
  147. data/lib/datadog/error_tracking/component.rb +167 -0
  148. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  149. data/lib/datadog/error_tracking/configuration.rb +11 -0
  150. data/lib/datadog/error_tracking/ext.rb +18 -0
  151. data/lib/datadog/error_tracking/extensions.rb +16 -0
  152. data/lib/datadog/error_tracking/filters.rb +77 -0
  153. data/lib/datadog/error_tracking.rb +18 -0
  154. data/lib/datadog/kit/identity.rb +1 -1
  155. data/lib/datadog/profiling/collectors/code_provenance.rb +1 -1
  156. data/lib/datadog/profiling/exporter.rb +1 -1
  157. data/lib/datadog/profiling/ext.rb +0 -1
  158. data/lib/datadog/profiling/flush.rb +1 -1
  159. data/lib/datadog/profiling/http_transport.rb +1 -6
  160. data/lib/datadog/profiling/scheduler.rb +8 -1
  161. data/lib/datadog/profiling/tag_builder.rb +1 -5
  162. data/lib/datadog/tracing/analytics.rb +1 -1
  163. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +4 -1
  164. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
  165. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  166. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
  167. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  168. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  169. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -5
  170. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -5
  171. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -5
  172. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  173. data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
  174. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  175. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  176. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  177. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  178. data/lib/datadog/tracing/contrib/support.rb +28 -0
  179. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  180. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  181. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  182. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  183. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  184. data/lib/datadog/tracing/span_operation.rb +38 -14
  185. data/lib/datadog/tracing/trace_operation.rb +15 -7
  186. data/lib/datadog/tracing/tracer.rb +7 -3
  187. data/lib/datadog/tracing/utils.rb +1 -1
  188. data/lib/datadog/version.rb +1 -1
  189. data/lib/datadog.rb +2 -3
  190. metadata +53 -10
  191. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  192. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  193. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  194. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../event'
4
+ require_relative '../../security_event'
3
5
  require_relative '../../instrumentation/gateway'
4
6
 
5
7
  module Datadog
@@ -8,18 +10,24 @@ module Datadog
8
10
  module Gateway
9
11
  # Watcher for Apssec internal events
10
12
  module Watcher
13
+ ARBITRARY_VALUE = 'invalid'
14
+ EVENT_LOGIN_SUCCESS = 'users.login.success'
15
+ EVENT_LOGIN_FAILURE = 'users.login.failure'
16
+ WATCHED_LOGIN_EVENTS = [EVENT_LOGIN_SUCCESS, EVENT_LOGIN_FAILURE].freeze
17
+
11
18
  class << self
12
19
  def watch
13
20
  gateway = Instrumentation.gateway
14
21
 
15
22
  watch_user_id(gateway)
23
+ watch_user_login(gateway)
16
24
  end
17
25
 
18
26
  def watch_user_id(gateway = Instrumentation.gateway)
19
27
  gateway.watch('identity.set_user', :appsec) do |stack, user|
20
- context = Datadog::AppSec.active_context
28
+ context = AppSec.active_context
21
29
 
22
- if user.id.nil? && user.login.nil?
30
+ if user.id.nil? && user.login.nil? && user.session_id.nil?
23
31
  Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
24
32
  next stack.call(user)
25
33
  end
@@ -27,24 +35,46 @@ module Datadog
27
35
  persistent_data = {}
28
36
  persistent_data['usr.id'] = user.id if user.id
29
37
  persistent_data['usr.login'] = user.login if user.login
38
+ persistent_data['usr.session_id'] = user.session_id if user.session_id
30
39
 
31
40
  result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
32
41
 
42
+ if result.match? || result.derivatives.any?
43
+ context.events.push(
44
+ AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
45
+ )
46
+ end
47
+
33
48
  if result.match?
34
- Datadog::AppSec::Event.tag_and_keep!(context, result)
49
+ AppSec::Event.tag_and_keep!(context, result)
50
+ AppSec::ActionsHandler.handle(result.actions)
51
+ end
52
+
53
+ stack.call(user)
54
+ end
55
+ end
35
56
 
36
- context.events << {
37
- waf_result: result,
38
- trace: context.trace,
39
- span: context.span,
40
- user: user,
41
- actions: result.actions
42
- }
57
+ def watch_user_login(gateway = Instrumentation.gateway)
58
+ gateway.watch('appsec.events.user_lifecycle', :appsec) do |stack, kind|
59
+ context = AppSec.active_context
43
60
 
44
- Datadog::AppSec::ActionsHandler.handle(result.actions)
61
+ next stack.call(kind) unless WATCHED_LOGIN_EVENTS.include?(kind)
62
+
63
+ persistent_data = {"server.business_logic.#{kind}" => ARBITRARY_VALUE}
64
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
65
+
66
+ if result.match? || result.derivatives.any?
67
+ context.events.push(
68
+ AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
69
+ )
45
70
  end
46
71
 
47
- stack.call(user)
72
+ if result.match?
73
+ AppSec::Event.tag_and_keep!(context, result)
74
+ AppSec::ActionsHandler.handle(result.actions)
75
+ end
76
+
77
+ stack.call(kind)
48
78
  end
49
79
  end
50
80
  end
@@ -10,35 +10,33 @@ module Datadog
10
10
  module RuleLoader
11
11
  class << self
12
12
  def load_rules(ruleset:, telemetry:)
13
- begin
14
- case ruleset
15
- when :recommended, :strict
16
- JSON.parse(Datadog::AppSec::Assets.waf_rules(ruleset))
17
- when :risky
18
- Datadog.logger.warn(
19
- 'The :risky Application Security Management ruleset has been deprecated and no longer available.'\
20
- 'The `:recommended` ruleset will be used instead.'\
21
- 'Please remove the `appsec.ruleset = :risky` setting from your Datadog.configure block.'
22
- )
23
- JSON.parse(Datadog::AppSec::Assets.waf_rules(:recommended))
24
- when String
25
- JSON.parse(File.read(File.expand_path(ruleset)))
26
- when File, StringIO
27
- JSON.parse(ruleset.read || '').tap { ruleset.rewind }
28
- when Hash
29
- ruleset
30
- else
31
- raise ArgumentError, "unsupported value for ruleset setting: #{ruleset.inspect}"
32
- end
33
- rescue StandardError => e
34
- Datadog.logger.error do
35
- "libddwaf ruleset failed to load, ruleset: #{ruleset.inspect} error: #{e.inspect}"
36
- end
13
+ case ruleset
14
+ when :recommended, :strict
15
+ JSON.parse(Datadog::AppSec::Assets.waf_rules(ruleset))
16
+ when :risky
17
+ Datadog.logger.warn(
18
+ 'The :risky Application Security Management ruleset has been deprecated and no longer available.' \
19
+ 'The `:recommended` ruleset will be used instead.' \
20
+ 'Please remove the `appsec.ruleset = :risky` setting from your Datadog.configure block.'
21
+ )
22
+ JSON.parse(Datadog::AppSec::Assets.waf_rules(:recommended))
23
+ when String
24
+ JSON.parse(File.read(File.expand_path(ruleset)))
25
+ when File, StringIO
26
+ JSON.parse(ruleset.read || '').tap { ruleset.rewind }
27
+ when Hash
28
+ ruleset
29
+ else
30
+ raise ArgumentError, "unsupported value for ruleset setting: #{ruleset.inspect}"
31
+ end
32
+ rescue => e
33
+ Datadog.logger.error do
34
+ "libddwaf ruleset failed to load, ruleset: #{ruleset.inspect} error: #{e.inspect}"
35
+ end
37
36
 
38
- telemetry.report(e, description: 'libddwaf ruleset failed to load')
37
+ telemetry.report(e, description: 'libddwaf ruleset failed to load')
39
38
 
40
- nil
41
- end
39
+ nil
42
40
  end
43
41
 
44
42
  def load_data(ip_denylist: [], user_id_denylist: [])
@@ -62,7 +60,7 @@ module Datadog
62
60
  {
63
61
  'id' => id,
64
62
  'type' => 'data_with_expiration',
65
- 'data' => denylist.map { |v| { 'value' => v.to_s, 'expiration' => 2**63 } }
63
+ 'data' => denylist.map { |v| {'value' => v.to_s, 'expiration' => 2**63} }
66
64
  }
67
65
  end
68
66
 
@@ -11,8 +11,8 @@ module Datadog
11
11
  # RuleVersionMismatchError
12
12
  class RuleVersionMismatchError < StandardError
13
13
  def initialize(version1, version2)
14
- msg = 'Merging rule files with different version could lead to unkown behaviour. '\
15
- "We have receieve two rule files with versions: #{version1}, #{version2}. "\
14
+ msg = 'Merging rule files with different version could lead to unkown behaviour. ' \
15
+ "We have receieve two rule files with versions: #{version1}, #{version2}. " \
16
16
  'Please validate the configuration is correct and try again.'
17
17
  super(msg)
18
18
  end
@@ -27,7 +27,7 @@ module Datadog
27
27
  )
28
28
  processors ||= begin
29
29
  default_waf_processors
30
- rescue StandardError => e
30
+ rescue => e
31
31
  Datadog.logger.error("libddwaf rulemerger failed to parse default waf processors. Error: #{e.inspect}")
32
32
  telemetry.report(
33
33
  e,
@@ -38,7 +38,7 @@ module Datadog
38
38
 
39
39
  scanners ||= begin
40
40
  default_waf_scanners
41
- rescue StandardError => e
41
+ rescue => e
42
42
  Datadog.logger.error("libddwaf rulemerger failed to parse default waf scanners. Error: #{e.inspect}")
43
43
  telemetry.report(
44
44
  e,
@@ -146,7 +146,7 @@ module Datadog
146
146
  end
147
147
 
148
148
  result.each_with_object([]) do |entry, acc|
149
- value = { 'value' => entry[0] }
149
+ value = {'value' => entry[0]}
150
150
  value['expiration'] = entry[1] if entry[1]
151
151
 
152
152
  acc << value
@@ -82,7 +82,7 @@ module Datadog
82
82
  @diagnostics = e.diagnostics if e.diagnostics
83
83
 
84
84
  false
85
- rescue StandardError => e
85
+ rescue => e
86
86
  Datadog.logger.error do
87
87
  "libddwaf failed to initialize, error: #{e.inspect}"
88
88
  end
@@ -9,22 +9,23 @@ module Datadog
9
9
  # Remote
10
10
  module Remote
11
11
  class ReadError < StandardError; end
12
+
12
13
  class NoRulesError < StandardError; end
13
14
 
14
15
  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
16
+ CAP_ASM_RESERVED_1 = 1 << 0 # RESERVED
17
+ CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
18
+ CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
19
+ CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
20
+ CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
21
+ CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
22
+ CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
23
+ CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
24
+ CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
25
+ CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
26
+ CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
27
+ CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
28
+ CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
28
29
 
29
30
  # TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
30
31
  ASM_CAPABILITIES = [
@@ -30,13 +30,13 @@ module Datadog
30
30
 
31
31
  def block_response(interrupt_params, http_accept_header)
32
32
  content_type = case interrupt_params['type']
33
- when nil, 'auto' then content_type(http_accept_header)
34
- else FORMAT_TO_CONTENT_TYPE.fetch(interrupt_params['type'], DEFAULT_CONTENT_TYPE)
35
- end
33
+ when nil, 'auto' then content_type(http_accept_header)
34
+ else FORMAT_TO_CONTENT_TYPE.fetch(interrupt_params['type'], DEFAULT_CONTENT_TYPE)
35
+ end
36
36
 
37
37
  Response.new(
38
38
  status: interrupt_params['status_code']&.to_i || 403,
39
- headers: { 'Content-Type' => content_type },
39
+ headers: {'Content-Type' => content_type},
40
40
  body: [content(content_type)],
41
41
  )
42
42
  end
@@ -45,8 +45,8 @@ module Datadog
45
45
  status_code = interrupt_params['status_code'].to_i
46
46
 
47
47
  Response.new(
48
- status: (status_code >= 300 && status_code < 400 ? status_code : 303),
49
- headers: { 'Location' => interrupt_params.fetch('location') },
48
+ status: ((status_code >= 300 && status_code < 400) ? status_code : 303),
49
+ headers: {'Location' => interrupt_params.fetch('location')},
50
50
  body: [],
51
51
  )
52
52
  end
@@ -42,7 +42,7 @@ module Datadog
42
42
  return Result::Error.new(duration_ext_ns: stop_ns - start_ns)
43
43
  end
44
44
 
45
- klass = result.status == :match ? Result::Match : Result::Ok
45
+ klass = (result.status == :match) ? Result::Match : Result::Ok
46
46
  klass.new(
47
47
  events: result.events,
48
48
  actions: result.actions,
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
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
7
+ # security check with extracted schema.
8
+ class SecurityEvent
9
+ SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
10
+ FINGERPRINT_KEY_PREFIX = '_dd.appsec.fp.'
11
+
12
+ attr_reader :waf_result, :trace, :span
13
+
14
+ def initialize(waf_result, trace:, span:)
15
+ @waf_result = waf_result
16
+ @trace = trace
17
+ @span = span
18
+ end
19
+
20
+ def attack?
21
+ return @is_attack if defined?(@is_attack)
22
+
23
+ @is_attack = @waf_result.is_a?(SecurityEngine::Result::Match)
24
+ end
25
+
26
+ def schema?
27
+ return @has_schema if defined?(@has_schema)
28
+
29
+ @has_schema = @waf_result.derivatives.any? { |name, _| name.start_with?(SCHEMA_KEY_PREFIX) }
30
+ end
31
+
32
+ def fingerprint?
33
+ return @has_fingerprint if defined?(@has_fingerprint)
34
+
35
+ @has_fingerprint = @waf_result.derivatives.any? { |name, _| name.start_with?(FINGERPRINT_KEY_PREFIX) }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -44,7 +44,7 @@ module Datadog
44
44
  appsec_component.reconfigure_lock(&block)
45
45
  end
46
46
 
47
- def api_security_enabled?
47
+ def perform_api_security_check?
48
48
  Datadog.configuration.appsec.api_security.enabled &&
49
49
  Datadog.configuration.appsec.api_security.sample_rate.sample?
50
50
  end
@@ -40,7 +40,23 @@ module Datadog
40
40
  add_all!(underflow) unless underflow.nil?
41
41
 
42
42
  # Iteratively replace items, to ensure pseudo-random replacement.
43
- overflow.each { |item| replace!(item) } unless overflow.nil?
43
+ overflow&.each { |item| replace!(item) }
44
+ end
45
+
46
+ def unshift(*items)
47
+ # TODO The existing concat implementation does not always append
48
+ # to the end of the buffer - if the buffer is full, a random
49
+ # item is deleted and the new item is added in the position of
50
+ # removed item.
51
+ # Therefore, if we want to preserve the item order, concat
52
+ # would also need to be changed to maintain order.
53
+ # With the existing implementation, the idea is to not move
54
+ # existing items around, which is what sets unshift apart from
55
+ # concat to begin with.
56
+ #
57
+ # Since this method currently delegates to +concat+, it does not
58
+ # have a matching definition in the thread-safe worker.
59
+ concat(items)
44
60
  end
45
61
 
46
62
  # Stored items are returned and the local buffer is reset.
@@ -78,7 +94,7 @@ module Datadog
78
94
  underflow = nil
79
95
  overflow = nil
80
96
 
81
- overflow_size = @max_size > 0 ? (@items.length + items.length) - @max_size : 0
97
+ overflow_size = (@max_size > 0) ? (@items.length + items.length) - @max_size : 0
82
98
 
83
99
  if overflow_size > 0
84
100
  # Items will overflow
@@ -37,8 +37,8 @@ module Datadog
37
37
  case adapter
38
38
  when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
39
39
  hostname = self.hostname
40
- hostname = "[#{hostname}]" if hostname =~ IPV6_REGEXP
41
- "#{ssl ? 'https' : 'http'}://#{hostname}:#{port}/"
40
+ hostname = "[#{hostname}]" if IPV6_REGEXP.match?(hostname)
41
+ "#{ssl ? "https" : "http"}://#{hostname}:#{port}/"
42
42
  when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
43
43
  "unix://#{uds_path}"
44
44
  else
@@ -158,7 +158,7 @@ module Datadog
158
158
  value: settings.agent.timeout_seconds,
159
159
  ),
160
160
  try_parsing_as_integer(
161
- friendly_name: "#{Datadog::Core::Configuration::Ext::Agent::ENV_DEFAULT_TIMEOUT_SECONDS} "\
161
+ friendly_name: "#{Datadog::Core::Configuration::Ext::Agent::ENV_DEFAULT_TIMEOUT_SECONDS} " \
162
162
  'environment variable',
163
163
  value: ENV[Datadog::Core::Configuration::Ext::Agent::ENV_DEFAULT_TIMEOUT_SECONDS],
164
164
  )
@@ -338,13 +338,13 @@ module Datadog
338
338
  log_warning(
339
339
  'Configuration mismatch: values differ between ' \
340
340
  "#{detected_configurations_in_priority_order
341
- .map { |config| "#{config.friendly_name} (#{config.value.inspect})" }.join(' and ')}" \
341
+ .map { |config| "#{config.friendly_name} (#{config.value.inspect})" }.join(" and ")}" \
342
342
  ". Using #{detected_configurations_in_priority_order.first.value.inspect} and ignoring other configuration."
343
343
  )
344
344
  end
345
345
 
346
346
  def log_warning(message)
347
- logger.warn(message) if logger
347
+ logger&.warn(message)
348
348
  end
349
349
 
350
350
  def http_scheme?(uri)
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/*
4
+
5
+ require 'uri'
6
+
7
+ require_relative 'agent_settings_resolver'
8
+
9
+ module Datadog
10
+ module Core
11
+ module Configuration
12
+ # Agent settings resolver for agentless operations (currently, telemetry
13
+ # in agentless mode).
14
+ #
15
+ # The terminology gets a little confusing here, but transports communicate
16
+ # with servers which are - for most components in the tracer - the
17
+ # (local) agent. Hence, "agent settings" to refer to where the server
18
+ # is located. Telemetry supports sending to the local agent but also
19
+ # implements agentless mode where it sends directly to Datadog intake
20
+ # endpoints. The agentless mode is configured using different settings,
21
+ # and this class produces AgentSettings instances when in agentless mode.
22
+ #
23
+ # Agentless settings resolver uses the following configuration sources:
24
+ #
25
+ # 1. url_override constructor parameter, if provided
26
+ # 2. Built-in default host/port/TLS settings for the backend
27
+ # intake endpoint
28
+ #
29
+ # The agentless resolver does NOT use agent settings (since it is
30
+ # for agentless operation), specifically it ignores:
31
+ #
32
+ # - c.agent.host
33
+ # - DD_AGENT_HOST
34
+ # - c.agent.port
35
+ # - DD_AGENT_PORT
36
+ #
37
+ # However, agentless resolver does respect the timeout specified via
38
+ # c.agent.timeout_seconds or DD_TRACE_AGENT_TIMEOUT_SECONDS.
39
+ class AgentlessSettingsResolver < AgentSettingsResolver
40
+ # To avoid coupling this class to telemetry, the URL override is
41
+ # taken here as a parameter instead of being read out of
42
+ # c.telemetry.agentless_url_override. For the same reason, the
43
+ # +url_override_source+ parameter should be set to the string
44
+ # "c.telemetry.agentless_url_override".
45
+ def self.call(settings, host_prefix:, url_override: nil, url_override_source: nil, logger: Datadog.logger)
46
+ new(
47
+ settings,
48
+ host_prefix: host_prefix,
49
+ url_override: url_override,
50
+ url_override_source: url_override_source,
51
+ logger: logger
52
+ ).send(:call)
53
+ end
54
+
55
+ private
56
+
57
+ attr_reader \
58
+ :host_prefix,
59
+ :url_override,
60
+ :url_override_source
61
+
62
+ def initialize(settings, host_prefix:, url_override: nil, url_override_source: nil, logger: Datadog.logger)
63
+ if url_override && url_override_source.nil?
64
+ raise ArgumentError, 'url_override_source must be provided when url_override is provided'
65
+ end
66
+
67
+ super(settings, logger: logger)
68
+
69
+ @host_prefix = host_prefix
70
+ @url_override = url_override
71
+ @url_override_source = url_override_source
72
+ end
73
+
74
+ def hostname
75
+ if should_use_uds?
76
+ nil
77
+ else
78
+ configured_hostname || "#{host_prefix}.#{settings.site}"
79
+ end
80
+ end
81
+
82
+ def configured_hostname
83
+ return @configured_hostname if defined?(@configured_hostname)
84
+
85
+ if should_use_uds?
86
+ nil
87
+ else
88
+ @configured_hostname = (parsed_url.hostname if parsed_url)
89
+ end
90
+ end
91
+
92
+ def configured_port
93
+ return @configured_port if defined?(@configured_port)
94
+
95
+ @configured_port = (parsed_url.port if parsed_url)
96
+ end
97
+
98
+ # Note that this method should always return true or false
99
+ def ssl?
100
+ if configured_hostname
101
+ configured_ssl || false
102
+ else
103
+ if should_use_uds?
104
+ false
105
+ else
106
+ # If no hostname is specified, we are communicating with the
107
+ # default Datadog intake, which uses TLS.
108
+ true
109
+ end
110
+ end
111
+ end
112
+
113
+ # Note that this method can return nil
114
+ def configured_ssl
115
+ return @configured_ssl if defined?(@configured_ssl)
116
+
117
+ @configured_ssl = (parsed_url_ssl? if parsed_url)
118
+ end
119
+
120
+ def port
121
+ if configured_port
122
+ configured_port
123
+ else
124
+ if should_use_uds?
125
+ nil
126
+ else
127
+ # If no hostname is specified, we are communicating with the
128
+ # default Datadog intake, which exists on port 443.
129
+ 443
130
+ end
131
+ end
132
+ end
133
+
134
+ def mixed_http_and_uds
135
+ false
136
+ end
137
+
138
+ def configured_uds_path
139
+ return @configured_uds_path if defined?(@configured_uds_path)
140
+
141
+ parsed_url_uds_path
142
+ end
143
+
144
+ def can_use_uds?
145
+ # While in theory agentless transport could communicate via UDS,
146
+ # in practice "agentless" means we are communicating with Datadog
147
+ # infrastructure which is always remote.
148
+ # Permit UDS for proxy usage?
149
+ !configured_uds_path.nil?
150
+ end
151
+
152
+ def parsed_url
153
+ return @parsed_url if defined?(@parsed_url)
154
+
155
+ @parsed_url =
156
+ if @url_override
157
+ parsed = URI.parse(@url_override)
158
+
159
+ # Agentless URL should never refer to a UDS?
160
+ if http_scheme?(parsed) || unix_scheme?(parsed)
161
+ parsed
162
+ else
163
+ log_warning(
164
+ "Invalid URI scheme '#{parsed.scheme}' for #{url_override_source}. " \
165
+ "Ignoring the contents of #{url_override_source}."
166
+ )
167
+ nil
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ # rubocop:enable Style/*