dd-trace 6.8.0 → 6.10.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 (209) 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 +9 -3
  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/vitest.js +517 -0
  20. package/ci/test-optimization-validation/generated-files.js +38 -28
  21. package/ci/test-optimization-validation/generated-test-contract.js +8 -4
  22. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  23. package/ci/test-optimization-validation/literal-glob.js +52 -0
  24. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  25. package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
  26. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  27. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  28. package/ci/test-optimization-validation/package-check.js +94 -0
  29. package/ci/test-optimization-validation/plan-writer.js +183 -10
  30. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  31. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  32. package/ci/test-optimization-validation/redaction.js +18 -27
  33. package/ci/test-optimization-validation/report-writer.js +182 -48
  34. package/ci/test-optimization-validation/result-semantics.js +20 -3
  35. package/ci/test-optimization-validation/runner-command.js +40 -20
  36. package/ci/test-optimization-validation/runner-contract.js +79 -43
  37. package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
  38. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  39. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  40. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  41. package/ci/test-optimization-validation/test-output.js +2 -1
  42. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  43. package/ci/vitest-no-worker-init-setup.mjs +140 -42
  44. package/index.d.ts +140 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +8 -5
  49. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  50. package/packages/datadog-esbuild/src/utils.js +16 -1
  51. package/packages/datadog-instrumentations/src/ai.js +61 -34
  52. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  53. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
  54. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  55. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  56. package/packages/datadog-instrumentations/src/dns.js +21 -2
  57. package/packages/datadog-instrumentations/src/electron.js +1 -0
  58. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  59. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  60. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  61. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  62. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  69. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  70. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  71. package/packages/datadog-instrumentations/src/jest.js +980 -234
  72. package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
  73. package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
  74. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  75. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  76. package/packages/datadog-instrumentations/src/mysql.js +46 -16
  77. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  78. package/packages/datadog-instrumentations/src/next.js +155 -23
  79. package/packages/datadog-instrumentations/src/openai.js +14 -0
  80. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  81. package/packages/datadog-instrumentations/src/pg.js +209 -11
  82. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  83. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  84. package/packages/datadog-instrumentations/src/promise.js +3 -3
  85. package/packages/datadog-instrumentations/src/router.js +195 -19
  86. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
  87. package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
  88. package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
  89. package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
  90. package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
  91. package/packages/datadog-instrumentations/src/when.js +3 -3
  92. package/packages/datadog-instrumentations/src/ws.js +5 -1
  93. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  94. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  95. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
  96. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  97. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  98. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  99. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
  100. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  101. package/packages/datadog-plugin-electron/src/net.js +10 -4
  102. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  103. package/packages/datadog-plugin-http/src/client.js +1 -1
  104. package/packages/datadog-plugin-http2/src/client.js +1 -1
  105. package/packages/datadog-plugin-jest/src/index.js +6 -22
  106. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  107. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  108. package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
  109. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  110. package/packages/datadog-plugin-mocha/src/index.js +832 -4
  111. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  112. package/packages/datadog-plugin-next/src/index.js +51 -21
  113. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  114. package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
  115. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  116. package/packages/datadog-plugin-pg/src/index.js +66 -1
  117. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  118. package/packages/datadog-plugin-vitest/src/index.js +103 -27
  119. package/packages/datadog-plugin-ws/src/util.js +2 -1
  120. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  121. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  123. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  124. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  127. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  128. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  129. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  130. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  131. package/packages/dd-trace/src/appsec/index.js +48 -476
  132. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  133. package/packages/dd-trace/src/carrier.js +356 -0
  134. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  135. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  136. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  137. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  139. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  140. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  141. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  142. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  143. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  144. package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
  145. package/packages/dd-trace/src/config/helper.js +2 -0
  146. package/packages/dd-trace/src/config/index.js +16 -5
  147. package/packages/dd-trace/src/config/remote_config.js +1 -1
  148. package/packages/dd-trace/src/config/supported-configurations.json +18 -0
  149. package/packages/dd-trace/src/datastreams/context.js +6 -1
  150. package/packages/dd-trace/src/datastreams/manager.js +5 -1
  151. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  152. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  153. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  154. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  155. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  156. package/packages/dd-trace/src/guardrails/log.js +1 -10
  157. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  158. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  159. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  160. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  161. package/packages/dd-trace/src/llmobs/index.js +34 -2
  162. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  163. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  165. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  166. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  167. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  168. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  169. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  170. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  171. package/packages/dd-trace/src/llmobs/util.js +78 -0
  172. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  173. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  174. package/packages/dd-trace/src/log/channels.js +1 -9
  175. package/packages/dd-trace/src/log/levels.js +12 -0
  176. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  177. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  179. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  180. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  181. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  182. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  183. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  184. package/packages/dd-trace/src/plugin_manager.js +2 -0
  185. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  186. package/packages/dd-trace/src/plugins/index.js +1 -0
  187. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  188. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  189. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  190. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  191. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
  192. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  193. package/packages/dd-trace/src/plugins/util/test.js +397 -74
  194. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  195. package/packages/dd-trace/src/plugins/util/web.js +76 -3
  196. package/packages/dd-trace/src/profiler.js +1 -1
  197. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  198. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  199. package/packages/dd-trace/src/proxy.js +1 -0
  200. package/packages/dd-trace/src/ritm.js +5 -0
  201. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  202. package/packages/dd-trace/src/sampling_rule.js +4 -2
  203. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  204. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  205. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  206. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  207. package/vendor/dist/pprof-format/index.js +1 -1
  208. package/version.js +10 -8
  209. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -0,0 +1,228 @@
1
+ 'use strict'
2
+
3
+ const { HTTP_CLIENT_IP } = require('../../../../../ext/tags')
4
+ const log = require('../../log')
5
+ const web = require('../../plugins/util/web')
6
+ const { extractIp } = require('../../plugins/util/ip_extractor')
7
+ const { isEmpty } = require('../../util')
8
+ const addresses = require('../addresses')
9
+ const apiSecurity = require('../api_security')
10
+ const { normalizeRoute } = require('../api_security/normalized-route')
11
+ const { handleResults } = require('../blocking')
12
+ const Reporter = require('../reporter')
13
+ const { getActiveRequest } = require('../store')
14
+ const waf = require('../waf')
15
+ const { storedResponseHeaders, copyHeadersOmitting } = require('./http-shared')
16
+
17
+ const analyzedBodies = new WeakSet()
18
+ const analyzedCookies = new WeakSet()
19
+ const storedBodies = new WeakMap()
20
+
21
+ let config
22
+
23
+ /**
24
+ * @param {import('../../config/config-base')|undefined} _config
25
+ */
26
+ function setConfig (_config) {
27
+ config = _config
28
+ }
29
+
30
+ function onRequestBodyParsed ({ req, res, body, abortController }) {
31
+ if (body === undefined || body === null) return
32
+
33
+ if (!req) {
34
+ req = getActiveRequest()
35
+ }
36
+
37
+ const rootSpan = web.root(req)
38
+ if (!rootSpan) return
39
+
40
+ if (!req.body) {
41
+ // do not store body if it is in req.body
42
+ storedBodies.set(req, body)
43
+ }
44
+
45
+ if (typeof body === 'object') {
46
+ if (isEmpty(body)) return
47
+ analyzedBodies.add(body)
48
+ }
49
+
50
+ const results = waf.run({
51
+ persistent: {
52
+ [addresses.HTTP_INCOMING_BODY]: body,
53
+ },
54
+ }, req)
55
+
56
+ handleResults(results?.actions, req, res, rootSpan, abortController)
57
+ }
58
+
59
+ function onRequestCookieParser ({ req, res, abortController, cookies }) {
60
+ if (!cookies || typeof cookies !== 'object') return
61
+
62
+ const rootSpan = web.root(req)
63
+ if (!rootSpan) return
64
+
65
+ if (isEmpty(cookies)) return
66
+ analyzedCookies.add(cookies)
67
+
68
+ const results = waf.run({
69
+ persistent: {
70
+ [addresses.HTTP_INCOMING_COOKIES]: cookies,
71
+ },
72
+ }, req)
73
+
74
+ handleResults(results?.actions, req, res, rootSpan, abortController)
75
+ }
76
+
77
+ function incomingHttpStartTranslator ({ req, res, abortController }) {
78
+ const rootSpan = web.root(req)
79
+ if (!rootSpan) return
80
+
81
+ const clientIp = extractIp(config, req)
82
+
83
+ rootSpan.addTags({
84
+ '_dd.appsec.enabled': 1,
85
+ '_dd.runtime_family': 'nodejs',
86
+ [HTTP_CLIENT_IP]: clientIp,
87
+ })
88
+
89
+ if (config.inferredProxyServicesEnabled) {
90
+ const context = web.getContext(req)
91
+ if (context?.inferredProxySpan) {
92
+ context.inferredProxySpan.setTag('_dd.appsec.enabled', 1)
93
+ }
94
+ }
95
+
96
+ const persistent = {
97
+ [addresses.HTTP_INCOMING_URL]: req.url,
98
+ [addresses.HTTP_INCOMING_HEADERS]: copyHeadersOmitting(req.headers, 'cookie'),
99
+ [addresses.HTTP_INCOMING_METHOD]: req.method,
100
+ }
101
+
102
+ if (clientIp) {
103
+ persistent[addresses.HTTP_CLIENT_IP] = clientIp
104
+ }
105
+
106
+ const results = waf.run({ persistent }, req)
107
+
108
+ handleResults(results?.actions, req, res, rootSpan, abortController)
109
+ }
110
+
111
+ function incomingHttpEndTranslator ({ req, res }) {
112
+ const persistent = {}
113
+
114
+ // we need to keep this to support other body parsers
115
+ if (req.body !== undefined && req.body !== null) {
116
+ if (typeof req.body === 'object') {
117
+ if (!isEmpty(req.body) && !analyzedBodies.has(req.body)) {
118
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
119
+ }
120
+ } else {
121
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
122
+ }
123
+ }
124
+
125
+ // we need to keep this to support other cookie parsers
126
+ if (
127
+ req.cookies !== null &&
128
+ typeof req.cookies === 'object' &&
129
+ !isEmpty(req.cookies) &&
130
+ !analyzedCookies.has(req.cookies)
131
+ ) {
132
+ persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
133
+ }
134
+
135
+ // we need to keep this to support nextjs
136
+ const query = req.query
137
+ if (
138
+ query !== null &&
139
+ typeof query === 'object' &&
140
+ !isEmpty(query)
141
+ ) {
142
+ persistent[addresses.HTTP_INCOMING_QUERY] = query
143
+ }
144
+
145
+ // This hook runs before span finish, so ensure route/endpoint tags are available before API Security sampling runs.
146
+ web.setRouteOrEndpointTag(req)
147
+
148
+ if (config.appsec.DD_API_SECURITY_ENABLED) {
149
+ try {
150
+ const normalized = normalizeRoute(req)
151
+ if (normalized !== null) {
152
+ web.root(req)?.setTag('_dd.appsec.normalized_route', normalized)
153
+ }
154
+ } catch (e) {
155
+ log.debug('[ASM] Unable to compute normalized route: %s', e.message)
156
+ }
157
+ }
158
+
159
+ const apiSecSamplingDecision = apiSecurity.sampleRequest(req, res, true)
160
+ if (apiSecSamplingDecision === apiSecurity.SamplingDecision.SAMPLE) {
161
+ persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
162
+ }
163
+
164
+ let wafResult
165
+ if (!isEmpty(persistent)) {
166
+ wafResult = waf.run({ persistent }, req)
167
+ }
168
+
169
+ apiSecurity.reportRequest(req, apiSecSamplingDecision, wafResult)
170
+
171
+ waf.disposeContext(req)
172
+
173
+ const storedHeaders = storedResponseHeaders.get(req) || {}
174
+
175
+ const body = req.body || storedBodies.get(req)
176
+ Reporter.finishRequest(req, res, storedHeaders, body)
177
+
178
+ if (storedHeaders) {
179
+ storedResponseHeaders.delete(req)
180
+ }
181
+ storedBodies.delete(req)
182
+ }
183
+
184
+ function onRequestQueryParsed ({ req, res, query, abortController }) {
185
+ if (!query || typeof query !== 'object') return
186
+
187
+ if (!req) {
188
+ req = getActiveRequest()
189
+ }
190
+
191
+ const rootSpan = web.root(req)
192
+ if (!rootSpan) return
193
+
194
+ if (isEmpty(query)) return
195
+
196
+ const results = waf.run({
197
+ persistent: {
198
+ [addresses.HTTP_INCOMING_QUERY]: query,
199
+ },
200
+ }, req)
201
+
202
+ handleResults(results?.actions, req, res, rootSpan, abortController)
203
+ }
204
+
205
+ function onRequestProcessParams ({ req, res, abortController, params }) {
206
+ const rootSpan = web.root(req)
207
+ if (!rootSpan) return
208
+
209
+ if (!params || typeof params !== 'object' || isEmpty(params)) return
210
+
211
+ const results = waf.run({
212
+ persistent: {
213
+ [addresses.HTTP_INCOMING_PARAMS]: params,
214
+ },
215
+ }, req)
216
+
217
+ handleResults(results?.actions, req, res, rootSpan, abortController)
218
+ }
219
+
220
+ module.exports = {
221
+ setConfig,
222
+ onRequestBodyParsed,
223
+ onRequestCookieParser,
224
+ incomingHttpStartTranslator,
225
+ incomingHttpEndTranslator,
226
+ onRequestQueryParsed,
227
+ onRequestProcessParams,
228
+ }
@@ -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
+ }
@@ -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
  }
@@ -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