dd-trace 5.132.0 → 5.134.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 (231) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-runner-setup.mjs +27 -0
  16. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  17. package/index.d.ts +41 -19
  18. package/initialize.mjs +1 -3
  19. package/openfeature/rules-based.d.ts +11 -0
  20. package/openfeature/rules-based.js +7 -0
  21. package/package.json +25 -22
  22. package/packages/datadog-core/src/storage.js +16 -0
  23. package/packages/datadog-esbuild/index.js +18 -12
  24. package/packages/datadog-instrumentations/index.js +2 -1
  25. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  26. package/packages/datadog-instrumentations/src/ai.js +3 -3
  27. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  28. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  29. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  30. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  31. package/packages/datadog-instrumentations/src/cucumber.js +65 -41
  32. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  33. package/packages/datadog-instrumentations/src/electron.js +6 -6
  34. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  35. package/packages/datadog-instrumentations/src/fs.js +2 -2
  36. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  37. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  38. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +28 -9
  39. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  40. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  41. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  42. package/packages/datadog-instrumentations/src/helpers/register.js +45 -1
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +89 -6
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/jest.js +12 -0
  47. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  48. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/vitest.js +8 -0
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  50. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +5 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  53. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  54. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  55. package/packages/datadog-instrumentations/src/jest.js +72 -67
  56. package/packages/datadog-instrumentations/src/knex.js +9 -5
  57. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  58. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  59. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  60. package/packages/datadog-instrumentations/src/mocha/utils.js +38 -4
  61. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  62. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  63. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  64. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  65. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  66. package/packages/datadog-instrumentations/src/nova-sonic/session.js +492 -0
  67. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  68. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  69. package/packages/datadog-instrumentations/src/openai.js +7 -0
  70. package/packages/datadog-instrumentations/src/pino.js +5 -6
  71. package/packages/datadog-instrumentations/src/playwright.js +2 -0
  72. package/packages/datadog-instrumentations/src/redis.js +27 -13
  73. package/packages/datadog-instrumentations/src/router.js +30 -0
  74. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  75. package/packages/datadog-instrumentations/src/stripe.js +23 -12
  76. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -5
  77. package/packages/datadog-instrumentations/src/vitest-main.js +16 -18
  78. package/packages/datadog-instrumentations/src/vitest-util.js +7 -4
  79. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  80. package/packages/datadog-instrumentations/src/vitest.js +9 -1
  81. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  82. package/packages/datadog-instrumentations/src/ws.js +1 -4
  83. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  84. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  85. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  86. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  87. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  88. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  89. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  90. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  91. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  92. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  93. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  94. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +4 -5
  95. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  96. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  97. package/packages/datadog-plugin-cypress/src/support.js +3 -4
  98. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  99. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  100. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  101. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  102. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  103. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  104. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  105. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  106. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  107. package/packages/datadog-plugin-next/src/index.js +1 -2
  108. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  109. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  110. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  111. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  112. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  113. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  114. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  115. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  116. package/packages/datadog-webpack/index.js +55 -43
  117. package/packages/datadog-webpack/src/loader.js +56 -8
  118. package/packages/dd-trace/src/aiguard/evaluation.js +143 -1
  119. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  120. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  121. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  123. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  124. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  127. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  128. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  129. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  130. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  131. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  132. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  133. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  134. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  135. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  136. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  137. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  138. package/packages/dd-trace/src/appsec/stack_trace.js +4 -4
  139. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  140. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -2
  141. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  142. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  143. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  144. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  145. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  146. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  147. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  148. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  149. package/packages/dd-trace/src/config/defaults.js +5 -5
  150. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  151. package/packages/dd-trace/src/config/helper.js +34 -3
  152. package/packages/dd-trace/src/config/index.js +55 -9
  153. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  154. package/packages/dd-trace/src/config/parsers.js +2 -2
  155. package/packages/dd-trace/src/config/supported-configurations.json +44 -0
  156. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  157. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  158. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -5
  159. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -5
  160. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  161. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +4 -6
  162. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  163. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  164. package/packages/dd-trace/src/debugger/unsupported-runtime.js +16 -0
  165. package/packages/dd-trace/src/dogstatsd.js +1 -1
  166. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  167. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  168. package/packages/dd-trace/src/id.js +14 -1
  169. package/packages/dd-trace/src/lambda/handler.js +179 -19
  170. package/packages/dd-trace/src/lambda/index.js +15 -1
  171. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  172. package/packages/dd-trace/src/llmobs/agent-manifest.js +19 -2
  173. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  174. package/packages/dd-trace/src/llmobs/constants/tags.js +15 -0
  175. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  176. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  177. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  178. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  180. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  181. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +5 -0
  182. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +64 -2
  183. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  184. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  185. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  186. package/packages/dd-trace/src/llmobs/sdk.js +15 -4
  187. package/packages/dd-trace/src/llmobs/span_processor.js +13 -2
  188. package/packages/dd-trace/src/llmobs/tagger.js +20 -10
  189. package/packages/dd-trace/src/llmobs/util.js +11 -6
  190. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  191. package/packages/dd-trace/src/log/log.js +1 -5
  192. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  193. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  194. package/packages/dd-trace/src/openfeature/index.js +6 -0
  195. package/packages/dd-trace/src/openfeature/writers/util.js +4 -4
  196. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  199. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  200. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +24 -24
  201. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  202. package/packages/dd-trace/src/otel-sampling.js +4 -5
  203. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  204. package/packages/dd-trace/src/plugin_manager.js +1 -2
  205. package/packages/dd-trace/src/plugins/database.js +7 -16
  206. package/packages/dd-trace/src/plugins/tracing.js +0 -1
  207. package/packages/dd-trace/src/plugins/util/git.js +99 -59
  208. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  209. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -1
  210. package/packages/dd-trace/src/plugins/util/test.js +50 -4
  211. package/packages/dd-trace/src/plugins/util/web.js +90 -20
  212. package/packages/dd-trace/src/priority_sampler.js +1 -2
  213. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  214. package/packages/dd-trace/src/proxy.js +1 -2
  215. package/packages/dd-trace/src/ritm.js +4 -2
  216. package/packages/dd-trace/src/serverless.js +5 -0
  217. package/packages/dd-trace/src/span_processor.js +13 -17
  218. package/packages/dd-trace/src/span_stats.js +4 -1
  219. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  220. package/packages/dd-trace/src/telemetry/logs/log-collector.js +1 -1
  221. package/packages/dd-trace/src/util.js +37 -4
  222. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  223. package/vendor/dist/@apm-js-collab/code-transformer/index.js +8 -8
  224. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  225. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  226. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  227. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  228. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  229. package/vendor/dist/protobufjs/index.js +1 -1
  230. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  231. package/vendor/dist/shell-quote/index.js +1 -1
@@ -58,9 +58,7 @@ function disable () {
58
58
  * @returns {'sample' | 'missing_route' | 'skip'}
59
59
  */
60
60
  function sampleRootSpanRequest (rootSpan, request, record = false) {
61
- if (!enabled) return SamplingDecision.SKIP
62
-
63
- if (!rootSpan) return SamplingDecision.SKIP
61
+ if (!enabled || !rootSpan) return SamplingDecision.SKIP
64
62
 
65
63
  const { method, statusCode, route, blocked = false } = request ?? {}
66
64
 
@@ -102,9 +100,7 @@ function sampleRequest (req, res, record = false) {
102
100
  if (!enabled) return SamplingDecision.SKIP
103
101
 
104
102
  const rootSpan = web.root(req)
105
- if (!rootSpan) return SamplingDecision.SKIP
106
-
107
- if (isRejected(rootSpan)) return SamplingDecision.SKIP
103
+ if (!rootSpan || isRejected(rootSpan)) return SamplingDecision.SKIP
108
104
 
109
105
  const statusCode = res.statusCode
110
106
  const route = getRouteOrEndpoint(web.getContext(req), statusCode)
@@ -163,15 +163,7 @@ function isRedirectResponse (res) {
163
163
  * @param {object} ctx http client instrumentation context (mutated).
164
164
  */
165
165
  function planResponseBodyCollection (originatingReq, res, ctx) {
166
- if (!config) {
167
- return
168
- }
169
-
170
- if (isRedirectResponse(res)) {
171
- return
172
- }
173
-
174
- if (!shouldSampleBody(originatingReq)) {
166
+ if (!config || isRedirectResponse(res) || !shouldSampleBody(originatingReq)) {
175
167
  return
176
168
  }
177
169
 
@@ -60,9 +60,7 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) {
60
60
  if (!cookies || typeof cookies !== 'object') return
61
61
 
62
62
  const rootSpan = web.root(req)
63
- if (!rootSpan) return
64
-
65
- if (isEmpty(cookies)) return
63
+ if (!rootSpan || isEmpty(cookies)) return
66
64
  analyzedCookies.add(cookies)
67
65
 
68
66
  const results = waf.run({
@@ -189,9 +187,7 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
189
187
  }
190
188
 
191
189
  const rootSpan = web.root(req)
192
- if (!rootSpan) return
193
-
194
- if (isEmpty(query)) return
190
+ if (!rootSpan || isEmpty(query)) return
195
191
 
196
192
  const results = waf.run({
197
193
  persistent: {
@@ -204,9 +200,7 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
204
200
 
205
201
  function onRequestProcessParams ({ req, res, abortController, params }) {
206
202
  const rootSpan = web.root(req)
207
- if (!rootSpan) return
208
-
209
- if (!params || typeof params !== 'object' || isEmpty(params)) return
203
+ if (!rootSpan || !params || typeof params !== 'object' || isEmpty(params)) return
210
204
 
211
205
  const results = waf.run({
212
206
  persistent: {
@@ -11,8 +11,13 @@ const { storedResponseHeaders, copyHeadersOmitting } = require('./http-shared')
11
11
  const responseAnalyzedSet = new WeakSet()
12
12
 
13
13
  function onResponseBody ({ req, res, body }) {
14
- if (!body || typeof body !== 'object') return
15
- if (apiSecurity.sampleRequest(req, res) !== apiSecurity.SamplingDecision.SAMPLE) return
14
+ if (
15
+ !body ||
16
+ typeof body !== 'object' ||
17
+ apiSecurity.sampleRequest(req, res) !== apiSecurity.SamplingDecision.SAMPLE
18
+ ) {
19
+ return
20
+ }
16
21
 
17
22
  // we don't support blocking at this point, so no results needed
18
23
  waf.run({
@@ -2,6 +2,7 @@
2
2
 
3
3
  const path = require('path')
4
4
 
5
+ const { APPSEC_FS_STORAGE } = require('../../rasp/fs-plugin')
5
6
  const { getIastContext } = require('../iast-context')
6
7
  const { storage } = require('../../../../../datadog-core')
7
8
  const { PATH_TRAVERSAL } = require('../vulnerabilities')
@@ -29,10 +30,10 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
29
30
 
30
31
  onConfigure () {
31
32
  this.addSub('apm:fs:operation:start', (obj) => {
32
- const store = storage('legacy').getStore()
33
- const outOfReqOrChild = !store?.fs?.root
33
+ const fs = storage(APPSEC_FS_STORAGE).getStore()
34
+ const outOfReqOrChild = !fs?.root
34
35
 
35
- // we could filter out all the nested fs.operations based on store.fs.root
36
+ // we could filter out all the nested fs.operations based on fs.root
36
37
  // but if we spect a store in the context to be present we are going to exclude
37
38
  // all out_of_the_request fs.operations
38
39
  // AppsecFsPlugin must be enabled
@@ -17,10 +17,7 @@ class SetCookiesHeaderInterceptor extends Plugin {
17
17
 
18
18
  _handleCookies (name, value, res) {
19
19
  if (name.toLowerCase() === 'set-cookie') {
20
- let allCookies = value
21
- if (typeof value === 'string') {
22
- allCookies = [value]
23
- }
20
+ const allCookies = typeof value === 'string' ? [value] : value
24
21
  const alreadyCheckedCookies = this._getAlreadyCheckedCookiesInResponse(res)
25
22
 
26
23
  let location
@@ -66,7 +66,8 @@ function getCallSiteFramesForLocation (callSiteFrames, externallyExcludedPaths)
66
66
 
67
67
  if (
68
68
  !isExcluded(callsite, externallyExcludedPaths) &&
69
- (!filepath.includes(pathLine.ddBasePath) || globalThis.__DD_ESBUILD_IAST_WITH_NO_SM)
69
+ (pathLine.ddBasePath === undefined || !filepath.includes(pathLine.ddBasePath) ||
70
+ globalThis.__DD_ESBUILD_IAST_WITH_NO_SM)
70
71
  ) {
71
72
  clientFrames.push(callsite)
72
73
  }
@@ -3,9 +3,11 @@
3
3
  const TaintedUtils = require('@datadog/native-iast-taint-tracking')
4
4
  const { IAST_TRANSACTION_ID } = require('../iast-context')
5
5
  const log = require('../../../log')
6
+ const { typedArrayLength } = require('../../../util')
6
7
  const { HTTP_REQUEST_PARAMETER } = require('./source-types')
7
8
 
8
- const SEPARATOR = '\u0000' // Unit Separator (cannot be in URL keys)
9
+ const SEPARATOR = '\0' // Unit Separator (cannot be in URL keys)
10
+ const MAX_TRAVERSED_BINARY_VIEW_LENGTH = 1024
9
11
 
10
12
  function taintObject (iastContext, object, type) {
11
13
  let result = object
@@ -31,6 +33,10 @@ function taintObject (iastContext, object, type) {
31
33
  } else if (typeof value === 'object' && !visited.has(value)) {
32
34
  visited.add(value)
33
35
 
36
+ // Binary view elements are numbers, but named properties may hold strings. Their keys cannot be listed
37
+ // without also materializing one key per element, so only small views are walked.
38
+ if (ArrayBuffer.isView(value) && typedArrayLength(value) > MAX_TRAVERSED_BINARY_VIEW_LENGTH) continue
39
+
34
40
  for (const key of Object.keys(value)) {
35
41
  queue.push({ parent: value, property: property ? `${property}.${key}` : key, value: value[key], key })
36
42
  }
@@ -264,9 +264,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
264
264
  }
265
265
 
266
266
  _taintDatabaseResult (result, dbOrigin, iastContext, name) {
267
- if (!iastContext) return result
268
-
269
- if (this._rowsToTaint === 0) return result
267
+ if (!iastContext || this._rowsToTaint === 0) return result
270
268
 
271
269
  if (Array.isArray(result)) {
272
270
  for (let i = 0; i < result.length && i < this._rowsToTaint; i++) {
@@ -31,9 +31,7 @@ export async function initialize (data) {
31
31
  export async function load (url, context, nextLoad) {
32
32
  const result = await nextLoad(url, context)
33
33
 
34
- if (!port) return result
35
- if (!result.source) return result
36
- if (url.includes(ddTraceDir) || url.includes('iitm=true')) return result
34
+ if (!port || !result.source || url.includes(ddTraceDir) || url.includes('iitm=true')) return result
37
35
 
38
36
  let passes
39
37
  try {
@@ -24,12 +24,7 @@ function formatTags (tags, tagKey) {
24
24
  }
25
25
 
26
26
  function getNamespace (scope, context) {
27
- let namespace = globalNamespace
28
-
29
- if (scope === Scope.REQUEST) {
30
- namespace = getNamespaceFromContext(context) || globalNamespace
31
- }
32
- return namespace
27
+ return scope === Scope.REQUEST ? getNamespaceFromContext(context) || globalNamespace : globalNamespace
33
28
  }
34
29
 
35
30
  class IastMetric {
@@ -169,9 +169,7 @@ function parseResponseBody (rawBody, headers, isBase64Encoded) {
169
169
  // A handler that answered with the payload itself needs no parsing, and no content type either.
170
170
  if (typeof rawBody === 'object') return rawBody
171
171
 
172
- if (typeof rawBody !== 'string') return
173
-
174
- if (!isJsonContentType(headers?.['content-type'])) return
172
+ if (typeof rawBody !== 'string' || !isJsonContentType(headers?.['content-type'])) return
175
173
 
176
174
  if (isOverSizeCap(rawBody, isBase64Encoded)) {
177
175
  log.debug('[ASM] Lambda response body larger than %d bytes, skipping schema extraction',
@@ -1,11 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const Plugin = require('../../plugins/plugin')
3
+ const dc = require('dc-polyfill')
4
+
4
5
  const { storage } = require('../../../../datadog-core')
5
6
  const log = require('../../log')
6
7
 
7
8
  const RASP_MODULE = 'rasp'
8
9
  const IAST_MODULE = 'iast'
10
+ const APPSEC_FS_STORAGE = 'appsec-fs'
9
11
 
10
12
  const enabledFor = {
11
13
  [RASP_MODULE]: false,
@@ -13,42 +15,53 @@ const enabledFor = {
13
15
  }
14
16
 
15
17
  let fsPlugin
16
-
17
- function getStoreToStart (fsProps, store = storage('legacy').getStore()) {
18
- if (store && !store.fs?.opExcluded) {
18
+ const legacyStorage = storage('legacy')
19
+ const appsecFsStorage = storage(APPSEC_FS_STORAGE)
20
+ const fsOperationStart = dc.channel('apm:fs:operation:start')
21
+ const fsOperationFinish = dc.channel('apm:fs:operation:finish')
22
+ const responseRenderStart = dc.channel('tracing:datadog:express:response:render:start')
23
+ const responseRenderEnd = dc.channel('tracing:datadog:express:response:render:end')
24
+
25
+ function getStoreToStart (fsProps, store = appsecFsStorage.getStore()) {
26
+ // Only track operations inside a traced context, like a request. Callbacks of fs streams and watchers keep
27
+ // the state of the operation that created them, so state created outside of one would leak into every request
28
+ // of a server started from such a callback. The tracing fs plugin enters a noop store when no span is active.
29
+ const legacyStore = legacyStorage.getStore()
30
+ if (!legacyStore || legacyStore.noop) return
31
+
32
+ if (!store || !store.opExcluded) {
19
33
  return {
20
34
  ...store,
21
- fs: {
22
- ...store.fs,
23
- ...fsProps,
24
- parentStore: store,
25
- },
35
+ ...fsProps,
36
+ parentStore: store,
26
37
  }
27
38
  }
28
39
 
29
40
  return store
30
41
  }
31
42
 
32
- class AppsecFsPlugin extends Plugin {
43
+ class AppsecFsPlugin {
33
44
  enable () {
34
- this.addBind('apm:fs:operation:start', this._onFsOperationStart)
35
- this.addBind('apm:fs:operation:finish', this._onFsOperationFinishOrRenderEnd)
36
- this.addBind('tracing:datadog:express:response:render:start', this._onResponseRenderStart)
37
- this.addBind('tracing:datadog:express:response:render:end', this._onFsOperationFinishOrRenderEnd)
38
- // We might have to add the same subscribers for fastify later
39
-
40
- super.configure(true)
45
+ // The tracing fs plugin binds the legacy store on these channels. AppSec
46
+ // keeps its fs-only state in a dedicated storage namespace to avoid
47
+ // clobbering tracing state while still tracking nested operations.
48
+ fsOperationStart.bindStore(appsecFsStorage, this._onFsOperationStart)
49
+ fsOperationFinish.bindStore(appsecFsStorage, this._onFsOperationFinishOrRenderEnd)
50
+ responseRenderStart.bindStore(appsecFsStorage, this._onResponseRenderStart)
51
+ responseRenderEnd.bindStore(appsecFsStorage, this._onFsOperationFinishOrRenderEnd)
41
52
  }
42
53
 
43
54
  disable () {
44
- super.configure(false)
55
+ fsOperationStart.unbindStore(appsecFsStorage)
56
+ fsOperationFinish.unbindStore(appsecFsStorage)
57
+ responseRenderStart.unbindStore(appsecFsStorage)
58
+ responseRenderEnd.unbindStore(appsecFsStorage)
45
59
  }
46
60
 
47
61
  _onFsOperationStart () {
48
- const store = storage('legacy').getStore()
49
- if (store) {
50
- return getStoreToStart({ root: store.fs?.root === undefined }, store)
51
- }
62
+ const store = appsecFsStorage.getStore()
63
+
64
+ return getStoreToStart({ root: store?.root === undefined }, store)
52
65
  }
53
66
 
54
67
  _onResponseRenderStart () {
@@ -56,11 +69,7 @@ class AppsecFsPlugin extends Plugin {
56
69
  }
57
70
 
58
71
  _onFsOperationFinishOrRenderEnd () {
59
- const store = storage('legacy').getStore()
60
- if (store?.fs) {
61
- return store.fs.parentStore
62
- }
63
- return store
72
+ return appsecFsStorage.getStore()?.parentStore
64
73
  }
65
74
  }
66
75
 
@@ -97,6 +106,7 @@ module.exports = {
97
106
  disable,
98
107
 
99
108
  AppsecFsPlugin,
109
+ APPSEC_FS_STORAGE,
100
110
 
101
111
  RASP_MODULE,
102
112
  IAST_MODULE,
@@ -6,9 +6,14 @@ const { fsOperationStart, incomingHttpRequestStart, expressResponseRenderStart }
6
6
  const { storage } = require('../../../../datadog-core')
7
7
  const web = require('../../plugins/util/web')
8
8
  const { FS_OPERATION_PATH } = require('../addresses')
9
- const { getRequest } = require('../store')
9
+ const { getActiveRequest } = require('../store')
10
10
  const waf = require('../waf')
11
- const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin')
11
+ const {
12
+ APPSEC_FS_STORAGE,
13
+ enable: enableFsPlugin,
14
+ disable: disableFsPlugin,
15
+ RASP_MODULE,
16
+ } = require('./fs-plugin')
12
17
  const { RULE_TYPES, handleResult } = require('./utils')
13
18
 
14
19
  let config
@@ -59,11 +64,10 @@ function analyzeLfiInResponseRender (ctx) {
59
64
  }
60
65
 
61
66
  function analyzeLfi (ctx) {
62
- const store = storage('legacy').getStore()
63
- const fs = store?.fs
67
+ const fs = storage(APPSEC_FS_STORAGE).getStore()
64
68
  if (!fs) return
65
69
 
66
- const req = getRequest(store)
70
+ const req = getActiveRequest()
67
71
  if (!req) return
68
72
 
69
73
  const res = web.getContext(req)?.res
@@ -8,7 +8,7 @@ const web = require('../plugins/util/web')
8
8
  const { ipHeaderList } = require('../plugins/util/ip_extractor')
9
9
  const { keepTrace } = require('../priority_sampler')
10
10
  const { ASM } = require('../standalone/product')
11
- const { isEmpty, truncateString } = require('../util')
11
+ const { isEmpty, truncateString, typedArrayLength } = require('../util')
12
12
  const { getActiveRequest } = require('./store')
13
13
  const {
14
14
  incrementWafInitMetric,
@@ -431,6 +431,20 @@ function reportAttack ({ events: attackData, actions }, req, rootSpan) {
431
431
  }
432
432
  }
433
433
 
434
+ /**
435
+ * Copies the first elements by index, the one typed array access that user code cannot override, unlike `subarray`,
436
+ * `toJSON` or their species constructors. The result keeps the shape `Buffer#toJSON` and `Object.keys` produce.
437
+ *
438
+ * @param {import('../util').BinaryView} view
439
+ */
440
+ function truncateTypedArray (view) {
441
+ const data = new Array(COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE)
442
+ for (let i = 0; i < COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE; i++) {
443
+ data[i] = view[i]
444
+ }
445
+ return Buffer.isBuffer(view) ? { type: 'Buffer', data } : { ...data }
446
+ }
447
+
434
448
  function truncateRequestBody (target, depth = 0) {
435
449
  switch (typeof target) {
436
450
  case 'string':
@@ -447,6 +461,12 @@ function truncateRequestBody (target, depth = 0) {
447
461
  return { truncated: true }
448
462
  }
449
463
 
464
+ // `Buffer#toJSON` and `Object.keys` both materialize one entry per byte, so cut large binary views down
465
+ // before they are serialized.
466
+ if (ArrayBuffer.isView(target) && typedArrayLength(target) > COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE) {
467
+ return { value: truncateTypedArray(target), truncated: true }
468
+ }
469
+
450
470
  if (typeof target.toJSON === 'function') {
451
471
  try {
452
472
  return truncateRequestBody(target.toJSON(), depth + 1)
@@ -34,8 +34,9 @@ function getCallSiteList (maxDepth = 100, constructorOpt) {
34
34
 
35
35
  function filterOutFramesFromLibrary (callSiteList) {
36
36
  return callSiteList.filter(callSite => {
37
- if (globalThis.__DD_ESBUILD_IAST_WITH_NO_SM) {
38
- // bundled and no SourceMap, not possible to discriminate if the frame comes from dd-trace code or not
37
+ if (globalThis.__DD_ESBUILD_IAST_WITH_NO_SM || ddBasePath === undefined) {
38
+ // bundled without a SourceMap, or dd-trace's location is unknown: not possible to discriminate if the frame
39
+ // comes from dd-trace code or not
39
40
  return true
40
41
  }
41
42
 
@@ -81,8 +82,7 @@ function getCallsiteFrames (maxDepth = 32, constructorOpt = getCallsiteFrames, c
81
82
  }
82
83
 
83
84
  function reportStackTrace (rootSpan, stackId, frames, namespace = STACK_TRACE_NAMESPACES.RASP) {
84
- if (!rootSpan) return
85
- if (!Array.isArray(frames)) return
85
+ if (!rootSpan || !Array.isArray(frames)) return
86
86
 
87
87
  if (!rootSpan.meta_struct) {
88
88
  rootSpan.meta_struct = {}
@@ -86,12 +86,9 @@ function getDynamicAtrBuckets (value) {
86
86
  * @param {number[] | null} customBuckets
87
87
  */
88
88
  function getDynamicAtrRetryCount (durationMs, efdRetryPolicy, customBuckets) {
89
- let retryCount
90
- if (customBuckets) {
91
- retryCount = customBuckets[retryBucketIndexForDuration(durationMs)]
92
- } else {
93
- retryCount = retriesForDuration(durationMs, efdRetryPolicy)
94
- }
89
+ const retryCount = customBuckets
90
+ ? customBuckets[retryBucketIndexForDuration(durationMs)]
91
+ : retriesForDuration(durationMs, efdRetryPolicy)
95
92
  return Math.max(1, retryCount)
96
93
  }
97
94
 
@@ -228,9 +228,10 @@ async function addBreakpoint (probe) {
228
228
  }
229
229
  }
230
230
 
231
- function start () {
231
+ async function start () {
232
+ await session.post('Debugger.enable')
233
+ // Only flag the session as started once enabled, so the next probe retries a failed start and reports its cause
232
234
  sessionStarted = true
233
- return session.post('Debugger.enable') // return instead of await to reduce number of promises created
234
235
  }
235
236
 
236
237
  function drainBreakpointHitRequests () {
@@ -224,7 +224,7 @@ class MsgpackJsonConverter {
224
224
  * @param {number} value decoded floating-point value
225
225
  */
226
226
  #writeFloat (value) {
227
- if (!Number.isFinite(value)) throw new Error('MessagePack validation payload contains a non-finite number.')
227
+ if (!Number.isFinite(value)) throw new TypeError('MessagePack validation payload contains a non-finite number.')
228
228
  this.#writer.write(Object.is(value, -0) ? '-0' : String(value))
229
229
  }
230
230
 
@@ -114,10 +114,14 @@ function isValidRetryCount (value) {
114
114
  * @param {unknown} retryPolicy - Candidate retry policy.
115
115
  */
116
116
  function isValidCachedEfdRetryPolicy (retryPolicy) {
117
- if (retryPolicy === null || typeof retryPolicy !== 'object' || Array.isArray(retryPolicy)) return false
118
- if (!isValidRetryCount(retryPolicy.schedulingRetryCount)) return false
119
- if (!Array.isArray(retryPolicy.durationRetryCounts) ||
120
- retryPolicy.durationRetryCounts.length !== EMPTY_EFD_RETRY_POLICY.durationRetryCounts.length) {
117
+ if (
118
+ retryPolicy === null ||
119
+ typeof retryPolicy !== 'object' ||
120
+ Array.isArray(retryPolicy) ||
121
+ !isValidRetryCount(retryPolicy.schedulingRetryCount) ||
122
+ !Array.isArray(retryPolicy.durationRetryCounts) ||
123
+ retryPolicy.durationRetryCounts.length !== EMPTY_EFD_RETRY_POLICY.durationRetryCounts.length
124
+ ) {
121
125
  return false
122
126
  }
123
127
 
@@ -152,9 +156,11 @@ function isValidCachedSettings (settings) {
152
156
  }
153
157
  if (settings.isDiEnabled && !settings.isFlakyTestRetriesEnabled) return false
154
158
  if (settings.isEarlyFlakeDetectionEnabled && !settings.isKnownTestsEnabled) return false
155
- if (!isValidCachedEfdRetryPolicy(settings.earlyFlakeDetectionRetryPolicy)) return false
156
- if (!isNonNegativeSafeInteger(settings.earlyFlakeDetectionFaultyThreshold) ||
157
- settings.earlyFlakeDetectionFaultyThreshold > 100) {
159
+ if (
160
+ !isValidCachedEfdRetryPolicy(settings.earlyFlakeDetectionRetryPolicy) ||
161
+ !isNonNegativeSafeInteger(settings.earlyFlakeDetectionFaultyThreshold) ||
162
+ settings.earlyFlakeDetectionFaultyThreshold > 100
163
+ ) {
158
164
  return false
159
165
  }
160
166
  return settings.testManagementAttemptToFixRetries === undefined ||
@@ -12,8 +12,7 @@ const getConfig = require('../../config')
12
12
  * @param {object} configuration - Request configuration for the settings endpoint.
13
13
  */
14
14
  function buildSettingsCacheKey (configuration) {
15
- const config = getConfig()
16
- const { testOptimization } = config
15
+ const { testOptimization } = getConfig()
17
16
  return buildCacheKey('settings', [
18
17
  tracerVersion,
19
18
  configuration.url?.href,
@@ -35,10 +35,7 @@ function getInterprocessTraceCode () {
35
35
  if (DD_PLAYWRIGHT_WORKER) {
36
36
  return PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE
37
37
  }
38
- if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
39
- return VITEST_WORKER_TRACE_PAYLOAD_CODE
40
- }
41
- if (DD_VITEST_WORKER) {
38
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || DD_VITEST_WORKER) {
42
39
  return VITEST_WORKER_TRACE_PAYLOAD_CODE
43
40
  }
44
41
  return null
@@ -62,10 +59,7 @@ function getInterprocessLogsCode () {
62
59
  if (getEnvironmentVariable('MOCHA_WORKER_ID') === 'webdriverio') {
63
60
  return MOCHA_WORKER_LOGS_PAYLOAD_CODE
64
61
  }
65
- if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
66
- return VITEST_WORKER_LOGS_PAYLOAD_CODE
67
- }
68
- if (getConfig().DD_VITEST_WORKER) {
62
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || getConfig().DD_VITEST_WORKER) {
69
63
  return VITEST_WORKER_LOGS_PAYLOAD_CODE
70
64
  }
71
65
  return null
@@ -0,0 +1,54 @@
1
+ 'use strict'
2
+
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ const log = require('../log')
7
+
8
+ /**
9
+ * @param {string} filePath
10
+ * @param {Record<string, string | undefined>} env
11
+ */
12
+ function resolveRunfilePath (filePath, env) {
13
+ if (fs.existsSync(filePath)) return filePath
14
+
15
+ const runfilesDir = env.RUNFILES_DIR
16
+ if (runfilesDir) {
17
+ const candidate = path.join(runfilesDir, filePath)
18
+ if (fs.existsSync(candidate)) return candidate
19
+ }
20
+
21
+ const manifestCandidate = resolveFromManifest(filePath, env.RUNFILES_MANIFEST_FILE)
22
+ if (manifestCandidate) return manifestCandidate
23
+
24
+ const testSrcdir = env.TEST_SRCDIR
25
+ if (testSrcdir) {
26
+ const candidate = path.join(testSrcdir, filePath)
27
+ if (fs.existsSync(candidate)) return candidate
28
+ }
29
+
30
+ return filePath
31
+ }
32
+
33
+ /**
34
+ * @param {string} filePath
35
+ * @param {string | undefined} runfilesManifest
36
+ */
37
+ function resolveFromManifest (filePath, runfilesManifest) {
38
+ if (!runfilesManifest || !fs.existsSync(runfilesManifest)) return
39
+
40
+ try {
41
+ const lines = fs.readFileSync(runfilesManifest, 'utf8').split('\n')
42
+ for (const line of lines) {
43
+ const separatorIndex = line.indexOf(' ')
44
+ if (separatorIndex <= 0 || line.slice(0, separatorIndex) !== filePath) continue
45
+
46
+ const resolvedPath = line.slice(separatorIndex + 1).trim()
47
+ if (resolvedPath) return resolvedPath
48
+ }
49
+ } catch (err) {
50
+ log.debug('Failed to resolve Test Optimization runfile from %s: %s', runfilesManifest, err.message)
51
+ }
52
+ }
53
+
54
+ module.exports = resolveRunfilePath
@@ -75,14 +75,12 @@ function validateSettingsResponse (response, options = {}) {
75
75
  assertBooleanFields(attributes.test_management, ['enabled'], 'settings test_management')
76
76
  assertOptionalBoundedNumber(
77
77
  attributes.early_flake_detection.faulty_session_threshold,
78
- 'settings early_flake_detection faulty_session_threshold',
79
- { integer: true }
78
+ 'settings early_flake_detection faulty_session_threshold'
80
79
  )
81
80
  assertRetryMap(attributes.early_flake_detection.slow_test_retries)
82
81
  assertOptionalBoundedNumber(
83
82
  attributes.test_management.attempt_to_fix_retries,
84
- 'settings test_management attempt_to_fix_retries',
85
- { integer: true }
83
+ 'settings test_management attempt_to_fix_retries'
86
84
  )
87
85
  }
88
86
 
@@ -214,10 +212,9 @@ function assertBooleanFields (object, keys, endpoint) {
214
212
  }
215
213
  }
216
214
 
217
- function assertOptionalBoundedNumber (value, endpoint, { integer = false } = {}) {
215
+ function assertOptionalBoundedNumber (value, endpoint) {
218
216
  if (value === undefined) return
219
- if (!Number.isFinite(value) || (integer && !Number.isInteger(value)) ||
220
- value < 0 || value > MAX_RETRIES) {
217
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > MAX_RETRIES) {
221
218
  throw new TypeError(`Invalid ${endpoint}: value must be between 0 and ${MAX_RETRIES}`)
222
219
  }
223
220
  }
@@ -230,7 +227,7 @@ function assertRetryMap (retries) {
230
227
  if (!RETRY_THRESHOLD_PATTERN.test(threshold)) {
231
228
  throw new TypeError('Invalid early flake detection retry threshold: expected a duration such as 5s')
232
229
  }
233
- assertOptionalBoundedNumber(count, `early flake detection retry count ${threshold}`, { integer: true })
230
+ assertOptionalBoundedNumber(count, `early flake detection retry count ${threshold}`)
234
231
  }
235
232
  }
236
233