dd-trace 5.101.0 → 5.102.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 (140) hide show
  1. package/package.json +9 -7
  2. package/packages/datadog-instrumentations/src/aerospike.js +2 -2
  3. package/packages/datadog-instrumentations/src/ai.js +8 -8
  4. package/packages/datadog-instrumentations/src/amqplib.js +6 -7
  5. package/packages/datadog-instrumentations/src/anthropic.js +10 -10
  6. package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
  7. package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
  8. package/packages/datadog-instrumentations/src/avsc.js +6 -6
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
  10. package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
  11. package/packages/datadog-instrumentations/src/bluebird.js +2 -2
  12. package/packages/datadog-instrumentations/src/body-parser.js +2 -2
  13. package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
  14. package/packages/datadog-instrumentations/src/child_process.js +12 -12
  15. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +9 -9
  16. package/packages/datadog-instrumentations/src/connect.js +7 -7
  17. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  18. package/packages/datadog-instrumentations/src/cookie.js +2 -2
  19. package/packages/datadog-instrumentations/src/couchbase.js +16 -30
  20. package/packages/datadog-instrumentations/src/crypto.js +4 -4
  21. package/packages/datadog-instrumentations/src/cucumber.js +77 -16
  22. package/packages/datadog-instrumentations/src/dns.js +0 -3
  23. package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
  24. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
  25. package/packages/datadog-instrumentations/src/express-session.js +4 -4
  26. package/packages/datadog-instrumentations/src/express.js +10 -11
  27. package/packages/datadog-instrumentations/src/fastify.js +2 -2
  28. package/packages/datadog-instrumentations/src/fs.js +14 -14
  29. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
  30. package/packages/datadog-instrumentations/src/google-genai.js +4 -4
  31. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  32. package/packages/datadog-instrumentations/src/hapi.js +2 -2
  33. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +8 -8
  34. package/packages/datadog-instrumentations/src/helpers/promise.js +2 -2
  35. package/packages/datadog-instrumentations/src/hono.js +2 -2
  36. package/packages/datadog-instrumentations/src/http/client.js +6 -6
  37. package/packages/datadog-instrumentations/src/http/server.js +9 -9
  38. package/packages/datadog-instrumentations/src/jest.js +31 -31
  39. package/packages/datadog-instrumentations/src/kafkajs.js +9 -9
  40. package/packages/datadog-instrumentations/src/knex.js +17 -17
  41. package/packages/datadog-instrumentations/src/koa.js +12 -12
  42. package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
  43. package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
  44. package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
  45. package/packages/datadog-instrumentations/src/lodash.js +4 -4
  46. package/packages/datadog-instrumentations/src/mariadb.js +13 -13
  47. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  48. package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
  49. package/packages/datadog-instrumentations/src/mocha/common.js +3 -3
  50. package/packages/datadog-instrumentations/src/mocha/main.js +12 -10
  51. package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
  52. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
  53. package/packages/datadog-instrumentations/src/mongodb-core.js +9 -22
  54. package/packages/datadog-instrumentations/src/mongodb.js +5 -5
  55. package/packages/datadog-instrumentations/src/mongoose.js +21 -21
  56. package/packages/datadog-instrumentations/src/mquery.js +5 -5
  57. package/packages/datadog-instrumentations/src/multer.js +4 -4
  58. package/packages/datadog-instrumentations/src/mysql.js +16 -16
  59. package/packages/datadog-instrumentations/src/mysql2.js +4 -4
  60. package/packages/datadog-instrumentations/src/net.js +14 -8
  61. package/packages/datadog-instrumentations/src/nyc.js +5 -5
  62. package/packages/datadog-instrumentations/src/openai.js +19 -19
  63. package/packages/datadog-instrumentations/src/oracledb.js +6 -6
  64. package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
  65. package/packages/datadog-instrumentations/src/pg.js +15 -15
  66. package/packages/datadog-instrumentations/src/pino.js +6 -10
  67. package/packages/datadog-instrumentations/src/playwright.js +20 -15
  68. package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
  69. package/packages/datadog-instrumentations/src/redis.js +1 -2
  70. package/packages/datadog-instrumentations/src/restify.js +2 -2
  71. package/packages/datadog-instrumentations/src/router.js +12 -12
  72. package/packages/datadog-instrumentations/src/stripe.js +12 -12
  73. package/packages/datadog-instrumentations/src/vitest.js +107 -26
  74. package/packages/datadog-instrumentations/src/winston.js +4 -4
  75. package/packages/datadog-instrumentations/src/ws.js +7 -7
  76. package/packages/datadog-plugin-aws-sdk/src/base.js +52 -4
  77. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +19 -12
  78. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +45 -35
  79. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +33 -22
  80. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +12 -13
  81. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +73 -54
  82. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +19 -17
  83. package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
  84. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
  85. package/packages/datadog-plugin-cucumber/src/index.js +4 -0
  86. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -4
  87. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -5
  88. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
  89. package/packages/datadog-plugin-http/src/client.js +1 -5
  90. package/packages/datadog-plugin-jest/src/util.js +1 -2
  91. package/packages/datadog-plugin-mocha/src/index.js +4 -0
  92. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -1
  93. package/packages/datadog-plugin-openai/src/tracing.js +12 -23
  94. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  95. package/packages/datadog-plugin-vitest/src/index.js +8 -1
  96. package/packages/datadog-shimmer/src/shimmer.js +7 -1
  97. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
  98. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
  99. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  100. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  101. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  102. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
  103. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  104. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
  105. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
  106. package/packages/dd-trace/src/appsec/index.js +21 -24
  107. package/packages/dd-trace/src/appsec/reporter.js +3 -1
  108. package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
  109. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
  110. package/packages/dd-trace/src/config/git_properties.js +2 -2
  111. package/packages/dd-trace/src/datastreams/index.js +2 -1
  112. package/packages/dd-trace/src/datastreams/processor.js +1 -2
  113. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
  114. package/packages/dd-trace/src/encode/0.4.js +757 -232
  115. package/packages/dd-trace/src/encode/0.5.js +13 -7
  116. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
  117. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +6 -3
  118. package/packages/dd-trace/src/llmobs/sdk.js +24 -26
  119. package/packages/dd-trace/src/llmobs/span_processor.js +25 -5
  120. package/packages/dd-trace/src/llmobs/util.js +1 -0
  121. package/packages/dd-trace/src/msgpack/chunk.js +6 -3
  122. package/packages/dd-trace/src/openfeature/noop.js +40 -36
  123. package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
  124. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
  125. package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
  126. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
  127. package/packages/dd-trace/src/plugins/util/ci.js +1 -1
  128. package/packages/dd-trace/src/plugins/util/git-cache.js +3 -5
  129. package/packages/dd-trace/src/plugins/util/test.js +19 -7
  130. package/packages/dd-trace/src/plugins/util/url.js +1 -3
  131. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  132. package/packages/dd-trace/src/plugins/util/web.js +5 -7
  133. package/packages/dd-trace/src/profiling/profilers/events.js +3 -23
  134. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -5
  135. package/packages/dd-trace/src/runtime_metrics/index.js +2 -2
  136. package/packages/dd-trace/src/scope.js +3 -10
  137. package/packages/dd-trace/src/serverless.js +1 -4
  138. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +7 -1
  139. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +4 -0
  140. package/packages/dd-trace/src/tracer.js +7 -7
@@ -83,14 +83,12 @@ class VulnerabilityFormatter {
83
83
  formatVulnerability (vulnerability, sourcesIndexes, sources) {
84
84
  const { type, hash, evidence, location } = vulnerability
85
85
 
86
- const formattedVulnerability = {
86
+ return {
87
87
  type,
88
88
  hash,
89
89
  evidence: this.formatEvidence(type, evidence, sourcesIndexes, sources),
90
90
  location,
91
91
  }
92
-
93
- return formattedVulnerability
94
92
  }
95
93
 
96
94
  toJson (vulnerabilitiesToFormat) {
@@ -9,8 +9,9 @@ const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
9
9
  const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
10
10
 
11
11
  // eslint-disable-next-line @stylistic/max-len
12
- const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm')
13
- const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm')
12
+ const REGEX_FOR_STRINGIFY_SENSITIVE_NOT_STRING = new RegExp(String.raw`"${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s\-+0-9.a-zA-Z]*)"`)
13
+ const REGEX_FOR_STRINGIFY_SENSITIVE = new RegExp(String.raw`${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_`)
14
+ const REGEX_FOR_STRINGIFY_RANGE = new RegExp(String.raw`(${STRINGIFY_RANGE_KEY}_\d+_)`)
14
15
 
15
16
  const sensitiveValueRegex = new RegExp(/** @type {string} */ (defaults['iast.redactionValuePattern']), 'gmi')
16
17
 
@@ -41,7 +42,6 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
41
42
  let counter = 0
42
43
  const allRanges = {}
43
44
  const sensitiveKeysMapping = {}
44
-
45
45
  iterateObject(obj, (val, levelKeys, parent, key) => {
46
46
  let currentLevelClone = cloneObj
47
47
  for (let i = 0; i < levelKeys.length - 1; i++) {
@@ -108,55 +108,90 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
108
108
  value = JSON.stringify(cloneObj, null, 2)
109
109
 
110
110
  if (counter > 0) {
111
- const keysRegex = loadSensitiveRanges
112
- ? KEYS_REGEX_WITH_SENSITIVE_RANGES
113
- : KEYS_REGEX_WITHOUT_SENSITIVE_RANGES
114
- keysRegex.lastIndex = 0
115
-
116
- let regexRes = keysRegex.exec(value)
117
- while (regexRes) {
118
- const offset = regexRes.index + 1 // +1 to increase the " char
119
-
120
- if (regexRes[1]) {
121
- // is a range
122
- const rangesId = regexRes[1]
123
- value = value.replace(rangesId, '')
124
-
125
- const updatedRanges = allRanges[rangesId].map(range => {
126
- return {
111
+ const segments = []
112
+ let outputLength = 0
113
+ let pos = 0
114
+ let rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY)
115
+
116
+ while (rangeKeyIndex > -1) {
117
+ let remainingStringValue = value.slice(rangeKeyIndex)
118
+ let cleanLength = rangeKeyIndex - pos
119
+
120
+ if (remainingStringValue.startsWith(STRINGIFY_SENSITIVE_NOT_STRING_KEY)) {
121
+ // In this case, we want to remove also the previous " char, because the value is not an string
122
+ rangeKeyIndex--
123
+ cleanLength--
124
+ remainingStringValue = value.slice(rangeKeyIndex)
125
+ const regexRes = REGEX_FOR_STRINGIFY_SENSITIVE_NOT_STRING.exec(remainingStringValue)
126
+
127
+ if (regexRes?.index === 0) {
128
+ const matchValue = regexRes[0]
129
+ const originalValue = regexRes[1]
130
+ const start = outputLength + cleanLength
131
+
132
+ sensitiveRanges.push({
133
+ start,
134
+ end: start + originalValue.length,
135
+ })
136
+ segments.push(value.slice(pos, rangeKeyIndex), originalValue)
137
+ outputLength += cleanLength + originalValue.length
138
+ pos = rangeKeyIndex + matchValue.length
139
+ } else {
140
+ // can't happen, the only way to this to happen is
141
+ // if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_NOT_STRING_KEY
142
+ segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1))
143
+ outputLength += cleanLength + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
144
+ pos = rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
145
+ }
146
+ } else if (remainingStringValue.startsWith(STRINGIFY_SENSITIVE_KEY)) {
147
+ const regexRes = REGEX_FOR_STRINGIFY_SENSITIVE.exec(remainingStringValue)
148
+ if (regexRes?.index === 0) {
149
+ const start = outputLength + cleanLength
150
+
151
+ sensitiveRanges.push({
152
+ start,
153
+ end: start + Number.parseInt(regexRes[1]),
154
+ })
155
+ segments.push(value.slice(pos, rangeKeyIndex))
156
+ outputLength += cleanLength
157
+ pos = rangeKeyIndex + regexRes[0].length
158
+ } else {
159
+ // can't happen, the only way to this to happen is
160
+ // if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_KEY
161
+ segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length))
162
+ outputLength += cleanLength + STRINGIFY_SENSITIVE_KEY.length
163
+ pos = rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length
164
+ }
165
+ } else {
166
+ const regexRes = REGEX_FOR_STRINGIFY_RANGE.exec(remainingStringValue)
167
+ if (regexRes?.index === 0) {
168
+ const start = outputLength + cleanLength
169
+ const rangesId = regexRes[1]
170
+
171
+ const updatedRanges = allRanges[rangesId].map(range => ({
127
172
  ...range,
128
- start: range.start + offset,
129
- end: range.end + offset,
130
- }
131
- })
132
-
133
- ranges.push(...updatedRanges)
134
- } else if (regexRes[2]) {
135
- // is a sensitive string literal
136
- const sensitiveId = regexRes[2]
137
-
138
- sensitiveRanges.push({
139
- start: offset,
140
- end: offset + Number.parseInt(regexRes[3]),
141
- })
142
-
143
- value = value.replace(sensitiveId, '')
144
- } else if (regexRes[4]) {
145
- // is a sensitive value (number, null, false, ...)
146
- const sensitiveId = regexRes[4]
147
- const originalValue = regexRes[5]
148
-
149
- sensitiveRanges.push({
150
- start: regexRes.index,
151
- end: regexRes.index + originalValue.length,
152
- })
153
-
154
- value = value.replace(sensitiveId, originalValue)
173
+ start: range.start + start,
174
+ end: range.end + start,
175
+ }))
176
+ ranges.push(...updatedRanges)
177
+
178
+ segments.push(value.slice(pos, rangeKeyIndex))
179
+ outputLength += cleanLength
180
+ pos = rangeKeyIndex + regexRes[0].length
181
+ } else {
182
+ // can't happen, the only way to this to happen is
183
+ // if the JSON has a value starting with the value of STRINGIFY_RANGE_KEY
184
+ segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_RANGE_KEY.length))
185
+ outputLength += cleanLength + STRINGIFY_RANGE_KEY.length
186
+ pos = rangeKeyIndex + STRINGIFY_RANGE_KEY.length
187
+ }
155
188
  }
156
189
 
157
- keysRegex.lastIndex = 0
158
- regexRes = keysRegex.exec(value)
190
+ rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY, pos)
159
191
  }
192
+
193
+ segments.push(value.slice(pos))
194
+ value = segments.join('')
160
195
  }
161
196
  } else {
162
197
  value = JSON.stringify(obj, null, 2)
@@ -5,6 +5,7 @@ const web = require('../plugins/util/web')
5
5
  const { extractIp } = require('../plugins/util/ip_extractor')
6
6
  const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
7
7
  const { IS_SERVERLESS } = require('../serverless')
8
+ const { isEmpty } = require('../util')
8
9
  const RuleManager = require('./rule_manager')
9
10
  const appsecRemoteConfig = require('./remote_config')
10
11
  const {
@@ -128,7 +129,7 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
128
129
  }
129
130
 
130
131
  if (typeof body === 'object') {
131
- if (isEmptyObject(body)) return
132
+ if (isEmpty(body)) return
132
133
  analyzedBodies.add(body)
133
134
  }
134
135
 
@@ -149,7 +150,7 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) {
149
150
  const rootSpan = web.root(req)
150
151
  if (!rootSpan) return
151
152
 
152
- if (isEmptyObject(cookies)) return
153
+ if (isEmpty(cookies)) return
153
154
  analyzedCookies.add(cookies)
154
155
 
155
156
  const results = waf.run({
@@ -180,12 +181,9 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
180
181
  }
181
182
  }
182
183
 
183
- const requestHeaders = { ...req.headers }
184
- delete requestHeaders.cookie
185
-
186
184
  const persistent = {
187
185
  [addresses.HTTP_INCOMING_URL]: req.url,
188
- [addresses.HTTP_INCOMING_HEADERS]: requestHeaders,
186
+ [addresses.HTTP_INCOMING_HEADERS]: copyHeadersOmitting(req.headers, 'cookie'),
189
187
  [addresses.HTTP_INCOMING_METHOD]: req.method,
190
188
  }
191
189
 
@@ -204,7 +202,7 @@ function incomingHttpEndTranslator ({ req, res }) {
204
202
  // we need to keep this to support other body parsers
205
203
  if (req.body !== undefined && req.body !== null) {
206
204
  if (typeof req.body === 'object') {
207
- if (!isEmptyObject(req.body) && !analyzedBodies.has(req.body)) {
205
+ if (!isEmpty(req.body) && !analyzedBodies.has(req.body)) {
208
206
  persistent[addresses.HTTP_INCOMING_BODY] = req.body
209
207
  }
210
208
  } else {
@@ -216,7 +214,7 @@ function incomingHttpEndTranslator ({ req, res }) {
216
214
  if (
217
215
  req.cookies !== null &&
218
216
  typeof req.cookies === 'object' &&
219
- !isEmptyObject(req.cookies) &&
217
+ !isEmpty(req.cookies) &&
220
218
  !analyzedCookies.has(req.cookies)
221
219
  ) {
222
220
  persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
@@ -227,7 +225,7 @@ function incomingHttpEndTranslator ({ req, res }) {
227
225
  if (
228
226
  query !== null &&
229
227
  typeof query === 'object' &&
230
- !isEmptyObject(query)
228
+ !isEmpty(query)
231
229
  ) {
232
230
  persistent[addresses.HTTP_INCOMING_QUERY] = query
233
231
  }
@@ -239,7 +237,7 @@ function incomingHttpEndTranslator ({ req, res }) {
239
237
  persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
240
238
  }
241
239
 
242
- if (!isEmptyObject(persistent)) {
240
+ if (!isEmpty(persistent)) {
243
241
  waf.run({ persistent }, req)
244
242
  }
245
243
 
@@ -313,7 +311,7 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
313
311
  const rootSpan = web.root(req)
314
312
  if (!rootSpan) return
315
313
 
316
- if (isEmptyObject(query)) return
314
+ if (isEmpty(query)) return
317
315
 
318
316
  const results = waf.run({
319
317
  persistent: {
@@ -328,7 +326,7 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
328
326
  const rootSpan = web.root(req)
329
327
  if (!rootSpan) return
330
328
 
331
- if (!params || typeof params !== 'object' || isEmptyObject(params)) return
329
+ if (!params || typeof params !== 'object' || isEmpty(params)) return
332
330
 
333
331
  const results = waf.run({
334
332
  persistent: {
@@ -352,7 +350,7 @@ function onResponseBody ({ req, res, body }) {
352
350
  }
353
351
 
354
352
  function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
355
- if (!isEmptyObject(responseHeaders)) {
353
+ if (!isEmpty(responseHeaders)) {
356
354
  storedResponseHeaders.set(req, responseHeaders)
357
355
  }
358
356
 
@@ -375,13 +373,10 @@ function onResponseWriteHead ({ req, res, abortController, statusCode, responseH
375
373
  const rootSpan = web.root(req)
376
374
  if (!rootSpan) return
377
375
 
378
- responseHeaders = { ...responseHeaders }
379
- delete responseHeaders['set-cookie']
380
-
381
376
  const results = waf.run({
382
377
  persistent: {
383
378
  [addresses.HTTP_INCOMING_RESPONSE_CODE]: String(statusCode),
384
- [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: responseHeaders,
379
+ [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: copyHeadersOmitting(responseHeaders, 'set-cookie'),
385
380
  },
386
381
  }, req)
387
382
 
@@ -540,14 +535,16 @@ function disable () {
540
535
  if (stripeConstructEvent.hasSubscribers) stripeConstructEvent.unsubscribe(onStripeConstructEvent)
541
536
  }
542
537
 
543
- // this is faster than Object.keys().length === 0
544
- function isEmptyObject (obj) {
545
- // eslint-disable-next-line no-unreachable-loop
546
- for (const _ in obj) {
547
- return false
538
+ /**
539
+ * @param {Record<string, unknown>} src
540
+ * @param {string} omit
541
+ */
542
+ function copyHeadersOmitting (src, omit) {
543
+ const filtered = {}
544
+ for (const key of Object.keys(src)) {
545
+ if (key !== omit) filtered[key] = src[key]
548
546
  }
549
-
550
- return true
547
+ return filtered
551
548
  }
552
549
 
553
550
  module.exports = {
@@ -509,7 +509,9 @@ function finishRequest (req, res, storedResponseHeaders, requestBody) {
509
509
  if (!rootSpan) return
510
510
 
511
511
  if (metricsQueue.size) {
512
- rootSpan.addTags(Object.fromEntries(metricsQueue))
512
+ for (const [key, value] of metricsQueue) {
513
+ rootSpan.setTag(key, value)
514
+ }
513
515
 
514
516
  keepTrace(rootSpan, ASM)
515
517
 
@@ -142,12 +142,14 @@ function extractErrors (diagnostics) {
142
142
 
143
143
  for (const diagnosticKey of DIAGNOSTIC_KEYS) {
144
144
  if (diagnostics[diagnosticKey]?.error) {
145
- (result[diagnosticKey] ??= {}).error = diagnostics[diagnosticKey]?.error
145
+ result[diagnosticKey] ??= {}
146
+ result[diagnosticKey].error = diagnostics[diagnosticKey]?.error
146
147
  isResultPopulated = true
147
148
  }
148
149
 
149
150
  if (diagnostics[diagnosticKey]?.errors) {
150
- (result[diagnosticKey] ??= {}).errors = diagnostics[diagnosticKey]?.errors
151
+ result[diagnosticKey] ??= {}
152
+ result[diagnosticKey].errors = diagnostics[diagnosticKey]?.errors
151
153
  isResultPopulated = true
152
154
  }
153
155
  }
@@ -20,6 +20,20 @@ class WAFContextWrapper {
20
20
  this.knownAddresses = knownAddresses
21
21
  this.addressesToSkip = new Set()
22
22
  this.cachedUserIdResults = new Map()
23
+ // Reused across run() calls; Reporter.reportMetrics consumes it synchronously.
24
+ this.metrics = {
25
+ rulesVersion,
26
+ wafVersion,
27
+ wafTimeout: false,
28
+ duration: 0,
29
+ durationExt: 0,
30
+ blockTriggered: false,
31
+ ruleTriggered: false,
32
+ errorCode: null,
33
+ maxTruncatedString: null,
34
+ maxTruncatedContainerSize: null,
35
+ maxTruncatedContainerDepth: null,
36
+ }
23
37
  }
24
38
 
25
39
  run ({ persistent, ephemeral }, raspRule, req) {
@@ -44,7 +58,8 @@ class WAFContextWrapper {
44
58
 
45
59
  const payload = {}
46
60
  let payloadHasData = false
47
- const newAddressesToSkip = new Set(this.addressesToSkip)
61
+ // Cloned lazily; only the preventDuplicateAddresses branch below adds to it.
62
+ let newAddressesToSkip
48
63
 
49
64
  if (persistent !== null && typeof persistent === 'object') {
50
65
  const persistentInputs = {}
@@ -55,6 +70,7 @@ class WAFContextWrapper {
55
70
  hasPersistentInputs = true
56
71
  persistentInputs[key] = persistent[key]
57
72
  if (preventDuplicateAddresses.has(key)) {
73
+ newAddressesToSkip ??= new Set(this.addressesToSkip)
58
74
  newAddressesToSkip.add(key)
59
75
  }
60
76
  }
@@ -85,19 +101,16 @@ class WAFContextWrapper {
85
101
 
86
102
  if (!payloadHasData) return
87
103
 
88
- const metrics = {
89
- rulesVersion: this.rulesVersion,
90
- wafVersion: this.wafVersion,
91
- wafTimeout: false,
92
- duration: 0,
93
- durationExt: 0,
94
- blockTriggered: false,
95
- ruleTriggered: false,
96
- errorCode: null,
97
- maxTruncatedString: null,
98
- maxTruncatedContainerSize: null,
99
- maxTruncatedContainerDepth: null,
100
- }
104
+ const metrics = this.metrics
105
+ metrics.wafTimeout = false
106
+ metrics.duration = 0
107
+ metrics.durationExt = 0
108
+ metrics.blockTriggered = false
109
+ metrics.ruleTriggered = false
110
+ metrics.errorCode = null
111
+ metrics.maxTruncatedString = null
112
+ metrics.maxTruncatedContainerSize = null
113
+ metrics.maxTruncatedContainerDepth = null
101
114
 
102
115
  try {
103
116
  const start = process.hrtime.bigint()
@@ -106,7 +119,7 @@ class WAFContextWrapper {
106
119
 
107
120
  const end = process.hrtime.bigint()
108
121
 
109
- metrics.durationExt = Number.parseInt(end - start) / 1e3
122
+ metrics.durationExt = Number(end - start) / 1e3
110
123
 
111
124
  if (typeof result.errorCode === 'number' && result.errorCode < 0) {
112
125
  const error = new Error('WAF code error')
@@ -123,7 +136,9 @@ class WAFContextWrapper {
123
136
  if (maxTruncatedContainerDepth) metrics.maxTruncatedContainerDepth = maxTruncatedContainerDepth
124
137
  }
125
138
 
126
- this.addressesToSkip = newAddressesToSkip
139
+ if (newAddressesToSkip !== undefined) {
140
+ this.addressesToSkip = newAddressesToSkip
141
+ }
127
142
 
128
143
  const ruleTriggered = !!result.events?.length
129
144
 
@@ -4,8 +4,8 @@ const fs = require('fs')
4
4
  const path = require('path')
5
5
 
6
6
  const gitPropertiesCommitSHARegex = /git\.commit\.sha=([a-f\d]{40})/
7
- const gitPropertiesRepositoryUrlRegex = /git\.repository_url=([\w\d:@/.-]+)/
8
- const repositoryUrlRegex = /^([\w\d:@/.-]+)$/
7
+ const gitPropertiesRepositoryUrlRegex = /git\.repository_url=([\w:@/.-]+)/
8
+ const repositoryUrlRegex = /^([\w:@/.-]+)$/
9
9
  const remoteOriginRegex = /^\[remote\s+"origin"\]/i
10
10
  const gitHeadRefRegex = /ref:\s+(refs\/[A-Za-z0-9._/-]+)/
11
11
  const commitSHARegex = /^[0-9a-f]{40}$/
@@ -28,7 +28,8 @@ function lazyClass (classGetter, methods = [], staticMethods = []) {
28
28
  }
29
29
 
30
30
  const activate = () => {
31
- return (ActiveClass = ActiveClass || classGetter())
31
+ ActiveClass ??= classGetter()
32
+ return ActiveClass
32
33
  }
33
34
 
34
35
  for (const method of methods) {
@@ -271,8 +271,7 @@ class DataStreamsProcessor {
271
271
  */
272
272
  bucketFromTimestamp (timestamp) {
273
273
  const bucketTime = Math.round(timestamp - (timestamp % this.bucketSizeNs))
274
- const bucket = this.buckets.forTime(bucketTime)
275
- return bucket
274
+ return this.buckets.forTime(bucketTime)
276
275
  }
277
276
 
278
277
  recordCheckpoint (checkpoint, span = null) {
@@ -175,6 +175,7 @@ function parseJsonToTree (json) {
175
175
  switch (json.charCodeAt(index)) {
176
176
  case 34: { // 34: double quote
177
177
  const stringStart = index + 1
178
+ // eslint-disable-next-line sonarjs/updated-loop-counter -- skip past the string token
178
179
  index = skipString(json, index)
179
180
  const stringLength = index - stringStart
180
181