dd-trace 6.7.0 → 6.9.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 (272) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +14 -8
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
  20. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  21. package/ci/test-optimization-validation/generated-files.js +47 -37
  22. package/ci/test-optimization-validation/generated-test-contract.js +9 -7
  23. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  24. package/ci/test-optimization-validation/literal-glob.js +52 -0
  25. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  26. package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
  27. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  28. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  29. package/ci/test-optimization-validation/offline-output.js +6 -6
  30. package/ci/test-optimization-validation/package-check.js +94 -0
  31. package/ci/test-optimization-validation/plan-writer.js +183 -10
  32. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  33. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  34. package/ci/test-optimization-validation/redaction.js +18 -27
  35. package/ci/test-optimization-validation/report-writer.js +182 -48
  36. package/ci/test-optimization-validation/result-semantics.js +20 -3
  37. package/ci/test-optimization-validation/runner-command.js +41 -21
  38. package/ci/test-optimization-validation/runner-contract.js +79 -43
  39. package/ci/test-optimization-validation/safe-files.js +4 -4
  40. package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
  41. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  42. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  43. package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
  44. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  45. package/ci/test-optimization-validation/test-output.js +2 -1
  46. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  47. package/ci/vitest-no-worker-init-setup.mjs +377 -84
  48. package/index.d.ts +175 -14
  49. package/init.js +1 -17
  50. package/initialize.mjs +11 -0
  51. package/loader-hook.mjs +6 -4
  52. package/package.json +12 -4
  53. package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
  54. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  55. package/packages/datadog-esbuild/index.js +4 -2
  56. package/packages/datadog-esbuild/src/utils.js +17 -2
  57. package/packages/datadog-instrumentations/src/ai.js +61 -34
  58. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
  60. package/packages/datadog-instrumentations/src/cucumber.js +131 -51
  61. package/packages/datadog-instrumentations/src/dns.js +21 -2
  62. package/packages/datadog-instrumentations/src/electron.js +1 -0
  63. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  64. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  65. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  66. package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
  68. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  69. package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  76. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
  77. package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
  78. package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
  79. package/packages/datadog-instrumentations/src/http/client.js +1 -3
  80. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  81. package/packages/datadog-instrumentations/src/jest.js +986 -246
  82. package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
  83. package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
  84. package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
  85. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
  86. package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
  87. package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
  88. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  89. package/packages/datadog-instrumentations/src/next.js +155 -23
  90. package/packages/datadog-instrumentations/src/openai.js +14 -0
  91. package/packages/datadog-instrumentations/src/pg.js +180 -6
  92. package/packages/datadog-instrumentations/src/playwright.js +176 -81
  93. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  94. package/packages/datadog-instrumentations/src/promise.js +3 -3
  95. package/packages/datadog-instrumentations/src/rhea.js +1 -1
  96. package/packages/datadog-instrumentations/src/router.js +195 -19
  97. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
  98. package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
  99. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  100. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  101. package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
  102. package/packages/datadog-instrumentations/src/when.js +3 -3
  103. package/packages/datadog-instrumentations/src/ws.js +5 -1
  104. package/packages/datadog-plugin-aerospike/src/index.js +1 -3
  105. package/packages/datadog-plugin-amqplib/src/client.js +1 -1
  106. package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
  107. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  108. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  109. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
  110. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
  111. package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
  112. package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
  113. package/packages/datadog-plugin-child_process/src/index.js +3 -3
  114. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
  115. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  116. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  117. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
  118. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  119. package/packages/datadog-plugin-electron/src/net.js +10 -4
  120. package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
  121. package/packages/datadog-plugin-fs/src/index.js +0 -4
  122. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
  123. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
  124. package/packages/datadog-plugin-graphql/src/execute.js +1 -0
  125. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
  126. package/packages/datadog-plugin-http/src/client.js +3 -2
  127. package/packages/datadog-plugin-http2/src/client.js +1 -1
  128. package/packages/datadog-plugin-jest/src/index.js +3 -21
  129. package/packages/datadog-plugin-jest/src/util.js +4 -1
  130. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  131. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  132. package/packages/datadog-plugin-mocha/src/index.js +342 -6
  133. package/packages/datadog-plugin-next/src/index.js +57 -9
  134. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  135. package/packages/datadog-plugin-pg/src/index.js +65 -1
  136. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  137. package/packages/datadog-plugin-prisma/src/index.js +5 -1
  138. package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
  139. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  140. package/packages/datadog-plugin-undici/src/index.js +2 -1
  141. package/packages/datadog-plugin-vitest/src/index.js +179 -18
  142. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  143. package/packages/datadog-webpack/index.js +2 -2
  144. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  145. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  146. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
  147. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  148. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  149. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  150. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  151. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  152. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
  153. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  155. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
  157. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
  158. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
  159. package/packages/dd-trace/src/appsec/index.js +48 -476
  160. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  162. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  163. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  164. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
  165. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
  166. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
  169. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
  170. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  171. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  172. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
  173. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  174. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
  175. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  176. package/packages/dd-trace/src/config/defaults.js +14 -6
  177. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
  178. package/packages/dd-trace/src/config/helper.js +2 -0
  179. package/packages/dd-trace/src/config/index.js +31 -5
  180. package/packages/dd-trace/src/config/parsers.js +19 -1
  181. package/packages/dd-trace/src/config/remote_config.js +1 -1
  182. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  183. package/packages/dd-trace/src/datastreams/encoding.js +3 -4
  184. package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
  185. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
  186. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
  187. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
  188. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
  189. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  190. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  191. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  192. package/packages/dd-trace/src/exporters/common/url.js +3 -5
  193. package/packages/dd-trace/src/exporters/electron/index.js +4 -1
  194. package/packages/dd-trace/src/external-logger/src/index.js +1 -2
  195. package/packages/dd-trace/src/flare/index.js +2 -2
  196. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  197. package/packages/dd-trace/src/guardrails/log.js +1 -10
  198. package/packages/dd-trace/src/id.js +1 -1
  199. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
  200. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  201. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
  202. package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
  203. package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
  204. package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
  205. package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
  206. package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
  207. package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
  208. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  209. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  210. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  211. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
  212. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  213. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
  214. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  215. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  216. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  217. package/packages/dd-trace/src/llmobs/sdk.js +152 -78
  218. package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
  219. package/packages/dd-trace/src/llmobs/tagger.js +1 -2
  220. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  221. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  222. package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
  223. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  224. package/packages/dd-trace/src/log/channels.js +1 -9
  225. package/packages/dd-trace/src/log/levels.js +12 -0
  226. package/packages/dd-trace/src/noop/proxy.js +2 -2
  227. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  228. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  229. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  230. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  231. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  232. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  233. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  234. package/packages/dd-trace/src/opentracing/span.js +1 -1
  235. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  236. package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
  237. package/packages/dd-trace/src/plugins/index.js +2 -0
  238. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  239. package/packages/dd-trace/src/plugins/tracing.js +1 -0
  240. package/packages/dd-trace/src/plugins/util/ci.js +13 -6
  241. package/packages/dd-trace/src/plugins/util/git.js +7 -5
  242. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
  243. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  244. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  245. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  246. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  247. package/packages/dd-trace/src/plugins/util/test.js +356 -86
  248. package/packages/dd-trace/src/plugins/util/url.js +5 -5
  249. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  250. package/packages/dd-trace/src/plugins/util/web.js +24 -2
  251. package/packages/dd-trace/src/priority_sampler.js +6 -3
  252. package/packages/dd-trace/src/profiler.js +1 -1
  253. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  254. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  255. package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
  256. package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
  257. package/packages/dd-trace/src/proxy.js +1 -0
  258. package/packages/dd-trace/src/ritm.js +10 -20
  259. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  260. package/packages/dd-trace/src/sampling_rule.js +4 -2
  261. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  262. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  263. package/packages/dd-trace/src/startup-log.js +3 -0
  264. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  265. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
  266. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  267. package/packages/dd-trace/src/util.js +16 -0
  268. package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
  269. package/vendor/dist/pprof-format/index.js +1 -1
  270. package/vendor/dist/source-map/index.js +1 -1
  271. package/version.js +10 -8
  272. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -15,6 +15,10 @@ const skipMethodSize = skipMethods.size
15
15
 
16
16
  const nonConfigurableModuleExports = new WeakMap()
17
17
 
18
+ /**
19
+ * @typedef {NonNullable<ReturnType<typeof globalThis.Object.getOwnPropertyDescriptor>>} Descriptor
20
+ */
21
+
18
22
  // Reused descriptor scratch space for the `name` and `length` slots that
19
23
  // `copyProperties` and `wrapCallback` rewrite per wrap. `Object.defineProperty`
20
24
  // reads the descriptor's slots synchronously and does not retain the object,
@@ -46,7 +50,7 @@ function copyProperties (original, wrapped) {
46
50
  if (ownKeys.length !== 2) {
47
51
  for (const key of ownKeys) {
48
52
  if (skipMethods.has(key)) continue
49
- const descriptor = /** @type {PropertyDescriptor} */ (Object.getOwnPropertyDescriptor(original, key))
53
+ const descriptor = /** @type {Descriptor} */ (Object.getOwnPropertyDescriptor(original, key))
50
54
  if (descriptor.writable && descriptor.enumerable && descriptor.configurable) {
51
55
  wrapped[key] = original[key]
52
56
  } else if (descriptor.writable || descriptor.configurable || !Object.hasOwn(wrapped, key)) {
@@ -65,7 +69,7 @@ function copyObjectProperties (original, wrapped, skipKey) {
65
69
  const ownKeys = Reflect.ownKeys(original)
66
70
  for (const key of ownKeys) {
67
71
  if (key === skipKey) continue
68
- const descriptor = /** @type {PropertyDescriptor} */ (Object.getOwnPropertyDescriptor(original, key))
72
+ const descriptor = /** @type {Descriptor} */ (Object.getOwnPropertyDescriptor(original, key))
69
73
  if (descriptor.writable && descriptor.enumerable && descriptor.configurable) {
70
74
  wrapped[key] = original[key]
71
75
  } else if (descriptor.writable || descriptor.configurable || !Object.hasOwn(wrapped, key)) {
@@ -141,7 +141,7 @@ class DatadogWebpackPlugin {
141
141
  // (#8980); absent peers stay opaque, so a build that does not opt into the feature does
142
142
  // not follow their dependency chain (#8635).
143
143
  if (matchesOptionalPeerFile(normalizedResource)) {
144
- createData.loaders = createData.loaders || []
144
+ createData.loaders ||= []
145
145
  createData.loaders.push({ loader: require.resolve('./src/optional-peer-loader') })
146
146
  log.debug('INLINE: optional-peer loader applied to %s', normalizedResource)
147
147
  return
@@ -188,7 +188,7 @@ class DatadogWebpackPlugin {
188
188
  const version = packageJson.version
189
189
  const pkgPath = request === pkg ? pkg : `${pkg}/${modulePath}`
190
190
 
191
- createData.loaders = createData.loaders || []
191
+ createData.loaders ||= []
192
192
  createData.loaders.unshift({
193
193
  loader: require.resolve('./src/loader'),
194
194
  options: { pkg, version, path: pkgPath },
@@ -121,6 +121,9 @@ class AIGuard extends NoopAIGuard {
121
121
  *
122
122
  * - Clones each message so callers cannot mutate the data set in the meta struct.
123
123
  * - Truncates the list of messages and `content` fields emitting metrics accordingly.
124
+ *
125
+ * @param {import('../../../../index').aiguard.Message[]} messages
126
+ * @param {{ source: string, integration: string }} telemetryTags
124
127
  */
125
128
  #buildMessagesForMetaStruct (messages, telemetryTags) {
126
129
  const size = Math.min(messages.length, this.#maxMessagesLength)
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { LRUCache } = require('../../../../vendor/dist/lru-cache')
4
- const log = require('../log')
5
- const web = require('../plugins/util/web')
6
- const blockedTemplates = require('./blocked_templates')
7
- const { updateBlockFailureMetric } = require('./telemetry')
3
+ const { LRUCache } = require('../../../../../vendor/dist/lru-cache')
4
+ const log = require('../../log')
5
+ const web = require('../../plugins/util/web')
6
+ const { updateBlockFailureMetric } = require('../telemetry')
7
+ const blockedTemplates = require('./templates')
8
8
 
9
9
  // Bounded by the LRU as defense-in-depth: getSpecificKey already keys on the
10
10
  // resolved route (or the path with the query string stripped) so cardinality
@@ -182,7 +182,26 @@ function getBlockingAction (actions) {
182
182
  }
183
183
 
184
184
  /**
185
- * @param {import('../config/config-base')} [config] - Tracer configuration
185
+ * Turns a WAF result's actions into a blocking response, when one is requested.
186
+ * Single seam from WAF output to {@link block}, shared by every request/response/auth handler.
187
+ *
188
+ * @param {Record<string, unknown>|undefined} actions WAF result actions.
189
+ * @param {import('http').IncomingMessage} req
190
+ * @param {import('http').ServerResponse} res
191
+ * @param {object} rootSpan
192
+ * @param {AbortController} abortController
193
+ */
194
+ function handleResults (actions, req, res, rootSpan, abortController) {
195
+ if (!actions || !req || !res || !rootSpan || !abortController) return
196
+
197
+ const blockingAction = getBlockingAction(actions)
198
+ if (blockingAction) {
199
+ block(req, res, rootSpan, abortController, blockingAction)
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @param {import('../../config/config-base')} [config] - Tracer configuration
186
205
  */
187
206
  function setTemplates (config) {
188
207
  templates.html.body = config?.appsec?.blockedTemplateHtml
@@ -233,6 +252,7 @@ module.exports = {
233
252
  specificBlockingTypes,
234
253
  getBlockingData,
235
254
  getBlockingAction,
255
+ handleResults,
236
256
  setTemplates,
237
257
  isBlocked,
238
258
  setDefaultBlockingActionParameters,
@@ -307,7 +307,7 @@ function parseBody (body, contentType) {
307
307
  const formBody = Buffer.isBuffer(body) ? body.toString('utf8') : String(body)
308
308
  const params = new URLSearchParams(formBody)
309
309
  const result = {}
310
- for (const [key, value] of params.entries()) {
310
+ for (const [key, value] of params) {
311
311
  if (key in result) {
312
312
  const existing = result[key]
313
313
  if (Array.isArray(existing)) {
@@ -0,0 +1,62 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../log')
4
+ const web = require('../../plugins/util/web')
5
+ const addresses = require('../addresses')
6
+ const { handleResults } = require('../blocking')
7
+ const { getActiveRequest } = require('../store')
8
+ const UserTracking = require('../user_tracking')
9
+ const waf = require('../waf')
10
+
11
+ function onPassportVerify ({ framework, login, user, success, abortController }) {
12
+ const req = getActiveRequest()
13
+ const rootSpan = req && web.root(req)
14
+
15
+ if (!rootSpan) {
16
+ log.warn('[ASM] No rootSpan found in onPassportVerify')
17
+ return
18
+ }
19
+
20
+ const results = UserTracking.trackLogin(framework, login, user, success, rootSpan)
21
+
22
+ handleResults(results?.actions, req, web.getContext(req)?.res, rootSpan, abortController)
23
+ }
24
+
25
+ function onPassportDeserializeUser ({ user, abortController }) {
26
+ const req = getActiveRequest()
27
+ const rootSpan = req && web.root(req)
28
+
29
+ if (!rootSpan) {
30
+ log.warn('[ASM] No rootSpan found in onPassportDeserializeUser')
31
+ return
32
+ }
33
+
34
+ const results = UserTracking.trackUser(user, rootSpan)
35
+
36
+ handleResults(results?.actions, req, web.getContext(req)?.res, rootSpan, abortController)
37
+ }
38
+
39
+ function onExpressSession ({ req, res, sessionId, abortController }) {
40
+ const rootSpan = web.root(req)
41
+ if (!rootSpan) {
42
+ log.warn('[ASM] No rootSpan found in onExpressSession')
43
+ return
44
+ }
45
+
46
+ const isSdkCalled = rootSpan.context().getTag('usr.session_id')
47
+ if (isSdkCalled) return
48
+
49
+ const results = waf.run({
50
+ persistent: {
51
+ [addresses.USER_SESSION_ID]: sessionId,
52
+ },
53
+ }, req)
54
+
55
+ handleResults(results?.actions, req, res, rootSpan, abortController)
56
+ }
57
+
58
+ module.exports = {
59
+ onPassportVerify,
60
+ onPassportDeserializeUser,
61
+ onExpressSession,
62
+ }
@@ -0,0 +1,215 @@
1
+ 'use strict'
2
+
3
+ const { HTTP_CLIENT_IP } = require('../../../../../ext/tags')
4
+ const web = require('../../plugins/util/web')
5
+ const { extractIp } = require('../../plugins/util/ip_extractor')
6
+ const { isEmpty } = require('../../util')
7
+ const addresses = require('../addresses')
8
+ const apiSecurity = require('../api_security')
9
+ const { handleResults } = require('../blocking')
10
+ const Reporter = require('../reporter')
11
+ const { getActiveRequest } = require('../store')
12
+ const waf = require('../waf')
13
+ const { storedResponseHeaders, copyHeadersOmitting } = require('./http-shared')
14
+
15
+ const analyzedBodies = new WeakSet()
16
+ const analyzedCookies = new WeakSet()
17
+ const storedBodies = new WeakMap()
18
+
19
+ let config
20
+
21
+ /**
22
+ * @param {import('../../config/config-base')|undefined} _config
23
+ */
24
+ function setConfig (_config) {
25
+ config = _config
26
+ }
27
+
28
+ function onRequestBodyParsed ({ req, res, body, abortController }) {
29
+ if (body === undefined || body === null) return
30
+
31
+ if (!req) {
32
+ req = getActiveRequest()
33
+ }
34
+
35
+ const rootSpan = web.root(req)
36
+ if (!rootSpan) return
37
+
38
+ if (!req.body) {
39
+ // do not store body if it is in req.body
40
+ storedBodies.set(req, body)
41
+ }
42
+
43
+ if (typeof body === 'object') {
44
+ if (isEmpty(body)) return
45
+ analyzedBodies.add(body)
46
+ }
47
+
48
+ const results = waf.run({
49
+ persistent: {
50
+ [addresses.HTTP_INCOMING_BODY]: body,
51
+ },
52
+ }, req)
53
+
54
+ handleResults(results?.actions, req, res, rootSpan, abortController)
55
+ }
56
+
57
+ function onRequestCookieParser ({ req, res, abortController, cookies }) {
58
+ if (!cookies || typeof cookies !== 'object') return
59
+
60
+ const rootSpan = web.root(req)
61
+ if (!rootSpan) return
62
+
63
+ if (isEmpty(cookies)) return
64
+ analyzedCookies.add(cookies)
65
+
66
+ const results = waf.run({
67
+ persistent: {
68
+ [addresses.HTTP_INCOMING_COOKIES]: cookies,
69
+ },
70
+ }, req)
71
+
72
+ handleResults(results?.actions, req, res, rootSpan, abortController)
73
+ }
74
+
75
+ function incomingHttpStartTranslator ({ req, res, abortController }) {
76
+ const rootSpan = web.root(req)
77
+ if (!rootSpan) return
78
+
79
+ const clientIp = extractIp(config, req)
80
+
81
+ rootSpan.addTags({
82
+ '_dd.appsec.enabled': 1,
83
+ '_dd.runtime_family': 'nodejs',
84
+ [HTTP_CLIENT_IP]: clientIp,
85
+ })
86
+
87
+ if (config.inferredProxyServicesEnabled) {
88
+ const context = web.getContext(req)
89
+ if (context?.inferredProxySpan) {
90
+ context.inferredProxySpan.setTag('_dd.appsec.enabled', 1)
91
+ }
92
+ }
93
+
94
+ const persistent = {
95
+ [addresses.HTTP_INCOMING_URL]: req.url,
96
+ [addresses.HTTP_INCOMING_HEADERS]: copyHeadersOmitting(req.headers, 'cookie'),
97
+ [addresses.HTTP_INCOMING_METHOD]: req.method,
98
+ }
99
+
100
+ if (clientIp) {
101
+ persistent[addresses.HTTP_CLIENT_IP] = clientIp
102
+ }
103
+
104
+ const results = waf.run({ persistent }, req)
105
+
106
+ handleResults(results?.actions, req, res, rootSpan, abortController)
107
+ }
108
+
109
+ function incomingHttpEndTranslator ({ req, res }) {
110
+ const persistent = {}
111
+
112
+ // we need to keep this to support other body parsers
113
+ if (req.body !== undefined && req.body !== null) {
114
+ if (typeof req.body === 'object') {
115
+ if (!isEmpty(req.body) && !analyzedBodies.has(req.body)) {
116
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
117
+ }
118
+ } else {
119
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
120
+ }
121
+ }
122
+
123
+ // we need to keep this to support other cookie parsers
124
+ if (
125
+ req.cookies !== null &&
126
+ typeof req.cookies === 'object' &&
127
+ !isEmpty(req.cookies) &&
128
+ !analyzedCookies.has(req.cookies)
129
+ ) {
130
+ persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
131
+ }
132
+
133
+ // we need to keep this to support nextjs
134
+ const query = req.query
135
+ if (
136
+ query !== null &&
137
+ typeof query === 'object' &&
138
+ !isEmpty(query)
139
+ ) {
140
+ persistent[addresses.HTTP_INCOMING_QUERY] = query
141
+ }
142
+
143
+ // This hook runs before span finish, so ensure route/endpoint tags are available before API Security sampling runs.
144
+ web.setRouteOrEndpointTag(req)
145
+
146
+ const apiSecSamplingDecision = apiSecurity.sampleRequest(req, res, true)
147
+ if (apiSecSamplingDecision === apiSecurity.SamplingDecision.SAMPLE) {
148
+ persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
149
+ }
150
+
151
+ let wafResult
152
+ if (!isEmpty(persistent)) {
153
+ wafResult = waf.run({ persistent }, req)
154
+ }
155
+
156
+ apiSecurity.reportRequest(req, apiSecSamplingDecision, wafResult)
157
+
158
+ waf.disposeContext(req)
159
+
160
+ const storedHeaders = storedResponseHeaders.get(req) || {}
161
+
162
+ const body = req.body || storedBodies.get(req)
163
+ Reporter.finishRequest(req, res, storedHeaders, body)
164
+
165
+ if (storedHeaders) {
166
+ storedResponseHeaders.delete(req)
167
+ }
168
+ storedBodies.delete(req)
169
+ }
170
+
171
+ function onRequestQueryParsed ({ req, res, query, abortController }) {
172
+ if (!query || typeof query !== 'object') return
173
+
174
+ if (!req) {
175
+ req = getActiveRequest()
176
+ }
177
+
178
+ const rootSpan = web.root(req)
179
+ if (!rootSpan) return
180
+
181
+ if (isEmpty(query)) return
182
+
183
+ const results = waf.run({
184
+ persistent: {
185
+ [addresses.HTTP_INCOMING_QUERY]: query,
186
+ },
187
+ }, req)
188
+
189
+ handleResults(results?.actions, req, res, rootSpan, abortController)
190
+ }
191
+
192
+ function onRequestProcessParams ({ req, res, abortController, params }) {
193
+ const rootSpan = web.root(req)
194
+ if (!rootSpan) return
195
+
196
+ if (!params || typeof params !== 'object' || isEmpty(params)) return
197
+
198
+ const results = waf.run({
199
+ persistent: {
200
+ [addresses.HTTP_INCOMING_PARAMS]: params,
201
+ },
202
+ }, req)
203
+
204
+ handleResults(results?.actions, req, res, rootSpan, abortController)
205
+ }
206
+
207
+ module.exports = {
208
+ setConfig,
209
+ onRequestBodyParsed,
210
+ onRequestCookieParser,
211
+ incomingHttpStartTranslator,
212
+ incomingHttpEndTranslator,
213
+ onRequestQueryParsed,
214
+ onRequestProcessParams,
215
+ }
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ const web = require('../../plugins/util/web')
4
+ const { isEmpty } = require('../../util')
5
+ const addresses = require('../addresses')
6
+ const apiSecurity = require('../api_security')
7
+ const { isBlocked, callBlockDelegation, handleResults } = require('../blocking')
8
+ const waf = require('../waf')
9
+ const { storedResponseHeaders, copyHeadersOmitting } = require('./http-shared')
10
+
11
+ const responseAnalyzedSet = new WeakSet()
12
+
13
+ function onResponseBody ({ req, res, body }) {
14
+ if (!body || typeof body !== 'object') return
15
+ if (apiSecurity.sampleRequest(req, res) !== apiSecurity.SamplingDecision.SAMPLE) return
16
+
17
+ // we don't support blocking at this point, so no results needed
18
+ waf.run({
19
+ persistent: {
20
+ [addresses.HTTP_INCOMING_RESPONSE_BODY]: body,
21
+ },
22
+ }, req)
23
+ }
24
+
25
+ function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
26
+ // Normalize header names to lowercase so downstream consumers see the same shape
27
+ // regardless of how the caller wrote them.
28
+ const normalizedResponseHeaders = {}
29
+ for (const [key, value] of Object.entries(responseHeaders)) {
30
+ normalizedResponseHeaders[key.toLowerCase()] = value
31
+ }
32
+
33
+ if (!isEmpty(normalizedResponseHeaders)) {
34
+ storedResponseHeaders.set(req, normalizedResponseHeaders)
35
+ }
36
+
37
+ // TODO: do not call waf if inside block()
38
+ // if (isBlocking()) {
39
+ // return
40
+ // }
41
+
42
+ // avoid "write after end" error
43
+ if (isBlocked(res) || callBlockDelegation(res)) {
44
+ abortController?.abort()
45
+ return
46
+ }
47
+
48
+ // avoid double waf call
49
+ if (responseAnalyzedSet.has(res)) {
50
+ return
51
+ }
52
+
53
+ const rootSpan = web.root(req)
54
+ if (!rootSpan) return
55
+
56
+ const results = waf.run({
57
+ persistent: {
58
+ [addresses.HTTP_INCOMING_RESPONSE_CODE]: String(statusCode),
59
+ [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: copyHeadersOmitting(normalizedResponseHeaders, 'set-cookie'),
60
+ },
61
+ }, req)
62
+
63
+ responseAnalyzedSet.add(res)
64
+
65
+ handleResults(results?.actions, req, res, rootSpan, abortController)
66
+ }
67
+
68
+ function onResponseOperation ({ res, abortController }) {
69
+ if (isBlocked(res)) {
70
+ abortController?.abort()
71
+ }
72
+ }
73
+
74
+ module.exports = {
75
+ onResponseBody,
76
+ onResponseWriteHead,
77
+ onResponseOperation,
78
+ }
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+
3
+ // Response headers captured on `responseWriteHead` (http-response) and later read
4
+ // when the request finishes (http-request end translator). This is the only piece
5
+ // of per-request state shared across the two handler modules.
6
+ const storedResponseHeaders = new WeakMap()
7
+
8
+ /**
9
+ * @param {Record<string, unknown>} src
10
+ * @param {string} omit
11
+ * @returns {Record<string, unknown>}
12
+ */
13
+ function copyHeadersOmitting (src, omit) {
14
+ const filtered = {}
15
+ for (const key of Object.keys(src)) {
16
+ if (key !== omit) filtered[key] = src[key]
17
+ }
18
+ return filtered
19
+ }
20
+
21
+ module.exports = {
22
+ storedResponseHeaders,
23
+ copyHeadersOmitting,
24
+ }
@@ -0,0 +1,104 @@
1
+ 'use strict'
2
+
3
+ const addresses = require('../addresses')
4
+ const waf = require('../waf')
5
+
6
+ function onStripeCheckoutSessionCreate (payload) {
7
+ if (payload?.mode !== 'payment') return
8
+
9
+ waf.run({
10
+ persistent: {
11
+ [addresses.PAYMENT_CREATION]: {
12
+ integration: 'stripe',
13
+ id: payload.id,
14
+ amount_total: payload.amount_total,
15
+ client_reference_id: payload.client_reference_id,
16
+ currency: payload.currency,
17
+ 'discounts.coupon': payload.discounts?.[0]?.coupon,
18
+ 'discounts.promotion_code': payload.discounts?.[0]?.promotion_code,
19
+ livemode: payload.livemode,
20
+ 'total_details.amount_discount': payload.total_details?.amount_discount,
21
+ 'total_details.amount_shipping': payload.total_details?.amount_shipping,
22
+ },
23
+ },
24
+ })
25
+ }
26
+
27
+ function onStripePaymentIntentCreate (payload) {
28
+ if (payload === null || typeof payload !== 'object') return
29
+
30
+ waf.run({
31
+ persistent: {
32
+ [addresses.PAYMENT_CREATION]: {
33
+ integration: 'stripe',
34
+ id: payload.id,
35
+ amount: payload.amount,
36
+ currency: payload.currency,
37
+ livemode: payload.livemode,
38
+ payment_method: payload.payment_method,
39
+ },
40
+ },
41
+ })
42
+ }
43
+
44
+ function onStripeConstructEvent (payload) {
45
+ const object = payload?.data?.object
46
+ if (object === null || typeof object !== 'object') return
47
+
48
+ let persistent
49
+
50
+ switch (payload.type) {
51
+ case 'payment_intent.succeeded':
52
+ persistent = {
53
+ [addresses.PAYMENT_SUCCESS]: {
54
+ integration: 'stripe',
55
+ id: object.id,
56
+ amount: object.amount,
57
+ currency: object.currency,
58
+ livemode: object.livemode,
59
+ payment_method: object.payment_method,
60
+ },
61
+ }
62
+ break
63
+
64
+ case 'payment_intent.payment_failed':
65
+ persistent = {
66
+ [addresses.PAYMENT_FAILURE]: {
67
+ integration: 'stripe',
68
+ id: object.id,
69
+ amount: object.amount,
70
+ currency: object.currency,
71
+ 'last_payment_error.code': object.last_payment_error?.code,
72
+ 'last_payment_error.decline_code': object.last_payment_error?.decline_code,
73
+ 'last_payment_error.payment_method.id': object.last_payment_error?.payment_method?.id,
74
+ 'last_payment_error.payment_method.type': object.last_payment_error?.payment_method?.type,
75
+ livemode: object.livemode,
76
+ },
77
+ }
78
+ break
79
+
80
+ case 'payment_intent.canceled':
81
+ persistent = {
82
+ [addresses.PAYMENT_CANCELLATION]: {
83
+ integration: 'stripe',
84
+ id: object.id,
85
+ amount: object.amount,
86
+ cancellation_reason: object.cancellation_reason,
87
+ currency: object.currency,
88
+ livemode: object.livemode,
89
+ },
90
+ }
91
+ break
92
+
93
+ default:
94
+ return
95
+ }
96
+
97
+ waf.run({ persistent })
98
+ }
99
+
100
+ module.exports = {
101
+ onStripeCheckoutSessionCreate,
102
+ onStripePaymentIntentCreate,
103
+ onStripeConstructEvent,
104
+ }
@@ -56,7 +56,7 @@ class HardcodedBaseAnalyzer extends Analyzer {
56
56
  }
57
57
 
58
58
  _getEvidence (value) {
59
- return { value: `${value.data}` }
59
+ return { value: value.data }
60
60
  }
61
61
 
62
62
  _getLocation (value) {
@@ -11,7 +11,7 @@ class HardcodedPasswordAnalyzer extends HardcodedBaseAnalyzer {
11
11
  }
12
12
 
13
13
  _getEvidence (value) {
14
- return { value: `${value.ident}` }
14
+ return { value: value.ident }
15
15
  }
16
16
  }
17
17
 
@@ -31,7 +31,7 @@ class HstsHeaderMissingAnalyzer extends MissingHeaderAnalyzer {
31
31
  semicolonIndex === -1 ? headerValue.length : semicolonIndex
32
32
  )
33
33
 
34
- const timestamp = Number.parseInt(timestampString)
34
+ const timestamp = Number.parseInt(timestampString, 10)
35
35
  // eslint-disable-next-line eqeqeq
36
36
  return timestamp > 0 && timestamp == timestampString
37
37
  }
@@ -74,7 +74,7 @@ class SqlInjectionAnalyzer extends StoredInjectionAnalyzer {
74
74
  }
75
75
 
76
76
  analyze (value, store, dialect) {
77
- store = store || storage('legacy').getStore()
77
+ store ||= storage('legacy').getStore()
78
78
  if (!(store && store.sqlAnalyzed)) {
79
79
  super.analyze(value, store, dialect)
80
80
  }
@@ -215,9 +215,7 @@ function getTaintTrackingImpl (telemetryVerbosity, dummy = false) {
215
215
  if (dummy) return TaintTrackingNoop
216
216
 
217
217
  // with Verbosity.DEBUG every invocation of a TaintedUtils method increases the EXECUTED_PROPAGATION metric
218
- return isDebugAllowed(telemetryVerbosity)
219
- ? createImplWith(getContextDebug)
220
- : createImplWith(getContextDefault)
218
+ return createImplWith(isDebugAllowed(telemetryVerbosity) ? getContextDebug : getContextDefault)
221
219
  }
222
220
 
223
221
  function getTaintTrackingNoop () {
@@ -35,7 +35,7 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
35
35
  let value
36
36
  const ranges = []
37
37
  const sensitiveRanges = []
38
- objRanges = objRanges || {}
38
+ objRanges ||= {}
39
39
 
40
40
  if (objRanges || loadSensitiveRanges) {
41
41
  const cloneObj = Array.isArray(obj) ? [] : {}
@@ -150,7 +150,7 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
150
150
 
151
151
  sensitiveRanges.push({
152
152
  start,
153
- end: start + Number.parseInt(regexRes[1]),
153
+ end: start + Number.parseInt(regexRes[1], 10),
154
154
  })
155
155
  segments.push(value.slice(pos, rangeKeyIndex))
156
156
  outputLength += cleanLength