dd-trace 5.73.0 → 5.75.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 (37) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +39 -7
  3. package/loader-hook.mjs +52 -1
  4. package/package.json +8 -16
  5. package/packages/datadog-core/src/utils/src/set.js +5 -1
  6. package/packages/datadog-esbuild/index.js +105 -36
  7. package/packages/datadog-esbuild/src/utils.js +198 -0
  8. package/packages/datadog-instrumentations/src/cookie-parser.js +0 -2
  9. package/packages/datadog-instrumentations/src/cucumber.js +2 -2
  10. package/packages/datadog-instrumentations/src/express.js +82 -0
  11. package/packages/datadog-instrumentations/src/helpers/router-helper.js +238 -0
  12. package/packages/datadog-instrumentations/src/jest.js +2 -1
  13. package/packages/datadog-instrumentations/src/mariadb.js +9 -7
  14. package/packages/datadog-instrumentations/src/playwright.js +226 -93
  15. package/packages/datadog-instrumentations/src/router.js +63 -6
  16. package/packages/datadog-instrumentations/src/vitest.js +44 -12
  17. package/packages/datadog-instrumentations/src/ws.js +3 -3
  18. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  19. package/packages/datadog-plugin-express/src/code_origin.js +2 -0
  20. package/packages/datadog-plugin-playwright/src/index.js +74 -31
  21. package/packages/datadog-plugin-ws/src/close.js +1 -1
  22. package/packages/datadog-shimmer/src/shimmer.js +2 -0
  23. package/packages/dd-trace/src/aiguard/sdk.js +25 -3
  24. package/packages/dd-trace/src/aiguard/tags.js +4 -1
  25. package/packages/dd-trace/src/config-helper.js +4 -1
  26. package/packages/dd-trace/src/config.js +599 -592
  27. package/packages/dd-trace/src/config_defaults.js +14 -12
  28. package/packages/dd-trace/src/plugins/util/ci.js +3 -2
  29. package/packages/dd-trace/src/plugins/util/stacktrace.js +16 -1
  30. package/packages/dd-trace/src/proxy.js +1 -1
  31. package/packages/dd-trace/src/supported-configurations.json +1 -0
  32. package/packages/dd-trace/src/telemetry/endpoints.js +27 -1
  33. package/packages/dd-trace/src/telemetry/index.js +16 -13
  34. package/packages/dd-trace/src/telemetry/logs/log-collector.js +5 -3
  35. package/register.js +1 -11
  36. package/scripts/preinstall.js +3 -1
  37. package/version.js +2 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  const pkg = require('./pkg')
4
4
  const { GRPC_CLIENT_ERROR_STATUSES, GRPC_SERVER_ERROR_STATUSES } = require('./constants')
5
- const { getEnvironmentVariables } = require('./config-helper')
5
+ const { getEnvironmentVariable: getEnv } = require('./config-helper')
6
6
 
7
7
  // eslint-disable-next-line @stylistic/max-len
8
8
  const qsRegex = String.raw`(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\s|%20)*(?::|%3A)(?:\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\s|%20)+[a-z0-9\._\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+\/=-]|%3D|%2F|%2B)+)?|[\-]{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY[\-]{5}[^\-]+[\-]{5}END(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY|ssh-rsa(?:\s|%20)*(?:[a-z0-9\/\.+]|%2F|%5C|%2B){100,}`
@@ -11,21 +11,16 @@ const defaultWafObfuscatorKeyRegex = String.raw`(?i)pass|pw(?:or)?d|secret|(?:ap
11
11
  // eslint-disable-next-line @stylistic/max-len
12
12
  const defaultWafObfuscatorValueRegex = String.raw`(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\.net(?:[_-]|-)sessionid|sid|jwt)(?:\s*=([^;&]+)|"\s*:\s*("[^"]+"|\d+))|bearer\s+([a-z0-9\._\-]+)|token\s*:\s*([a-z0-9]{13})|gh[opsu]_([0-9a-zA-Z]{36})|ey[I-L][\w=-]+\.(ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?)|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}([^\-]+)[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*([a-z0-9\/\.+]{100,})`
13
13
 
14
- const {
15
- AWS_LAMBDA_FUNCTION_NAME,
16
- FUNCTION_NAME,
17
- K_SERVICE,
18
- WEBSITE_SITE_NAME
19
- } = getEnvironmentVariables()
20
-
21
- const service = AWS_LAMBDA_FUNCTION_NAME ||
22
- FUNCTION_NAME || // Google Cloud Function Name set by deprecated runtimes
23
- K_SERVICE || // Google Cloud Function Name set by newer runtimes
24
- WEBSITE_SITE_NAME || // set by Azure Functions
14
+ const service = getEnv('AWS_LAMBDA_FUNCTION_NAME') ||
15
+ getEnv('FUNCTION_NAME') || // Google Cloud Function Name set by deprecated runtimes
16
+ getEnv('K_SERVICE') || // Google Cloud Function Name set by newer runtimes
17
+ getEnv('WEBSITE_SITE_NAME') || // set by Azure Functions
25
18
  pkg.name ||
26
19
  'node'
27
20
 
28
21
  module.exports = {
22
+ apiKey: undefined,
23
+ appKey: undefined,
29
24
  apmTracingEnabled: true,
30
25
  'appsec.apiSecurity.enabled': true,
31
26
  'appsec.apiSecurity.sampleDelay': 30,
@@ -57,6 +52,10 @@ module.exports = {
57
52
  baggageTagKeys: 'user.id,session.id,account.id',
58
53
  clientIpEnabled: false,
59
54
  clientIpHeader: null,
55
+ 'cloudPayloadTagging.requestsEnabled': false,
56
+ 'cloudPayloadTagging.responsesEnabled': false,
57
+ 'cloudPayloadTagging.maxDepth': 10,
58
+ 'cloudPayloadTagging.rules': [],
60
59
  'crashtracking.enabled': true,
61
60
  'codeOriginForSpans.enabled': true,
62
61
  'codeOriginForSpans.experimental.exit_spans.enabled': false,
@@ -102,6 +101,9 @@ module.exports = {
102
101
  'iast.telemetryVerbosity': 'INFORMATION',
103
102
  'iast.stackTrace.enabled': true,
104
103
  injectionEnabled: [],
104
+ 'installSignature.id': null,
105
+ 'installSignature.time': null,
106
+ 'installSignature.type': null,
105
107
  instrumentationSource: 'manual',
106
108
  injectForce: null,
107
109
  isAzureFunction: false,
@@ -95,10 +95,11 @@ function normalizeNumber (number) {
95
95
  }
96
96
 
97
97
  function getGitHubEventPayload () {
98
- if (!getEnvironmentVariable('GITHUB_EVENT_PATH')) {
98
+ const path = getEnvironmentVariable('GITHUB_EVENT_PATH')
99
+ if (!path) {
99
100
  return
100
101
  }
101
- return JSON.parse(readFileSync(getEnvironmentVariable('GITHUB_EVENT_PATH'), 'utf8'))
102
+ return JSON.parse(readFileSync(path, 'utf8'))
102
103
  }
103
104
 
104
105
  module.exports = {
@@ -1,12 +1,22 @@
1
1
  'use strict'
2
2
 
3
- const { relative, sep } = require('path')
3
+ const { relative, sep, join } = require('path')
4
4
 
5
5
  const cwd = process.cwd()
6
6
 
7
7
  const NODE_MODULES_PATTERN_MIDDLE = `${sep}node_modules${sep}`
8
8
  const NODE_MODULES_PATTERN_START = `node_modules${sep}`
9
9
 
10
+ /**
11
+ * We detect if we're running inside the dd-trace-js repo by checking if the
12
+ * current file path ends with the expected path structure from the repo root.
13
+ * This is needed for local and CI where dd-trace-js is not in node_modules.
14
+ * In production, these frames are already filtered by isNodeModulesFrame.
15
+ */
16
+ const SHOULD_FILTER_DD_TRACE_INSTRUMENTAION = __filename.endsWith(
17
+ join(sep, 'dd-trace-js', 'packages', 'dd-trace', 'src', 'plugins', 'util', 'stacktrace.js')
18
+ )
19
+
10
20
  module.exports = {
11
21
  getCallSites,
12
22
  parseUserLandFrames
@@ -90,6 +100,7 @@ function parseLine (stack, start, end) {
90
100
  [fileName, lineNumber, columnNumber, index] = result
91
101
 
92
102
  if (isNodeModulesFrame(fileName)) return
103
+ if (SHOULD_FILTER_DD_TRACE_INSTRUMENTAION && isDDInstrumentationFile(fileName)) return
93
104
 
94
105
  // parse method name
95
106
  let methodName, functionName
@@ -153,6 +164,10 @@ function isNodeModulesFrame (fileName) {
153
164
  return relativePath.startsWith(NODE_MODULES_PATTERN_START) || relativePath.includes(NODE_MODULES_PATTERN_MIDDLE)
154
165
  }
155
166
 
167
+ function isDDInstrumentationFile (fileName) {
168
+ return fileName.includes(`packages${sep}datadog-instrumentations${sep}src`)
169
+ }
170
+
156
171
  /**
157
172
  * A stack trace location can be in one of the following formats:
158
173
  *
@@ -164,7 +164,7 @@ class Tracer extends NoopProxy {
164
164
  rc.setProductHandler('FFE_FLAGS', (action, conf) => {
165
165
  // Feed UFC config directly to OpenFeature provider
166
166
  if (action === 'apply' || action === 'modify') {
167
- this.openfeature._setConfiguration(conf.flag_configuration)
167
+ this.openfeature._setConfiguration(conf)
168
168
  }
169
169
  })
170
170
  }
@@ -282,6 +282,7 @@
282
282
  "DD_TRACE_FASTIFY_ENABLED": ["A"],
283
283
  "DD_TRACE_FETCH_ENABLED": ["A"],
284
284
  "DD_TRACE_FIND_MY_WAY_ENABLED": ["A"],
285
+ "DD_TRACE_FLUSH_INTERVAL": ["A"],
285
286
  "DD_TRACE_FS_ENABLED": ["A"],
286
287
  "DD_TRACE_GENERIC_POOL_ENABLED": ["A"],
287
288
  "DD_TRACE_GIT_METADATA_ENABLED": ["A"],
@@ -4,6 +4,8 @@ const dc = require('dc-polyfill')
4
4
  const { sendData } = require('./send-data')
5
5
 
6
6
  const fastifyRouteCh = dc.channel('apm:fastify:route:added')
7
+ const expressRouteCh = dc.channel('apm:express:route:added')
8
+ const routerRouteCh = dc.channel('apm:router:route:added')
7
9
 
8
10
  let config
9
11
  let application
@@ -16,6 +18,7 @@ let updateRetryData
16
18
  * Map key is `${METHOD} ${PATH}`, value is { method, path }
17
19
  */
18
20
  const pendingEndpoints = new Map()
21
+ const wildcardEndpoints = new Set()
19
22
  let flushScheduled = false
20
23
  let isFirstPayload = true
21
24
 
@@ -42,12 +45,31 @@ function onFastifyRoute (routeData) {
42
45
  if (!routeOptions?.path) return
43
46
 
44
47
  const methods = Array.isArray(routeOptions.method) ? routeOptions.method : [routeOptions.method]
45
-
46
48
  for (const method of methods) {
47
49
  recordEndpoint(method, routeOptions.path)
48
50
  }
49
51
  }
50
52
 
53
+ function onExpressRoute ({ method, path }) {
54
+ if (!method || !path) return
55
+
56
+ // If wildcard already recorded for this path, skip specific methods
57
+ if (wildcardEndpoints.has(path)) return
58
+
59
+ recordEndpoint(method, path)
60
+
61
+ // If this is a wildcard event, record it and mark path as wildcarded
62
+ if (method === '*') {
63
+ wildcardEndpoints.add(path)
64
+ return
65
+ }
66
+
67
+ // Express automatically adds HEAD support for GET routes
68
+ if (method.toUpperCase() === 'GET') {
69
+ recordEndpoint('HEAD', path)
70
+ }
71
+ }
72
+
51
73
  function buildEndpointObjects (endpoints) {
52
74
  return endpoints.map(({ method, path }) => {
53
75
  return {
@@ -108,10 +130,14 @@ function start (_config = {}, _application, _host, getRetryDataFunction, updateR
108
130
  updateRetryData = updateRetryDataFunction
109
131
 
110
132
  fastifyRouteCh.subscribe(onFastifyRoute)
133
+ expressRouteCh.subscribe(onExpressRoute)
134
+ routerRouteCh.subscribe(onExpressRoute)
111
135
  }
112
136
 
113
137
  function stop () {
114
138
  fastifyRouteCh.unsubscribe(onFastifyRoute)
139
+ expressRouteCh.unsubscribe(onExpressRoute)
140
+ routerRouteCh.unsubscribe(onExpressRoute)
115
141
 
116
142
  pendingEndpoints.clear()
117
143
  flushScheduled = false
@@ -1,23 +1,26 @@
1
1
  'use strict'
2
2
 
3
- const activate = () => {
4
- const active = require('./telemetry')
3
+ let telemetry
5
4
 
6
- return Object.setPrototypeOf(module.exports, active)
7
- }
8
-
9
- const inactive = {
5
+ // Lazy load the telemetry module to avoid the performance impact of loading it unconditionally
6
+ module.exports = {
10
7
  start (config, ...args) {
11
- return config?.telemetry?.enabled && activate().start(config, ...args)
8
+ telemetry ??= require('./telemetry')
9
+ telemetry.start(config, ...args)
10
+ },
11
+ stop () {
12
+ telemetry?.stop()
12
13
  },
13
- stop () {},
14
14
  // This might be called before `start` so we have to trigger loading the
15
15
  // underlying module here as well.
16
16
  updateConfig (changes, config, ...args) {
17
- return config?.telemetry?.enabled && activate().updateConfig(changes, config, ...args)
17
+ telemetry ??= require('./telemetry')
18
+ telemetry.updateConfig(changes, config, ...args)
18
19
  },
19
- updateIntegrations () {},
20
- appClosing () {}
20
+ updateIntegrations () {
21
+ telemetry?.updateIntegrations()
22
+ },
23
+ appClosing () {
24
+ telemetry?.appClosing()
25
+ }
21
26
  }
22
-
23
- module.exports = Object.setPrototypeOf({}, inactive)
@@ -41,12 +41,14 @@ function sanitize (logEntry) {
41
41
 
42
42
  const firstIndex = stackLines.findIndex(l => l.match(STACK_FRAME_LINE_REGEX))
43
43
 
44
- const isDDCode = firstIndex !== -1 && stackLines[firstIndex].includes(ddBasePath)
44
+ // Filter to keep only DD frames
45
45
  stackLines = stackLines
46
- .filter((line, index) => (isDDCode && index < firstIndex) || line.includes(ddBasePath))
46
+ .filter((line, index) => index >= firstIndex && line.includes(ddBasePath))
47
47
  .map(line => line.replace(ddBasePath, ''))
48
48
 
49
- if (!isDDCode && logEntry.errorType && stackLines.length) {
49
+ // ALWAYS redact error messages (RFC requirement: exception type only, no message)
50
+ // This handles single-line and multi-line error messages
51
+ if (logEntry.errorType && stackLines.length) {
50
52
  stackLines = [`${logEntry.errorType}: redacted`, ...stackLines]
51
53
  }
52
54
 
package/register.js CHANGED
@@ -5,14 +5,4 @@
5
5
  const { register } = require('node:module')
6
6
  const { pathToFileURL } = require('node:url')
7
7
 
8
- register('./loader-hook.mjs', pathToFileURL(__filename), {
9
- data: {
10
- exclude: [
11
- /langsmith/,
12
- /openai\/_shims/,
13
- /openai\/resources\/chat\/completions\/messages/,
14
- /openai\/agents-core\/dist\/shims/,
15
- /@anthropic-ai\/sdk\/_shims/
16
- ]
17
- }
18
- })
8
+ register('./loader-hook.mjs', pathToFileURL(__filename))
@@ -17,7 +17,9 @@ const nodeMajor = Number(process.versions.node.split('.')[0])
17
17
 
18
18
  const min = Number(requirePackageJson(path.join(__dirname, '..')).engines.node.match(/\d+/)[0])
19
19
 
20
- const hasIgnoreEngines = npmArgv &&
20
+ // Most package managers don't support `npm_config_argv`, so we need a custom
21
+ // flag to allow installing dd-trace on unsupported engines.
22
+ const hasIgnoreEngines = process.env._DD_IGNORE_ENGINES === 'true' || npmArgv &&
21
23
  npmArgv.original &&
22
24
  npmArgv.original.includes('--ignore-engines')
23
25
 
package/version.js CHANGED
@@ -13,5 +13,6 @@ module.exports = {
13
13
  DD_PATCH: parseInt(ddMatches[3]),
14
14
  NODE_MAJOR: parseInt(nodeMatches[1]),
15
15
  NODE_MINOR: parseInt(nodeMatches[2]),
16
- NODE_PATCH: parseInt(nodeMatches[3])
16
+ NODE_PATCH: parseInt(nodeMatches[3]),
17
+ NODE_VERSION: nodeMatches[0]
17
18
  }