ddtrace 1.14.0 → 1.16.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 (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +146 -2
  3. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +3 -5
  4. data/ext/ddtrace_profiling_native_extension/clock_id.h +0 -3
  5. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +0 -22
  6. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +0 -1
  7. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +41 -6
  8. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +3 -0
  9. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +76 -24
  10. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +1 -1
  11. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +207 -32
  12. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.h +1 -1
  13. data/ext/ddtrace_profiling_native_extension/extconf.rb +8 -2
  14. data/ext/ddtrace_profiling_native_extension/http_transport.c +26 -10
  15. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.c +42 -0
  16. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +6 -0
  17. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +1 -16
  18. data/ext/ddtrace_profiling_native_extension/pid_controller.c +57 -0
  19. data/ext/ddtrace_profiling_native_extension/pid_controller.h +45 -0
  20. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +17 -12
  21. data/ext/ddtrace_profiling_native_extension/profiling.c +0 -2
  22. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +74 -37
  23. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +13 -3
  24. data/lib/datadog/appsec/assets/waf_rules/processors.json +92 -0
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +698 -75
  26. data/lib/datadog/appsec/assets/waf_rules/scanners.json +114 -0
  27. data/lib/datadog/appsec/assets/waf_rules/strict.json +98 -8
  28. data/lib/datadog/appsec/assets.rb +8 -0
  29. data/lib/datadog/appsec/component.rb +9 -2
  30. data/lib/datadog/appsec/configuration/settings.rb +67 -2
  31. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -2
  32. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +46 -0
  33. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +8 -6
  34. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +2 -7
  35. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +2 -5
  36. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +7 -5
  37. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +3 -2
  38. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +34 -10
  39. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -2
  40. data/lib/datadog/appsec/contrib/rails/patcher.rb +9 -3
  41. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +2 -5
  42. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -4
  43. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +13 -7
  44. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +2 -5
  45. data/lib/datadog/appsec/event.rb +106 -50
  46. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -3
  47. data/lib/datadog/appsec/monitor/reactive/set_user.rb +2 -5
  48. data/lib/datadog/appsec/processor/actions.rb +49 -0
  49. data/lib/datadog/appsec/processor/rule_merger.rb +22 -2
  50. data/lib/datadog/appsec/processor.rb +34 -6
  51. data/lib/datadog/appsec/remote.rb +4 -1
  52. data/lib/datadog/appsec/response.rb +82 -4
  53. data/lib/datadog/appsec/sample_rate.rb +21 -0
  54. data/lib/datadog/appsec.rb +2 -2
  55. data/lib/datadog/core/configuration/agent_settings_resolver.rb +29 -24
  56. data/lib/datadog/core/configuration/base.rb +1 -11
  57. data/lib/datadog/core/configuration/components.rb +7 -2
  58. data/lib/datadog/core/configuration/ext.rb +21 -0
  59. data/lib/datadog/core/configuration/option.rb +2 -4
  60. data/lib/datadog/core/configuration/option_definition.rb +17 -41
  61. data/lib/datadog/core/configuration/options.rb +5 -5
  62. data/lib/datadog/core/configuration/settings.rb +47 -45
  63. data/lib/datadog/core/environment/execution.rb +47 -9
  64. data/lib/datadog/core/environment/variable_helpers.rb +0 -69
  65. data/lib/datadog/core/error.rb +1 -0
  66. data/lib/datadog/core/git/ext.rb +2 -0
  67. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  68. data/lib/datadog/core/remote/component.rb +2 -2
  69. data/lib/datadog/core/remote/negotiation.rb +2 -2
  70. data/lib/datadog/core/remote/transport/config.rb +60 -0
  71. data/lib/datadog/core/remote/transport/http/api/instance.rb +39 -0
  72. data/lib/datadog/core/remote/transport/http/api/spec.rb +21 -0
  73. data/lib/datadog/core/remote/transport/http/api.rb +58 -0
  74. data/lib/datadog/core/remote/transport/http/builder.rb +219 -0
  75. data/lib/datadog/core/remote/transport/http/client.rb +48 -0
  76. data/lib/datadog/core/remote/transport/http/config.rb +280 -0
  77. data/lib/datadog/core/remote/transport/http/negotiation.rb +146 -0
  78. data/lib/datadog/core/remote/transport/http.rb +179 -0
  79. data/lib/datadog/core/{transport → remote/transport}/negotiation.rb +25 -23
  80. data/lib/datadog/core/remote/worker.rb +3 -1
  81. data/lib/datadog/core/telemetry/collector.rb +3 -2
  82. data/lib/datadog/core/telemetry/http/transport.rb +2 -1
  83. data/lib/datadog/core/transport/ext.rb +47 -0
  84. data/lib/datadog/core/transport/http/adapters/net.rb +168 -0
  85. data/lib/datadog/core/transport/http/adapters/registry.rb +29 -0
  86. data/lib/datadog/core/transport/http/adapters/test.rb +89 -0
  87. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +83 -0
  88. data/lib/datadog/core/transport/http/api/endpoint.rb +31 -0
  89. data/lib/datadog/core/transport/http/api/fallbacks.rb +26 -0
  90. data/lib/datadog/core/transport/http/api/map.rb +18 -0
  91. data/lib/datadog/core/transport/http/env.rb +62 -0
  92. data/lib/datadog/core/transport/http/response.rb +60 -0
  93. data/lib/datadog/core/transport/parcel.rb +22 -0
  94. data/lib/datadog/core/transport/request.rb +17 -0
  95. data/lib/datadog/core/transport/response.rb +64 -0
  96. data/lib/datadog/core/workers/polling.rb +2 -2
  97. data/lib/datadog/opentelemetry/api/context.rb +10 -3
  98. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -1
  99. data/lib/datadog/opentelemetry/sdk/span_processor.rb +14 -2
  100. data/lib/datadog/opentelemetry/sdk/trace/span.rb +68 -0
  101. data/lib/datadog/opentelemetry/trace.rb +58 -0
  102. data/lib/datadog/opentelemetry.rb +1 -0
  103. data/lib/datadog/opentracer.rb +9 -0
  104. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -19
  105. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  106. data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
  107. data/lib/datadog/profiling/component.rb +24 -99
  108. data/lib/datadog/profiling/ext.rb +0 -12
  109. data/lib/datadog/profiling/flush.rb +0 -3
  110. data/lib/datadog/profiling/http_transport.rb +6 -3
  111. data/lib/datadog/profiling/native_extension.rb +0 -21
  112. data/lib/datadog/profiling/profiler.rb +36 -13
  113. data/lib/datadog/profiling/scheduler.rb +16 -9
  114. data/lib/datadog/profiling.rb +8 -81
  115. data/lib/datadog/tracing/component.rb +10 -4
  116. data/lib/datadog/tracing/configuration/agent_settings_resolver.rb +13 -0
  117. data/lib/datadog/tracing/configuration/ext.rb +4 -2
  118. data/lib/datadog/tracing/configuration/settings.rb +14 -7
  119. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -0
  122. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +106 -197
  123. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +3 -0
  124. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  125. data/lib/datadog/tracing/contrib/concurrent_ruby/context_composite_executor_service.rb +14 -14
  126. data/lib/datadog/tracing/contrib/concurrent_ruby/future_patch.rb +3 -10
  127. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +2 -1
  128. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +8 -1
  129. data/lib/datadog/tracing/contrib/concurrent_ruby/promises_future_patch.rb +22 -0
  130. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  131. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +6 -0
  132. data/lib/datadog/tracing/contrib/dalli/ext.rb +7 -0
  133. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +9 -2
  134. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +1 -1
  135. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +5 -0
  136. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +5 -0
  137. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +8 -0
  138. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -0
  139. data/lib/datadog/tracing/contrib/ext.rb +3 -0
  140. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
  141. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -0
  142. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +21 -1
  143. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +11 -1
  144. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +18 -0
  145. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +0 -4
  146. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +3 -3
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -0
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -0
  149. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  150. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +7 -0
  151. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +13 -3
  152. data/lib/datadog/tracing/contrib/opensearch/integration.rb +2 -2
  153. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +7 -0
  154. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -0
  155. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +5 -0
  156. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
  157. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +1 -1
  158. data/lib/datadog/tracing/contrib/racecar/event.rb +5 -0
  159. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +14 -4
  160. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +4 -4
  161. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +1 -1
  162. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
  163. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +3 -38
  164. data/lib/datadog/tracing/contrib/redis/tags.rb +7 -2
  165. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +46 -33
  166. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +1 -1
  167. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -0
  168. data/lib/datadog/tracing/contrib/sequel/utils.rb +5 -0
  169. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +1 -1
  170. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +1 -1
  171. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +1 -1
  172. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +2 -2
  173. data/lib/datadog/tracing/diagnostics/environment_logger.rb +6 -0
  174. data/lib/datadog/tracing/distributed/propagation.rb +13 -33
  175. data/lib/datadog/tracing/metadata/tagging.rb +3 -3
  176. data/lib/datadog/tracing/sync_writer.rb +3 -3
  177. data/lib/datadog/tracing/tracer.rb +2 -0
  178. data/lib/datadog/{core → tracing}/transport/http/api/instance.rb +1 -1
  179. data/lib/datadog/{core → tracing}/transport/http/api/spec.rb +1 -1
  180. data/lib/datadog/tracing/transport/http/api.rb +43 -0
  181. data/lib/datadog/{core → tracing}/transport/http/builder.rb +13 -68
  182. data/lib/datadog/tracing/transport/http/client.rb +57 -0
  183. data/lib/datadog/tracing/transport/http/statistics.rb +47 -0
  184. data/lib/datadog/tracing/transport/http/traces.rb +152 -0
  185. data/lib/datadog/tracing/transport/http.rb +124 -0
  186. data/lib/datadog/tracing/transport/io/client.rb +89 -0
  187. data/lib/datadog/tracing/transport/io/response.rb +27 -0
  188. data/lib/datadog/tracing/transport/io/traces.rb +101 -0
  189. data/lib/datadog/tracing/transport/io.rb +30 -0
  190. data/lib/datadog/tracing/transport/serializable_trace.rb +126 -0
  191. data/lib/datadog/tracing/transport/statistics.rb +77 -0
  192. data/lib/datadog/tracing/transport/trace_formatter.rb +209 -0
  193. data/lib/datadog/tracing/transport/traces.rb +224 -0
  194. data/lib/datadog/tracing/workers/trace_writer.rb +5 -3
  195. data/lib/datadog/tracing/workers.rb +3 -2
  196. data/lib/datadog/tracing/writer.rb +5 -2
  197. data/lib/ddtrace/transport/ext.rb +17 -15
  198. data/lib/ddtrace/version.rb +1 -1
  199. data/lib/ddtrace.rb +1 -1
  200. metadata +73 -96
  201. data/lib/datadog/ci/configuration/components.rb +0 -32
  202. data/lib/datadog/ci/configuration/settings.rb +0 -51
  203. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -35
  204. data/lib/datadog/ci/contrib/cucumber/ext.rb +0 -22
  205. data/lib/datadog/ci/contrib/cucumber/formatter.rb +0 -94
  206. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +0 -28
  207. data/lib/datadog/ci/contrib/cucumber/integration.rb +0 -47
  208. data/lib/datadog/ci/contrib/cucumber/patcher.rb +0 -27
  209. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -35
  210. data/lib/datadog/ci/contrib/minitest/ext.rb +0 -21
  211. data/lib/datadog/ci/contrib/minitest/integration.rb +0 -49
  212. data/lib/datadog/ci/contrib/minitest/patcher.rb +0 -27
  213. data/lib/datadog/ci/contrib/minitest/test_helper.rb +0 -68
  214. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -35
  215. data/lib/datadog/ci/contrib/rspec/example.rb +0 -68
  216. data/lib/datadog/ci/contrib/rspec/ext.rb +0 -21
  217. data/lib/datadog/ci/contrib/rspec/integration.rb +0 -48
  218. data/lib/datadog/ci/contrib/rspec/patcher.rb +0 -27
  219. data/lib/datadog/ci/ext/app_types.rb +0 -9
  220. data/lib/datadog/ci/ext/environment.rb +0 -575
  221. data/lib/datadog/ci/ext/settings.rb +0 -10
  222. data/lib/datadog/ci/ext/test.rb +0 -35
  223. data/lib/datadog/ci/extensions.rb +0 -19
  224. data/lib/datadog/ci/flush.rb +0 -38
  225. data/lib/datadog/ci/test.rb +0 -81
  226. data/lib/datadog/ci.rb +0 -21
  227. data/lib/datadog/core/configuration/dependency_resolver.rb +0 -28
  228. data/lib/datadog/core/configuration/option_definition_set.rb +0 -22
  229. data/lib/datadog/core/configuration/option_set.rb +0 -10
  230. data/lib/datadog/core/transport/config.rb +0 -58
  231. data/lib/datadog/core/transport/http/api.rb +0 -57
  232. data/lib/datadog/core/transport/http/client.rb +0 -45
  233. data/lib/datadog/core/transport/http/config.rb +0 -278
  234. data/lib/datadog/core/transport/http/negotiation.rb +0 -144
  235. data/lib/datadog/core/transport/http.rb +0 -169
  236. data/lib/datadog/core/utils/object_set.rb +0 -43
  237. data/lib/datadog/core/utils/string_table.rb +0 -47
  238. data/lib/datadog/profiling/backtrace_location.rb +0 -34
  239. data/lib/datadog/profiling/buffer.rb +0 -43
  240. data/lib/datadog/profiling/collectors/old_stack.rb +0 -301
  241. data/lib/datadog/profiling/encoding/profile.rb +0 -41
  242. data/lib/datadog/profiling/event.rb +0 -15
  243. data/lib/datadog/profiling/events/stack.rb +0 -82
  244. data/lib/datadog/profiling/old_recorder.rb +0 -107
  245. data/lib/datadog/profiling/pprof/builder.rb +0 -125
  246. data/lib/datadog/profiling/pprof/converter.rb +0 -102
  247. data/lib/datadog/profiling/pprof/message_set.rb +0 -16
  248. data/lib/datadog/profiling/pprof/payload.rb +0 -20
  249. data/lib/datadog/profiling/pprof/pprof.proto +0 -212
  250. data/lib/datadog/profiling/pprof/pprof_pb.rb +0 -81
  251. data/lib/datadog/profiling/pprof/stack_sample.rb +0 -139
  252. data/lib/datadog/profiling/pprof/string_table.rb +0 -12
  253. data/lib/datadog/profiling/pprof/template.rb +0 -118
  254. data/lib/datadog/profiling/trace_identifiers/ddtrace.rb +0 -43
  255. data/lib/datadog/profiling/trace_identifiers/helper.rb +0 -45
  256. data/lib/ddtrace/transport/http/adapters/net.rb +0 -168
  257. data/lib/ddtrace/transport/http/adapters/registry.rb +0 -27
  258. data/lib/ddtrace/transport/http/adapters/test.rb +0 -85
  259. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +0 -77
  260. data/lib/ddtrace/transport/http/api/endpoint.rb +0 -29
  261. data/lib/ddtrace/transport/http/api/fallbacks.rb +0 -24
  262. data/lib/ddtrace/transport/http/api/instance.rb +0 -35
  263. data/lib/ddtrace/transport/http/api/map.rb +0 -16
  264. data/lib/ddtrace/transport/http/api/spec.rb +0 -17
  265. data/lib/ddtrace/transport/http/api.rb +0 -39
  266. data/lib/ddtrace/transport/http/builder.rb +0 -176
  267. data/lib/ddtrace/transport/http/client.rb +0 -52
  268. data/lib/ddtrace/transport/http/env.rb +0 -58
  269. data/lib/ddtrace/transport/http/response.rb +0 -58
  270. data/lib/ddtrace/transport/http/statistics.rb +0 -43
  271. data/lib/ddtrace/transport/http/traces.rb +0 -144
  272. data/lib/ddtrace/transport/http.rb +0 -117
  273. data/lib/ddtrace/transport/io/client.rb +0 -85
  274. data/lib/ddtrace/transport/io/response.rb +0 -25
  275. data/lib/ddtrace/transport/io/traces.rb +0 -99
  276. data/lib/ddtrace/transport/io.rb +0 -28
  277. data/lib/ddtrace/transport/parcel.rb +0 -20
  278. data/lib/ddtrace/transport/request.rb +0 -15
  279. data/lib/ddtrace/transport/response.rb +0 -60
  280. data/lib/ddtrace/transport/serializable_trace.rb +0 -122
  281. data/lib/ddtrace/transport/statistics.rb +0 -75
  282. data/lib/ddtrace/transport/trace_formatter.rb +0 -207
  283. data/lib/ddtrace/transport/traces.rb +0 -216
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../core/utils/duration'
4
+ require_relative '../sample_rate'
4
5
 
5
6
  module Datadog
6
7
  module AppSec
@@ -25,7 +26,7 @@ module Datadog
25
26
  add_settings!(base)
26
27
  end
27
28
 
28
- # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/BlockLength
29
+ # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/BlockLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
29
30
  def self.add_settings!(base)
30
31
  base.class_eval do
31
32
  settings :appsec do
@@ -95,6 +96,46 @@ module Datadog
95
96
  o.default DEFAULT_OBFUSCATOR_VALUE_REGEX
96
97
  end
97
98
 
99
+ settings :block do
100
+ settings :templates do
101
+ option :html do |o|
102
+ o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML'
103
+ o.type :string, nilable: true
104
+ o.setter do |value|
105
+ if value
106
+ raise(ArgumentError, "appsec.templates.html: file not found: #{value}") unless File.exist?(value)
107
+
108
+ File.open(value, 'rb', &:read) || ''
109
+ end
110
+ end
111
+ end
112
+
113
+ option :json do |o|
114
+ o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON'
115
+ o.type :string, nilable: true
116
+ o.setter do |value|
117
+ if value
118
+ raise(ArgumentError, "appsec.templates.json: file not found: #{value}") unless File.exist?(value)
119
+
120
+ File.open(value, 'rb', &:read) || ''
121
+ end
122
+ end
123
+ end
124
+
125
+ option :text do |o|
126
+ o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_TEXT'
127
+ o.type :string, nilable: true
128
+ o.setter do |value|
129
+ if value
130
+ raise(ArgumentError, "appsec.templates.text: file not found: #{value}") unless File.exist?(value)
131
+
132
+ File.open(value, 'rb', &:read) || ''
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
98
139
  settings :track_user_events do
99
140
  option :enabled do |o|
100
141
  o.default true
@@ -129,10 +170,34 @@ module Datadog
129
170
  end
130
171
  end
131
172
  end
173
+
174
+ settings :api_security do
175
+ option :enabled do |o|
176
+ o.type :bool
177
+ o.env 'DD_EXPERIMENTAL_API_SECURITY_ENABLED'
178
+ o.default false
179
+ end
180
+
181
+ option :sample_rate do |o|
182
+ o.type :float
183
+ o.env 'DD_API_SECURITY_REQUEST_SAMPLE_RATE'
184
+ o.default 0.1
185
+ o.setter do |value|
186
+ value = 1 if value > 1
187
+ SampleRate.new(value)
188
+ end
189
+ end
190
+ end
191
+
192
+ option :parse_response_body do |o|
193
+ o.type :bool
194
+ o.env 'DD_API_SECURITY_PARSE_RESPONSE_BODY'
195
+ o.default true
196
+ end
132
197
  end
133
198
  end
134
199
  end
135
- # rubocop:enable Metrics/AbcSize,Metrics/MethodLength,Metrics/BlockLength
200
+ # rubocop:enable Metrics/AbcSize,Metrics/MethodLength,Metrics/BlockLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
136
201
  end
137
202
  end
138
203
  end
@@ -40,9 +40,13 @@ module Datadog
40
40
  end
41
41
 
42
42
  def headers
43
- request.env.each_with_object({}) do |(k, v), h|
44
- h[k.gsub(/^HTTP_/, '').downcase.tr('_', '-')] = v if k =~ /^HTTP_/
43
+ result = request.env.each_with_object({}) do |(k, v), h|
44
+ h[k.gsub(/^HTTP_/, '').downcase!.tr('_', '-')] = v if k =~ /^HTTP_/
45
45
  end
46
+
47
+ result['content-type'] = request.content_type if request.content_type
48
+ result['content-length'] = request.content_length if request.content_length
49
+ result
46
50
  end
47
51
 
48
52
  def body
@@ -19,9 +19,55 @@ module Datadog
19
19
  @scope = scope
20
20
  end
21
21
 
22
+ def parsed_body
23
+ return unless Datadog.configuration.appsec.parse_response_body
24
+
25
+ unless body.instance_of?(Array)
26
+ Datadog.logger.debug do
27
+ "Response body type unsupported: #{body.class}"
28
+ end
29
+ return
30
+ end
31
+
32
+ return unless json_content_type?
33
+
34
+ result = ''.dup
35
+ all_body_parts_are_string = true
36
+
37
+ body.each do |body_part|
38
+ if body_part.is_a?(String)
39
+ result.concat(body_part)
40
+ else
41
+ all_body_parts_are_string = false
42
+ break
43
+ end
44
+ end
45
+
46
+ return unless all_body_parts_are_string
47
+
48
+ begin
49
+ JSON.parse(result)
50
+ rescue JSON::ParserError => e
51
+ Datadog.logger.debug { "Failed to parse response body. Error #{e.class}. Message #{e.message}" }
52
+ nil
53
+ end
54
+ end
55
+
22
56
  def response
23
57
  @response ||= ::Rack::Response.new(body, status, headers)
24
58
  end
59
+
60
+ private
61
+
62
+ VALID_JSON_TYPES = [
63
+ 'application/json',
64
+ 'text/json'
65
+ ].freeze
66
+
67
+ def json_content_type?
68
+ content_type = headers['content-type']
69
+ VALID_JSON_TYPES.include?(content_type)
70
+ end
25
71
  end
26
72
  end
27
73
  end
@@ -28,7 +28,7 @@ module Datadog
28
28
  scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
29
29
 
30
30
  AppSec::Reactive::Operation.new('rack.request') do |op|
31
- Rack::Reactive::Request.subscribe(op, scope.processor_context) do |result, _block|
31
+ Rack::Reactive::Request.subscribe(op, scope.processor_context) do |result|
32
32
  if result.status == :match
33
33
  # TODO: should this hash be an Event instance instead?
34
34
  event = {
@@ -48,7 +48,7 @@ module Datadog
48
48
  end
49
49
  end
50
50
 
51
- _result, block = Rack::Reactive::Request.publish(op, gateway_request)
51
+ block = Rack::Reactive::Request.publish(op, gateway_request)
52
52
  end
53
53
 
54
54
  next [nil, [[:block, event]]] if block
@@ -67,11 +67,12 @@ module Datadog
67
67
  def watch_response(gateway = Instrumentation.gateway)
68
68
  gateway.watch('rack.response', :appsec) do |stack, gateway_response|
69
69
  block = false
70
+
70
71
  event = nil
71
72
  scope = gateway_response.scope
72
73
 
73
74
  AppSec::Reactive::Operation.new('rack.response') do |op|
74
- Rack::Reactive::Response.subscribe(op, scope.processor_context) do |result, _block|
75
+ Rack::Reactive::Response.subscribe(op, scope.processor_context) do |result|
75
76
  if result.status == :match
76
77
  # TODO: should this hash be an Event instance instead?
77
78
  event = {
@@ -91,7 +92,7 @@ module Datadog
91
92
  end
92
93
  end
93
94
 
94
- _result, block = Rack::Reactive::Response.publish(op, gateway_response)
95
+ block = Rack::Reactive::Response.publish(op, gateway_response)
95
96
  end
96
97
 
97
98
  next [nil, [[:block, event]]] if block
@@ -110,11 +111,12 @@ module Datadog
110
111
  def watch_request_body(gateway = Instrumentation.gateway)
111
112
  gateway.watch('rack.request.body', :appsec) do |stack, gateway_request|
112
113
  block = false
114
+
113
115
  event = nil
114
116
  scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
115
117
 
116
118
  AppSec::Reactive::Operation.new('rack.request.body') do |op|
117
- Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result, _block|
119
+ Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result|
118
120
  if result.status == :match
119
121
  # TODO: should this hash be an Event instance instead?
120
122
  event = {
@@ -134,7 +136,7 @@ module Datadog
134
136
  end
135
137
  end
136
138
 
137
- _result, block = Rack::Reactive::RequestBody.publish(op, gateway_request)
139
+ block = Rack::Reactive::RequestBody.publish(op, gateway_request)
138
140
  end
139
141
 
140
142
  next [nil, [[:block, event]]] if block
@@ -30,7 +30,6 @@ module Datadog
30
30
  end
31
31
  end
32
32
 
33
- # rubocop:disable Metrics/MethodLength
34
33
  def self.subscribe(op, waf_context)
35
34
  op.subscribe(*ADDRESSES) do |*values|
36
35
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
@@ -61,11 +60,8 @@ module Datadog
61
60
  when :match
62
61
  Datadog.logger.debug { "WAF: #{result.inspect}" }
63
62
 
64
- block = result.actions.include?('block')
65
-
66
- yield [result, block]
67
-
68
- throw(:block, [result, true]) if block
63
+ yield result
64
+ throw(:block, true) unless result.actions.empty?
69
65
  when :ok
70
66
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
71
67
  when :invalid_call
@@ -76,7 +72,6 @@ module Datadog
76
72
  Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
77
73
  end
78
74
  end
79
- # rubocop:enable Metrics/MethodLength
80
75
  end
81
76
  end
82
77
  end
@@ -39,11 +39,8 @@ module Datadog
39
39
  when :match
40
40
  Datadog.logger.debug { "WAF: #{result.inspect}" }
41
41
 
42
- block = result.actions.include?('block')
43
-
44
- yield [result, block]
45
-
46
- throw(:block, [result, true]) if block
42
+ yield result
43
+ throw(:block, true) unless result.actions.empty?
47
44
  when :ok
48
45
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
49
46
  when :invalid_call
@@ -10,6 +10,7 @@ module Datadog
10
10
  ADDRESSES = [
11
11
  'response.status',
12
12
  'response.headers',
13
+ 'response.body',
13
14
  ].freeze
14
15
  private_constant :ADDRESSES
15
16
 
@@ -17,6 +18,7 @@ module Datadog
17
18
  catch(:block) do
18
19
  op.publish('response.status', gateway_response.status)
19
20
  op.publish('response.headers', gateway_response.headers)
21
+ op.publish('response.body', gateway_response.parsed_body)
20
22
 
21
23
  nil
22
24
  end
@@ -29,6 +31,7 @@ module Datadog
29
31
  response_status = values[0]
30
32
  response_headers = values[1]
31
33
  response_headers_no_cookies = response_headers.dup.tap { |h| h.delete('set-cookie') }
34
+ response_body = values[2]
32
35
 
33
36
  waf_args = {
34
37
  'server.response.status' => response_status.to_s,
@@ -36,6 +39,8 @@ module Datadog
36
39
  'server.response.headers.no_cookies' => response_headers_no_cookies,
37
40
  }
38
41
 
42
+ waf_args['server.response.body'] = response_body if response_body
43
+
39
44
  waf_timeout = Datadog.configuration.appsec.waf_timeout
40
45
  result = waf_context.run(waf_args, waf_timeout)
41
46
 
@@ -45,11 +50,8 @@ module Datadog
45
50
  when :match
46
51
  Datadog.logger.debug { "WAF: #{result.inspect}" }
47
52
 
48
- block = result.actions.include?('block')
49
-
50
- yield [result, block]
51
-
52
- throw(:block, [result, true]) if block
53
+ yield result
54
+ throw(:block, true) unless result.actions.empty?
53
55
  when :ok
54
56
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
55
57
  when :invalid_call
@@ -30,8 +30,9 @@ module Datadog
30
30
  @app.call(env)
31
31
  end
32
32
 
33
- if request_response && request_response.any? { |action, _event| action == :block }
34
- request_return = AppSec::Response.negotiate(env).to_rack
33
+ if request_response
34
+ blocked_event = request_response.find { |action, _event| action == :block }
35
+ request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
35
36
  end
36
37
 
37
38
  request_return
@@ -61,19 +61,40 @@ module Datadog
61
61
  end
62
62
  end
63
63
 
64
- if request_response && request_response.any? { |action, _event| action == :block }
65
- request_return = AppSec::Response.negotiate(env).to_rack
64
+ if request_response
65
+ blocked_event = request_response.find { |action, _options| action == :block }
66
+ request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
67
+ end
68
+
69
+ if request_return[2].respond_to?(:to_ary)
70
+ # Following the Rack specification. The response body should only call :each once.
71
+ # Calling :to_ary returns an array with identical content as the produced when calling :each
72
+ # replacing request_return[2] with that new value allow us to safely operate on the response body.
73
+ # On Gateway::Response#parsed_body we might iterate over the reposne body using :each
74
+ # https://github.com/rack/rack/blob/main/SPEC.rdoc#enumerable-body-
75
+ consumed_body = request_return[2].to_ary
76
+ request_return[2] = consumed_body if consumed_body
66
77
  end
67
78
 
68
79
  gateway_response = Gateway::Response.new(
69
80
  request_return[2],
70
81
  request_return[0],
71
82
  request_return[1],
72
- scope: scope
83
+ scope: scope,
73
84
  )
74
85
 
75
86
  _response_return, response_response = Instrumentation.gateway.push('rack.response', gateway_response)
76
87
 
88
+ result = scope.processor_context.extract_schema
89
+
90
+ if result
91
+ scope.processor_context.events << {
92
+ trace: scope.trace,
93
+ span: scope.service_entry_span,
94
+ waf_result: result,
95
+ }
96
+ end
97
+
77
98
  scope.processor_context.events.each do |e|
78
99
  e[:response] ||= gateway_response
79
100
  e[:request] ||= gateway_request
@@ -81,8 +102,9 @@ module Datadog
81
102
 
82
103
  AppSec::Event.record(scope.service_entry_span, *scope.processor_context.events)
83
104
 
84
- if response_response && response_response.any? { |action, _event| action == :block }
85
- request_return = AppSec::Response.negotiate(env).to_rack
105
+ if response_response
106
+ blocked_event = response_response.find { |action, _options| action == :block }
107
+ request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
86
108
  end
87
109
 
88
110
  request_return
@@ -137,17 +159,19 @@ module Datadog
137
159
  )
138
160
  end
139
161
 
140
- if processor.ruleset_info
141
- span.set_tag('_dd.appsec.event_rules.version', processor.ruleset_info[:version])
162
+ if processor.diagnostics
163
+ diagnostics = processor.diagnostics
164
+
165
+ span.set_tag('_dd.appsec.event_rules.version', diagnostics['ruleset_version'])
142
166
 
143
167
  unless @oneshot_tags_sent
144
168
  # Small race condition, but it's inoccuous: worst case the tags
145
169
  # are sent a couple of times more than expected
146
170
  @oneshot_tags_sent = true
147
171
 
148
- span.set_tag('_dd.appsec.event_rules.loaded', processor.ruleset_info[:loaded].to_f)
149
- span.set_tag('_dd.appsec.event_rules.error_count', processor.ruleset_info[:failed].to_f)
150
- span.set_tag('_dd.appsec.event_rules.errors', JSON.dump(processor.ruleset_info[:errors]))
172
+ span.set_tag('_dd.appsec.event_rules.loaded', diagnostics['rules']['loaded'].size.to_f)
173
+ span.set_tag('_dd.appsec.event_rules.error_count', diagnostics['rules']['failed'].size.to_f)
174
+ span.set_tag('_dd.appsec.event_rules.errors', JSON.dump(diagnostics['rules']['errors']))
151
175
  span.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(processor.addresses))
152
176
 
153
177
  # Ensure these tags reach the backend
@@ -20,11 +20,12 @@ module Datadog
20
20
  def watch_request_action(gateway = Instrumentation.gateway)
21
21
  gateway.watch('rails.request.action', :appsec) do |stack, gateway_request|
22
22
  block = false
23
+
23
24
  event = nil
24
25
  scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
25
26
 
26
27
  AppSec::Reactive::Operation.new('rails.request.action') do |op|
27
- Rails::Reactive::Action.subscribe(op, scope.processor_context) do |result, _block|
28
+ Rails::Reactive::Action.subscribe(op, scope.processor_context) do |result|
28
29
  if result.status == :match
29
30
  # TODO: should this hash be an Event instance instead?
30
31
  event = {
@@ -44,7 +45,7 @@ module Datadog
44
45
  end
45
46
  end
46
47
 
47
- _result, block = Rails::Reactive::Action.publish(op, gateway_request)
48
+ block = Rails::Reactive::Action.publish(op, gateway_request)
48
49
  end
49
50
 
50
51
  next [nil, [[:block, event]]] if block
@@ -83,9 +83,15 @@ module Datadog
83
83
  super
84
84
  end
85
85
 
86
- if request_response && request_response.any? { |action, _event| action == :block }
87
- @_response = AppSec::Response.negotiate(env).to_action_dispatch_response
88
- request_return = @_response.body
86
+ if request_response
87
+ blocked_event = request_response.find { |action, _options| action == :block }
88
+ if blocked_event
89
+ @_response = AppSec::Response.negotiate(
90
+ env,
91
+ blocked_event.last[:actions]
92
+ ).to_action_dispatch_response
93
+ request_return = @_response.body
94
+ end
89
95
  end
90
96
 
91
97
  request_return
@@ -45,11 +45,8 @@ module Datadog
45
45
  when :match
46
46
  Datadog.logger.debug { "WAF: #{result.inspect}" }
47
47
 
48
- block = result.actions.include?('block')
49
-
50
- yield [result, block]
51
-
52
- throw(:block, [result, true]) if block
48
+ yield result
49
+ throw(:block, true) unless result.actions.empty?
53
50
  when :ok
54
51
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
55
52
  when :invalid_call
@@ -22,11 +22,12 @@ module Datadog
22
22
  def watch_request_dispatch(gateway = Instrumentation.gateway)
23
23
  gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
24
24
  block = false
25
+
25
26
  event = nil
26
27
  scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
27
28
 
28
29
  AppSec::Reactive::Operation.new('sinatra.request.dispatch') do |op|
29
- Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result, _block|
30
+ Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result|
30
31
  if result.status == :match
31
32
  # TODO: should this hash be an Event instance instead?
32
33
  event = {
@@ -46,7 +47,7 @@ module Datadog
46
47
  end
47
48
  end
48
49
 
49
- _result, block = Rack::Reactive::RequestBody.publish(op, gateway_request)
50
+ block = Rack::Reactive::RequestBody.publish(op, gateway_request)
50
51
  end
51
52
 
52
53
  next [nil, [[:block, event]]] if block
@@ -65,11 +66,12 @@ module Datadog
65
66
  def watch_request_routed(gateway = Instrumentation.gateway)
66
67
  gateway.watch('sinatra.request.routed', :appsec) do |stack, (gateway_request, gateway_route_params)|
67
68
  block = false
69
+
68
70
  event = nil
69
71
  scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
70
72
 
71
73
  AppSec::Reactive::Operation.new('sinatra.request.routed') do |op|
72
- Sinatra::Reactive::Routed.subscribe(op, scope.processor_context) do |result, _block|
74
+ Sinatra::Reactive::Routed.subscribe(op, scope.processor_context) do |result|
73
75
  if result.status == :match
74
76
  # TODO: should this hash be an Event instance instead?
75
77
  event = {
@@ -89,7 +91,7 @@ module Datadog
89
91
  end
90
92
  end
91
93
 
92
- _result, block = Sinatra::Reactive::Routed.publish(op, [gateway_request, gateway_route_params])
94
+ block = Sinatra::Reactive::Routed.publish(op, [gateway_request, gateway_route_params])
93
95
  end
94
96
 
95
97
  next [nil, [[:block, event]]] if block
@@ -65,9 +65,12 @@ module Datadog
65
65
  catch(Datadog::AppSec::Contrib::Sinatra::Ext::ROUTE_INTERRUPT) { super }
66
66
  end
67
67
 
68
- if request_response && request_response.any? { |action, _event| action == :block }
69
- self.response = AppSec::Response.negotiate(env).to_sinatra_response
70
- request_return = nil
68
+ if request_response
69
+ blocked_event = request_response.find { |action, _options| action == :block }
70
+ if blocked_event
71
+ self.response = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_sinatra_response
72
+ request_return = nil
73
+ end
71
74
  end
72
75
 
73
76
  request_return
@@ -103,11 +106,14 @@ module Datadog
103
106
  [gateway_request, gateway_route_params]
104
107
  )
105
108
 
106
- if request_response && request_response.any? { |action, _event| action == :block }
107
- self.response = AppSec::Response.negotiate(env).to_sinatra_response
109
+ if request_response
110
+ blocked_event = request_response.find { |action, _options| action == :block }
111
+ if blocked_event
112
+ self.response = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_sinatra_response
108
113
 
109
- # interrupt request and return response to dispatch! for consistency
110
- throw(Datadog::AppSec::Contrib::Sinatra::Ext::ROUTE_INTERRUPT, response)
114
+ # interrupt request and return response to dispatch! for consistency
115
+ throw(Datadog::AppSec::Contrib::Sinatra::Ext::ROUTE_INTERRUPT, response)
116
+ end
111
117
  end
112
118
 
113
119
  yield(*args)
@@ -40,11 +40,8 @@ module Datadog
40
40
  when :match
41
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
42
42
 
43
- block = result.actions.include?('block')
44
-
45
- yield [result, block]
46
-
47
- throw(:block, [result, true]) if block
43
+ yield result
44
+ throw(:block, true) unless result.actions.empty?
48
45
  when :ok
49
46
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
50
47
  when :invalid_call