dd-trace 5.100.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 (189) hide show
  1. package/index.d.ts +14 -0
  2. package/package.json +11 -9
  3. package/packages/datadog-instrumentations/src/aerospike.js +2 -2
  4. package/packages/datadog-instrumentations/src/ai.js +8 -8
  5. package/packages/datadog-instrumentations/src/amqplib.js +6 -7
  6. package/packages/datadog-instrumentations/src/anthropic.js +10 -10
  7. package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
  8. package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
  9. package/packages/datadog-instrumentations/src/avsc.js +6 -6
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
  11. package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
  12. package/packages/datadog-instrumentations/src/bluebird.js +2 -2
  13. package/packages/datadog-instrumentations/src/body-parser.js +2 -2
  14. package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
  15. package/packages/datadog-instrumentations/src/child_process.js +12 -12
  16. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +9 -9
  17. package/packages/datadog-instrumentations/src/connect.js +7 -7
  18. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  19. package/packages/datadog-instrumentations/src/cookie.js +2 -2
  20. package/packages/datadog-instrumentations/src/couchbase.js +16 -30
  21. package/packages/datadog-instrumentations/src/crypto.js +4 -4
  22. package/packages/datadog-instrumentations/src/cucumber.js +77 -16
  23. package/packages/datadog-instrumentations/src/cypress.js +5 -3
  24. package/packages/datadog-instrumentations/src/dns.js +0 -3
  25. package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
  26. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
  27. package/packages/datadog-instrumentations/src/express-session.js +4 -4
  28. package/packages/datadog-instrumentations/src/express.js +10 -11
  29. package/packages/datadog-instrumentations/src/fastify.js +2 -2
  30. package/packages/datadog-instrumentations/src/fs.js +14 -14
  31. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
  32. package/packages/datadog-instrumentations/src/google-genai.js +4 -4
  33. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  34. package/packages/datadog-instrumentations/src/hapi.js +2 -2
  35. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +8 -8
  36. package/packages/datadog-instrumentations/src/helpers/promise.js +2 -2
  37. package/packages/datadog-instrumentations/src/hono.js +2 -2
  38. package/packages/datadog-instrumentations/src/http/client.js +26 -9
  39. package/packages/datadog-instrumentations/src/http/server.js +9 -9
  40. package/packages/datadog-instrumentations/src/jest.js +93 -63
  41. package/packages/datadog-instrumentations/src/kafkajs.js +9 -9
  42. package/packages/datadog-instrumentations/src/knex.js +17 -17
  43. package/packages/datadog-instrumentations/src/koa.js +12 -12
  44. package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
  45. package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
  46. package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
  47. package/packages/datadog-instrumentations/src/lodash.js +4 -4
  48. package/packages/datadog-instrumentations/src/mariadb.js +13 -13
  49. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  50. package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
  51. package/packages/datadog-instrumentations/src/mocha/common.js +7 -4
  52. package/packages/datadog-instrumentations/src/mocha/main.js +37 -14
  53. package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
  54. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -7
  55. package/packages/datadog-instrumentations/src/mongodb-core.js +9 -22
  56. package/packages/datadog-instrumentations/src/mongodb.js +5 -5
  57. package/packages/datadog-instrumentations/src/mongoose.js +21 -21
  58. package/packages/datadog-instrumentations/src/mquery.js +5 -5
  59. package/packages/datadog-instrumentations/src/multer.js +4 -4
  60. package/packages/datadog-instrumentations/src/mysql.js +16 -16
  61. package/packages/datadog-instrumentations/src/mysql2.js +4 -4
  62. package/packages/datadog-instrumentations/src/net.js +14 -8
  63. package/packages/datadog-instrumentations/src/nyc.js +5 -5
  64. package/packages/datadog-instrumentations/src/openai.js +19 -19
  65. package/packages/datadog-instrumentations/src/oracledb.js +6 -6
  66. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
  67. package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
  68. package/packages/datadog-instrumentations/src/pg.js +15 -15
  69. package/packages/datadog-instrumentations/src/pino.js +6 -10
  70. package/packages/datadog-instrumentations/src/playwright.js +20 -15
  71. package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
  72. package/packages/datadog-instrumentations/src/redis.js +1 -2
  73. package/packages/datadog-instrumentations/src/restify.js +2 -2
  74. package/packages/datadog-instrumentations/src/router.js +12 -12
  75. package/packages/datadog-instrumentations/src/stripe.js +12 -12
  76. package/packages/datadog-instrumentations/src/vitest.js +107 -26
  77. package/packages/datadog-instrumentations/src/winston.js +4 -4
  78. package/packages/datadog-instrumentations/src/ws.js +7 -7
  79. package/packages/datadog-plugin-aws-sdk/src/base.js +52 -4
  80. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +19 -12
  81. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +45 -35
  82. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +33 -22
  83. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +12 -13
  84. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +73 -54
  85. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +19 -17
  86. package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
  87. package/packages/datadog-plugin-bullmq/src/consumer.js +2 -2
  88. package/packages/datadog-plugin-bullmq/src/producer.js +14 -20
  89. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
  90. package/packages/datadog-plugin-cucumber/src/index.js +4 -0
  91. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +18 -4
  92. package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
  93. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -5
  94. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
  95. package/packages/datadog-plugin-http/src/client.js +1 -5
  96. package/packages/datadog-plugin-jest/src/util.js +1 -2
  97. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
  98. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
  99. package/packages/datadog-plugin-mocha/src/index.js +4 -0
  100. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -1
  101. package/packages/datadog-plugin-openai/src/tracing.js +12 -23
  102. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  103. package/packages/datadog-plugin-vitest/src/index.js +8 -1
  104. package/packages/datadog-shimmer/src/shimmer.js +7 -1
  105. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
  106. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
  107. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
  111. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  112. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
  113. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
  114. package/packages/dd-trace/src/appsec/index.js +21 -24
  115. package/packages/dd-trace/src/appsec/reporter.js +7 -2
  116. package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
  117. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
  118. package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
  119. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
  120. package/packages/dd-trace/src/config/config-types.d.ts +0 -2
  121. package/packages/dd-trace/src/config/git_properties.js +2 -2
  122. package/packages/dd-trace/src/config/index.js +1 -55
  123. package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
  124. package/packages/dd-trace/src/datastreams/encoding.js +39 -28
  125. package/packages/dd-trace/src/datastreams/index.js +2 -1
  126. package/packages/dd-trace/src/datastreams/pathway.js +29 -26
  127. package/packages/dd-trace/src/datastreams/processor.js +18 -17
  128. package/packages/dd-trace/src/datastreams/size.js +6 -2
  129. package/packages/dd-trace/src/debugger/config.js +5 -2
  130. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
  131. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  132. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
  133. package/packages/dd-trace/src/dogstatsd.js +10 -7
  134. package/packages/dd-trace/src/encode/0.4.js +759 -234
  135. package/packages/dd-trace/src/encode/0.5.js +15 -9
  136. package/packages/dd-trace/src/encode/agentless-json.js +2 -2
  137. package/packages/dd-trace/src/encode/tags-processors.js +2 -27
  138. package/packages/dd-trace/src/exporters/common/request.js +22 -11
  139. package/packages/dd-trace/src/exporters/common/retry.js +104 -0
  140. package/packages/dd-trace/src/git_metadata.js +66 -0
  141. package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
  142. package/packages/dd-trace/src/id.js +15 -26
  143. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  144. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
  145. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
  146. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
  147. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +33 -13
  148. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
  149. package/packages/dd-trace/src/llmobs/sdk.js +29 -27
  150. package/packages/dd-trace/src/llmobs/span_processor.js +52 -6
  151. package/packages/dd-trace/src/llmobs/tagger.js +42 -0
  152. package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
  153. package/packages/dd-trace/src/llmobs/util.js +81 -5
  154. package/packages/dd-trace/src/msgpack/chunk.js +6 -3
  155. package/packages/dd-trace/src/openfeature/noop.js +40 -36
  156. package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
  157. package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
  158. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
  159. package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -2
  160. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
  161. package/packages/dd-trace/src/opentelemetry/span-helpers.js +188 -50
  162. package/packages/dd-trace/src/opentelemetry/span.js +42 -80
  163. package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
  164. package/packages/dd-trace/src/opentracing/propagation/text_map.js +65 -27
  165. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
  166. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +58 -22
  167. package/packages/dd-trace/src/opentracing/span.js +56 -48
  168. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  169. package/packages/dd-trace/src/plugins/util/ci.js +1 -1
  170. package/packages/dd-trace/src/plugins/util/git-cache.js +3 -5
  171. package/packages/dd-trace/src/plugins/util/test.js +19 -7
  172. package/packages/dd-trace/src/plugins/util/url.js +1 -3
  173. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  174. package/packages/dd-trace/src/plugins/util/web.js +5 -7
  175. package/packages/dd-trace/src/priority_sampler.js +6 -4
  176. package/packages/dd-trace/src/profiling/config.js +5 -4
  177. package/packages/dd-trace/src/profiling/profilers/events.js +3 -23
  178. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -5
  179. package/packages/dd-trace/src/remote_config/index.js +5 -3
  180. package/packages/dd-trace/src/runtime_metrics/index.js +2 -2
  181. package/packages/dd-trace/src/scope.js +3 -10
  182. package/packages/dd-trace/src/serverless.js +1 -4
  183. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +7 -1
  184. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +4 -0
  185. package/packages/dd-trace/src/span_format.js +52 -5
  186. package/packages/dd-trace/src/span_processor.js +0 -4
  187. package/packages/dd-trace/src/spanleak.js +0 -1
  188. package/packages/dd-trace/src/tracer.js +7 -7
  189. package/packages/dd-trace/src/util.js +17 -0
@@ -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 = {
@@ -8,6 +8,7 @@ const web = require('../plugins/util/web')
8
8
  const { ipHeaderList } = require('../plugins/util/ip_extractor')
9
9
  const { keepTrace } = require('../priority_sampler')
10
10
  const { ASM } = require('../standalone/product')
11
+ const { isEmpty } = require('../util')
11
12
  const { getActiveRequest } = require('./store')
12
13
  const {
13
14
  incrementWafInitMetric,
@@ -170,7 +171,9 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
170
171
  // Basic collection
171
172
  if (!shouldCollectEventHeaders) return mandatoryCollectedHeaders
172
173
 
173
- const responseHeaders = Object.keys(storedResponseHeaders).length === 0
174
+ // Skip the spread when the stored side is empty -- common during the early
175
+ // request lifecycle when no upstream response headers have been captured.
176
+ const responseHeaders = isEmpty(storedResponseHeaders)
174
177
  ? res.getHeaders()
175
178
  : { ...storedResponseHeaders, ...res.getHeaders() }
176
179
 
@@ -506,7 +509,9 @@ function finishRequest (req, res, storedResponseHeaders, requestBody) {
506
509
  if (!rootSpan) return
507
510
 
508
511
  if (metricsQueue.size) {
509
- rootSpan.addTags(Object.fromEntries(metricsQueue))
512
+ for (const [key, value] of metricsQueue) {
513
+ rootSpan.setTag(key, value)
514
+ }
510
515
 
511
516
  keepTrace(rootSpan, ASM)
512
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
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { getEnvironmentVariable } = require('../config/helper')
4
4
  const { isTrue } = require('../util')
5
+ const { DD_MAJOR } = require('../../../../version')
5
6
 
6
7
  /**
7
8
  * Returns the current Lage package name if the Lage package name override is enabled.
@@ -9,7 +10,7 @@ const { isTrue } = require('../util')
9
10
  * @returns {string|undefined}
10
11
  */
11
12
  function getLagePackageName () {
12
- if (!isTrue(getEnvironmentVariable('DD_ENABLE_LAGE_PACKAGE_NAME'))) {
13
+ if (DD_MAJOR < 6 && !isTrue(getEnvironmentVariable('DD_ENABLE_LAGE_PACKAGE_NAME'))) {
13
14
  return
14
15
  }
15
16
 
@@ -7,39 +7,13 @@ const zlib = require('zlib')
7
7
  const { storage } = require('../../../../datadog-core')
8
8
  const log = require('../../log')
9
9
  const { httpAgent, httpsAgent } = require('../../exporters/common/agents')
10
+ const {
11
+ RATE_LIMIT_MAX_WAIT_MS,
12
+ isRetriableNetworkError,
13
+ singleJitteredDelay,
14
+ } = require('../../exporters/common/retry')
10
15
  const { urlToHttpOptions } = require('../../exporters/common/url-to-http-options-polyfill')
11
16
 
12
- const RATE_LIMIT_MAX_WAIT_MS = 30_000
13
- const RETRY_BASE_MS = 5000
14
- const RETRY_JITTER_MS = 2500
15
-
16
- /**
17
- * Calculates retry delay with jitter to prevent thundering herd.
18
- * Delay is RETRY_BASE_MS + random(0, RETRY_JITTER_MS) (e.g. 5–7.5 seconds).
19
- *
20
- * @returns {number} Delay in milliseconds
21
- */
22
- function getRetryDelay () {
23
- return RETRY_BASE_MS + (Math.random() * RETRY_JITTER_MS)
24
- }
25
-
26
- /**
27
- * Determines if a network error is retriable (transient failures only).
28
- * ECONNREFUSED is retried because it can be transient (service starting up,
29
- * restarts, rolling deploys, k8s pod/readiness transitions). ENOTFOUND is
30
- * excluded as it indicates DNS failure or wrong host and is usually not transient.
31
- *
32
- * @param {Error} err - The error to check
33
- * @returns {boolean}
34
- */
35
- function isRetriableNetworkError (err) {
36
- if (!err.code) return false
37
- return err.code === 'ECONNREFUSED' ||
38
- err.code === 'ECONNRESET' ||
39
- err.code === 'ETIMEDOUT' ||
40
- err.code === 'EPIPE'
41
- }
42
-
43
17
  function parseUrl (urlObjOrString) {
44
18
  if (urlObjOrString !== null && typeof urlObjOrString === 'object') {
45
19
  return urlToHttpOptions(urlObjOrString)
@@ -63,6 +37,10 @@ function parseUrl (urlObjOrString) {
63
37
  * Destroys connections on errors to prevent reuse of bad connections. Preserves
64
38
  * original status code across retries for telemetry.
65
39
  *
40
+ * Retry timers stay ref'd. Test-runner plugins block the suite via
41
+ * `delay: true` channels until this callback fires; an unref'd retry would
42
+ * let the host exit first and the suite would never run.
43
+ *
66
44
  * @param {string} data - Request body (e.g. JSON string)
67
45
  * @param {object} options - { url, path?, method?, headers?, timeout? } (may be mutated)
68
46
  * @param {Function} callback - (err, res, statusCode) => void
@@ -157,7 +135,7 @@ function request (data, options, callback) {
157
135
  // ignore
158
136
  }
159
137
  hasRetried = true
160
- setTimeout(makeRequest, getRetryDelay())
138
+ setTimeout(makeRequest, singleJitteredDelay())
161
139
  return
162
140
  }
163
141
 
@@ -177,7 +155,7 @@ function request (data, options, callback) {
177
155
  // Retry on retriable network errors
178
156
  if (!hasRetried && isRetriableNetworkError(err)) {
179
157
  hasRetried = true
180
- setTimeout(makeRequest, getRetryDelay())
158
+ setTimeout(makeRequest, singleJitteredDelay())
181
159
  return
182
160
  }
183
161
 
@@ -8,7 +8,6 @@ export interface ConfigProperties extends GeneratedConfig {
8
8
  responsesEnabled: boolean
9
9
  rules: PayloadTaggingRules
10
10
  }
11
- commitSHA: string | undefined
12
11
  debug: boolean
13
12
  instrumentationSource: 'manual' | 'ssi'
14
13
  isCiVisibility: boolean
@@ -18,7 +17,6 @@ export interface ConfigProperties extends GeneratedConfig {
18
17
  lookup: NonNullable<import('../../../../index').TracerOptions['lookup']>
19
18
  readonly parsedDdTags: Record<string, string>
20
19
  plugins: boolean
21
- repositoryUrl: string | undefined
22
20
  sampler: {
23
21
  rateLimit: number
24
22
  rules: import('../../../../index').SamplingRule[]
@@ -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}$/
@@ -3,7 +3,6 @@
3
3
  const fs = require('node:fs')
4
4
  const os = require('node:os')
5
5
  const { URL } = require('node:url')
6
- const path = require('node:path')
7
6
 
8
7
  const rfdc = require('../../../../vendor/dist/rfdc')({ proto: false, circles: false })
9
8
  const uuid = require('../../../../vendor/dist/crypto-randomuuid') // we need to keep the old uuid dep because of cypress
@@ -12,7 +11,6 @@ const { DD_MAJOR } = require('../../../../version')
12
11
  const log = require('../log')
13
12
  const pkg = require('../pkg')
14
13
  const { isTrue } = require('../util')
15
- const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
16
14
  const telemetry = require('../telemetry')
17
15
  const telemetryMetrics = require('../telemetry/metrics')
18
16
  const {
@@ -22,8 +20,6 @@ const {
22
20
  } = require('../serverless')
23
21
  const { ORIGIN_KEY, DATADOG_MINI_AGENT_PATH } = require('../constants')
24
22
  const { appendRules } = require('../payload-tagging/config')
25
- const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } =
26
- require('./git_properties')
27
23
  const ConfigBase = require('./config-base')
28
24
  const {
29
25
  getEnvironmentVariable,
@@ -215,10 +211,6 @@ class Config extends ConfigBase {
215
211
 
216
212
  warnWrongOtelSettings()
217
213
 
218
- if (this.DD_TRACE_GIT_METADATA_ENABLED) {
219
- this.#loadGitMetadata()
220
- }
221
-
222
214
  parseErrors.clear()
223
215
  }
224
216
 
@@ -513,7 +505,7 @@ class Config extends ConfigBase {
513
505
  } else {
514
506
  const NX_TASK_TARGET_PROJECT = getEnvironmentVariable('NX_TASK_TARGET_PROJECT')
515
507
  if (NX_TASK_TARGET_PROJECT) {
516
- if (this.DD_ENABLE_NX_SERVICE_NAME) {
508
+ if (DD_MAJOR >= 6 || this.DD_ENABLE_NX_SERVICE_NAME) {
517
509
  setAndTrack(this, 'service', normalizeService(NX_TASK_TARGET_PROJECT) || 'node')
518
510
  isServiceNameInferred = true
519
511
  } else if (DD_MAJOR < 6) {
@@ -628,52 +620,6 @@ class Config extends ConfigBase {
628
620
 
629
621
  telemetry.updateConfig([...configWithOrigin.values()], this)
630
622
  }
631
-
632
- // TODO: Move outside of config. This is unrelated to the config system.
633
- #loadGitMetadata () {
634
- // Try to read Git metadata from the environment variables
635
- this.repositoryUrl = removeUserSensitiveInfo(this.DD_GIT_REPOSITORY_URL ?? this.tags[GIT_REPOSITORY_URL])
636
- this.commitSHA = this.DD_GIT_COMMIT_SHA ?? this.tags[GIT_COMMIT_SHA]
637
-
638
- // Otherwise, try to read Git metadata from the git.properties file
639
- if (!this.repositoryUrl || !this.commitSHA) {
640
- const DD_GIT_PROPERTIES_FILE = this.DD_GIT_PROPERTIES_FILE
641
- const gitPropertiesFile = DD_GIT_PROPERTIES_FILE ?? `${process.cwd()}/git.properties`
642
- try {
643
- const gitPropertiesString = fs.readFileSync(gitPropertiesFile, 'utf8')
644
- const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
645
- this.commitSHA ??= commitSHA
646
- this.repositoryUrl ??= repositoryUrl
647
- } catch (error) {
648
- // Only log error if the user has set a git.properties path
649
- if (DD_GIT_PROPERTIES_FILE) {
650
- log.error('Error reading DD_GIT_PROPERTIES_FILE: %s', gitPropertiesFile, error)
651
- }
652
- }
653
- }
654
-
655
- // Otherwise, try to read Git metadata from the .git/ folder
656
- const DD_GIT_FOLDER_PATH = this.DD_GIT_FOLDER_PATH
657
- const gitFolderPath = DD_GIT_FOLDER_PATH ?? path.join(process.cwd(), '.git')
658
-
659
- if (!this.repositoryUrl) {
660
- // Try to read git config (repository URL)
661
- const gitConfigPath = path.join(gitFolderPath, 'config')
662
- try {
663
- const gitConfigContent = fs.readFileSync(gitConfigPath, 'utf8')
664
- if (gitConfigContent) {
665
- this.repositoryUrl = getRemoteOriginURL(gitConfigContent)
666
- }
667
- } catch (error) {
668
- // Only log error if the user has set a .git/ path
669
- if (DD_GIT_FOLDER_PATH) {
670
- log.error('Error reading git config: %s', gitConfigPath, error)
671
- }
672
- }
673
- }
674
- // Try to read git HEAD (commit SHA)
675
- this.commitSHA ??= resolveGitHeadSHA(gitFolderPath)
676
- }
677
623
  }
678
624
 
679
625
  /**