dd-trace 5.18.0 → 5.20.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 (75) hide show
  1. package/LICENSE-3rdparty.csv +0 -2
  2. package/ext/formats.d.ts +1 -0
  3. package/ext/formats.js +2 -1
  4. package/index.d.ts +61 -39
  5. package/init.js +3 -15
  6. package/package.json +9 -12
  7. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  8. package/packages/datadog-instrumentations/src/fs.js +1 -1
  9. package/packages/datadog-instrumentations/src/hapi.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/register.js +13 -11
  11. package/packages/datadog-instrumentations/src/http/client.js +8 -2
  12. package/packages/datadog-instrumentations/src/http/server.js +50 -13
  13. package/packages/datadog-instrumentations/src/jest.js +17 -2
  14. package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
  15. package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
  16. package/packages/datadog-instrumentations/src/mocha/main.js +21 -8
  17. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  18. package/packages/datadog-instrumentations/src/next.js +1 -1
  19. package/packages/datadog-instrumentations/src/pg.js +2 -2
  20. package/packages/datadog-instrumentations/src/playwright.js +46 -32
  21. package/packages/datadog-instrumentations/src/process.js +29 -0
  22. package/packages/datadog-instrumentations/src/restify.js +1 -1
  23. package/packages/datadog-instrumentations/src/vitest.js +98 -28
  24. package/packages/datadog-plugin-aws-sdk/src/base.js +16 -2
  25. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  26. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  27. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +3 -3
  29. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  30. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -48
  32. package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
  33. package/packages/datadog-plugin-fs/src/index.js +1 -1
  34. package/packages/datadog-plugin-jest/src/index.js +7 -1
  35. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  36. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
  37. package/packages/datadog-plugin-openai/src/index.js +5 -5
  38. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  39. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  40. package/packages/datadog-plugin-vitest/src/index.js +19 -7
  41. package/packages/dd-trace/src/analytics_sampler.js +1 -1
  42. package/packages/dd-trace/src/appsec/blocking.js +10 -1
  43. package/packages/dd-trace/src/appsec/channels.js +4 -1
  44. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  45. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +16 -0
  46. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
  47. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -0
  48. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -1
  49. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  50. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  51. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +11 -0
  52. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/code-injection-sensitive-analyzer.js +25 -0
  53. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  54. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
  55. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
  56. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  57. package/packages/dd-trace/src/appsec/index.js +15 -10
  58. package/packages/dd-trace/src/appsec/passport.js +1 -1
  59. package/packages/dd-trace/src/appsec/rasp.js +121 -7
  60. package/packages/dd-trace/src/appsec/recommended.json +220 -2
  61. package/packages/dd-trace/src/appsec/reporter.js +0 -4
  62. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  63. package/packages/dd-trace/src/config.js +68 -66
  64. package/packages/dd-trace/src/data_streams.js +44 -0
  65. package/packages/dd-trace/src/datastreams/pathway.js +4 -2
  66. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  67. package/packages/dd-trace/src/log/index.js +32 -0
  68. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  69. package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
  70. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +43 -0
  71. package/packages/dd-trace/src/opentracing/tracer.js +10 -6
  72. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -2
  73. package/packages/dd-trace/src/plugins/plugin.js +12 -1
  74. package/packages/dd-trace/src/proxy.js +1 -0
  75. package/packages/dd-trace/src/tracer.js +2 -0
@@ -35,7 +35,7 @@ function wrapQuery (query) {
35
35
  const asyncResource = new AsyncResource('bound-anonymous-fn')
36
36
  const processId = this.processID
37
37
 
38
- const pgQuery = arguments[0] && typeof arguments[0] === 'object'
38
+ const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object'
39
39
  ? arguments[0]
40
40
  : { text: arguments[0] }
41
41
 
@@ -109,7 +109,7 @@ function wrapPoolQuery (query) {
109
109
 
110
110
  const asyncResource = new AsyncResource('bound-anonymous-fn')
111
111
 
112
- const pgQuery = arguments[0] && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }
112
+ const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }
113
113
 
114
114
  return asyncResource.runInAsyncScope(() => {
115
115
  startPoolQueryCh.publish({
@@ -2,7 +2,7 @@ const semver = require('semver')
2
2
 
3
3
  const { addHook, channel, AsyncResource } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
5
+ const { parseAnnotations, getTestSuitePath, NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
6
6
  const log = require('../../dd-trace/src/log')
7
7
 
8
8
  const testStartCh = channel('ci:playwright:test:start')
@@ -21,6 +21,7 @@ const testToAr = new WeakMap()
21
21
  const testSuiteToAr = new Map()
22
22
  const testSuiteToTestStatuses = new Map()
23
23
  const testSuiteToErrors = new Map()
24
+ const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
24
25
 
25
26
  let applyRepeatEachIndex = null
26
27
 
@@ -35,6 +36,7 @@ const STATUS_TO_TEST_STATUS = {
35
36
 
36
37
  let remainingTestsByFile = {}
37
38
  let isEarlyFlakeDetectionEnabled = false
39
+ let isFlakyTestRetriesEnabled = false
38
40
  let earlyFlakeDetectionNumRetries = 0
39
41
  let knownTests = {}
40
42
  let rootDir = ''
@@ -196,6 +198,31 @@ function getTestSuiteError (testSuiteAbsolutePath) {
196
198
  return new Error(`${errors.length} errors in this test suite:\n${errors.map(e => e.message).join('\n------\n')}`)
197
199
  }
198
200
 
201
+ function getTestByTestId (dispatcher, testId) {
202
+ if (dispatcher._testById) {
203
+ return dispatcher._testById.get(testId)?.test
204
+ }
205
+ const allTests = dispatcher._allTests || dispatcher._ddAllTests
206
+ if (allTests) {
207
+ return allTests.find(({ id }) => id === testId)
208
+ }
209
+ }
210
+
211
+ function getChannelPromise (channelToPublishTo) {
212
+ return new Promise(resolve => {
213
+ testSessionAsyncResource.runInAsyncScope(() => {
214
+ channelToPublishTo.publish({ onDone: resolve })
215
+ })
216
+ })
217
+ }
218
+ // eslint-disable-next-line
219
+ // Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
220
+ // We can't use test.outcome() directly because it's set on follow up handlers:
221
+ // our `testEndHandler` is called before the outcome is set.
222
+ function testWillRetry (test, testStatus) {
223
+ return testStatus === 'fail' && test.results.length <= test.retries
224
+ }
225
+
199
226
  function testBeginHandler (test, browserName) {
200
227
  const {
201
228
  _requireFile: testSuiteAbsolutePath,
@@ -250,6 +277,7 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
250
277
  testFinishCh.publish({
251
278
  testStatus,
252
279
  steps: testResult?.steps || [],
280
+ isRetry: testResult?.retry > 0,
253
281
  error,
254
282
  extraTags: annotationTags,
255
283
  isNew: test._ddIsNew,
@@ -267,8 +295,10 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
267
295
  addErrorToTestSuite(testSuiteAbsolutePath, error)
268
296
  }
269
297
 
270
- remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
271
- .filter(currentTest => currentTest !== test)
298
+ if (!testWillRetry(test, testStatus)) {
299
+ remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
300
+ .filter(currentTest => currentTest !== test)
301
+ }
272
302
 
273
303
  // Last test, we finish the suite
274
304
  if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
@@ -335,16 +365,6 @@ function dispatcherHook (dispatcherExport) {
335
365
  return dispatcherExport
336
366
  }
337
367
 
338
- function getTestByTestId (dispatcher, testId) {
339
- if (dispatcher._testById) {
340
- return dispatcher._testById.get(testId)?.test
341
- }
342
- const allTests = dispatcher._allTests || dispatcher._ddAllTests
343
- if (allTests) {
344
- return allTests.find(({ id }) => id === testId)
345
- }
346
- }
347
-
348
368
  function dispatcherHookNew (dispatcherExport, runWrapper) {
349
369
  shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)
350
370
  shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
@@ -373,8 +393,6 @@ function runnerHook (runnerExport, playwrightVersion) {
373
393
  shimmer.wrap(runnerExport.Runner.prototype, 'runAllTests', runAllTests => async function () {
374
394
  let onDone
375
395
 
376
- const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
377
-
378
396
  rootDir = getRootDir(this)
379
397
 
380
398
  const processArgv = process.argv.slice(2).join(' ')
@@ -383,46 +401,42 @@ function runnerHook (runnerExport, playwrightVersion) {
383
401
  testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
384
402
  })
385
403
 
386
- const configurationPromise = new Promise((resolve) => {
387
- onDone = resolve
388
- })
389
-
390
- testSessionAsyncResource.runInAsyncScope(() => {
391
- libraryConfigurationCh.publish({ onDone })
392
- })
393
-
394
404
  try {
395
- const { err, libraryConfig } = await configurationPromise
405
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
396
406
  if (!err) {
397
407
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
398
408
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
409
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
399
410
  }
400
411
  } catch (e) {
412
+ isEarlyFlakeDetectionEnabled = false
401
413
  log.error(e)
402
414
  }
403
415
 
404
416
  if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
405
- const knownTestsPromise = new Promise((resolve) => {
406
- onDone = resolve
407
- })
408
- testSessionAsyncResource.runInAsyncScope(() => {
409
- knownTestsCh.publish({ onDone })
410
- })
411
-
412
417
  try {
413
- const { err, knownTests: receivedKnownTests } = await knownTestsPromise
418
+ const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh)
414
419
  if (!err) {
415
420
  knownTests = receivedKnownTests
416
421
  } else {
417
422
  isEarlyFlakeDetectionEnabled = false
418
423
  }
419
424
  } catch (err) {
425
+ isEarlyFlakeDetectionEnabled = false
420
426
  log.error(err)
421
427
  }
422
428
  }
423
429
 
424
430
  const projects = getProjectsFromRunner(this)
425
431
 
432
+ if (isFlakyTestRetriesEnabled) {
433
+ projects.forEach(project => {
434
+ if (project.retries === 0) { // Only if it hasn't been set by the user
435
+ project.retries = NUM_FAILED_TEST_RETRIES
436
+ }
437
+ })
438
+ }
439
+
426
440
  const runAllTestsReturn = await runAllTests.apply(this, arguments)
427
441
 
428
442
  Object.values(remainingTestsByFile).forEach(tests => {
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel } = require('dc-polyfill')
5
+
6
+ const startSetUncaughtExceptionCaptureCallback = channel('datadog:process:setUncaughtExceptionCaptureCallback:start')
7
+
8
+ if (process.setUncaughtExceptionCaptureCallback) {
9
+ let currentCallback
10
+
11
+ shimmer.wrap(process, 'setUncaughtExceptionCaptureCallback',
12
+ function wrapSetUncaughtExceptionCaptureCallback (originalSetUncaughtExceptionCaptureCallback) {
13
+ return function setUncaughtExceptionCaptureCallback (newCallback) {
14
+ if (startSetUncaughtExceptionCaptureCallback.hasSubscribers) {
15
+ const abortController = new AbortController()
16
+ startSetUncaughtExceptionCaptureCallback.publish({ newCallback, currentCallback, abortController })
17
+ if (abortController.signal.aborted) {
18
+ return
19
+ }
20
+ }
21
+
22
+ const result = originalSetUncaughtExceptionCaptureCallback.apply(this, arguments)
23
+
24
+ currentCallback = newCallback
25
+
26
+ return result
27
+ }
28
+ })
29
+ }
@@ -51,7 +51,7 @@ function wrapFn (fn) {
51
51
 
52
52
  try {
53
53
  const result = fn.apply(this, arguments)
54
- if (result && typeof result === 'object' && typeof result.then === 'function') {
54
+ if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
55
55
  return result.then(function () {
56
56
  nextChannel.publish({ req })
57
57
  finishChannel.publish({ req })
@@ -1,5 +1,6 @@
1
1
  const { addHook, channel, AsyncResource } = require('./helpers/instrument')
2
2
  const shimmer = require('../../datadog-shimmer')
3
+ const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
3
4
 
4
5
  // test hooks
5
6
  const testStartCh = channel('ci:vitest:test:start')
@@ -16,6 +17,7 @@ const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
16
17
  // test session hooks
17
18
  const testSessionStartCh = channel('ci:vitest:session:start')
18
19
  const testSessionFinishCh = channel('ci:vitest:session:finish')
20
+ const libraryConfigurationCh = channel('ci:vitest:library-configuration')
19
21
 
20
22
  const taskToAsync = new WeakMap()
21
23
 
@@ -30,6 +32,18 @@ function isReporterPackageNew (vitestPackage) {
30
32
  return vitestPackage.e?.name === 'BaseSequencer'
31
33
  }
32
34
 
35
+ function isReporterPackageNewest (vitestPackage) {
36
+ return vitestPackage.h?.name === 'BaseSequencer'
37
+ }
38
+
39
+ function getChannelPromise (channelToPublishTo) {
40
+ return new Promise(resolve => {
41
+ sessionAsyncResource.runInAsyncScope(() => {
42
+ channelToPublishTo.publish({ onDone: resolve })
43
+ })
44
+ })
45
+ }
46
+
33
47
  function getSessionStatus (state) {
34
48
  if (state.getCountOfFailedTests() > 0) {
35
49
  return 'fail'
@@ -90,6 +104,23 @@ function getSortWrapper (sort) {
90
104
  if (!testSessionFinishCh.hasSubscribers) {
91
105
  return sort.apply(this, arguments)
92
106
  }
107
+ // There isn't any other async function that we seem to be able to hook into
108
+ // So we will use the sort from BaseSequencer. This means that a custom sequencer
109
+ // will not work. This will be a known limitation.
110
+ let isFlakyTestRetriesEnabled = false
111
+
112
+ try {
113
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
114
+ if (!err) {
115
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
116
+ }
117
+ } catch (e) {
118
+ isFlakyTestRetriesEnabled = false
119
+ }
120
+ if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) {
121
+ this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
122
+ }
123
+
93
124
  shimmer.wrap(this.ctx, 'exit', exit => async function () {
94
125
  let onFinish
95
126
 
@@ -119,6 +150,21 @@ function getSortWrapper (sort) {
119
150
  }
120
151
  }
121
152
 
153
+ function getCreateCliWrapper (vitestPackage, frameworkVersion) {
154
+ shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
155
+ if (!testSessionStartCh.hasSubscribers) {
156
+ return oldCreateCli.apply(this, arguments)
157
+ }
158
+ sessionAsyncResource.runInAsyncScope(() => {
159
+ const processArgv = process.argv.slice(2).join(' ')
160
+ testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
161
+ })
162
+ return oldCreateCli.apply(this, arguments)
163
+ })
164
+
165
+ return vitestPackage
166
+ }
167
+
122
168
  addHook({
123
169
  name: 'vitest',
124
170
  versions: ['>=1.6.0'],
@@ -126,15 +172,31 @@ addHook({
126
172
  }, (vitestPackage) => {
127
173
  const { VitestTestRunner } = vitestPackage
128
174
  // test start (only tests that are not marked as skip or todo)
129
- shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task) {
175
+ shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
130
176
  if (!testStartCh.hasSubscribers) {
131
177
  return onBeforeTryTask.apply(this, arguments)
132
178
  }
179
+ const { retry: numAttempt } = retryInfo
180
+ // We finish the previous test here because we know it has failed already
181
+ if (numAttempt > 0) {
182
+ const asyncResource = taskToAsync.get(task)
183
+ const testError = task.result?.errors?.[0]
184
+ if (asyncResource) {
185
+ asyncResource.runInAsyncScope(() => {
186
+ testErrorCh.publish({ error: testError })
187
+ })
188
+ }
189
+ }
190
+
133
191
  const asyncResource = new AsyncResource('bound-anonymous-fn')
134
192
  taskToAsync.set(task, asyncResource)
135
193
 
136
194
  asyncResource.runInAsyncScope(() => {
137
- testStartCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
195
+ testStartCh.publish({
196
+ testName: getTestName(task),
197
+ testSuiteAbsolutePath: task.file.filepath,
198
+ isRetry: numAttempt > 0
199
+ })
138
200
  })
139
201
  return onBeforeTryTask.apply(this, arguments)
140
202
  })
@@ -163,12 +225,25 @@ addHook({
163
225
  return vitestPackage
164
226
  })
165
227
 
228
+ // There are multiple index* files across different versions of vitest,
229
+ // so we check for the existence of BaseSequencer to determine if we are in the right file
166
230
  addHook({
167
231
  name: 'vitest',
168
- versions: ['>=2.0.0'],
232
+ versions: ['>=1.6.0 <2.0.0'],
233
+ filePattern: 'dist/vendor/index.*'
234
+ }, (vitestPackage) => {
235
+ if (isReporterPackage(vitestPackage)) {
236
+ shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
237
+ }
238
+
239
+ return vitestPackage
240
+ })
241
+
242
+ addHook({
243
+ name: 'vitest',
244
+ versions: ['>=2.0.0 <2.0.5'],
169
245
  filePattern: 'dist/vendor/index.*'
170
246
  }, (vitestPackage) => {
171
- // there are multiple index* files so we have to check the exported values
172
247
  if (isReporterPackageNew(vitestPackage)) {
173
248
  shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
174
249
  }
@@ -178,12 +253,11 @@ addHook({
178
253
 
179
254
  addHook({
180
255
  name: 'vitest',
181
- versions: ['>=1.6.0'],
182
- filePattern: 'dist/vendor/index.*'
256
+ versions: ['>=2.0.5'],
257
+ filePattern: 'dist/chunks/index.*'
183
258
  }, (vitestPackage) => {
184
- // there are multiple index* files so we have to check the exported values
185
- if (isReporterPackage(vitestPackage)) {
186
- shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
259
+ if (isReporterPackageNewest(vitestPackage)) {
260
+ shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
187
261
  }
188
262
 
189
263
  return vitestPackage
@@ -192,22 +266,15 @@ addHook({
192
266
  // Can't specify file because compiled vitest includes hashes in their files
193
267
  addHook({
194
268
  name: 'vitest',
195
- versions: ['>=1.6.0'],
269
+ versions: ['>=1.6.0 <2.0.5'],
196
270
  filePattern: 'dist/vendor/cac.*'
197
- }, (vitestPackage, frameworkVersion) => {
198
- shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
199
- if (!testSessionStartCh.hasSubscribers) {
200
- return oldCreateCli.apply(this, arguments)
201
- }
202
- sessionAsyncResource.runInAsyncScope(() => {
203
- const processArgv = process.argv.slice(2).join(' ')
204
- testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
205
- })
206
- return oldCreateCli.apply(this, arguments)
207
- })
271
+ }, getCreateCliWrapper)
208
272
 
209
- return vitestPackage
210
- })
273
+ addHook({
274
+ name: 'vitest',
275
+ versions: ['>=2.0.5'],
276
+ filePattern: 'dist/chunks/cac.*'
277
+ }, getCreateCliWrapper)
211
278
 
212
279
  // test suite start and finish
213
280
  // only relevant for workers
@@ -215,7 +282,7 @@ addHook({
215
282
  name: '@vitest/runner',
216
283
  versions: ['>=1.6.0'],
217
284
  file: 'dist/index.js'
218
- }, vitestPackage => {
285
+ }, (vitestPackage, frameworkVersion) => {
219
286
  shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPath) {
220
287
  let testSuiteError = null
221
288
  if (!testSuiteStartCh.hasSubscribers) {
@@ -224,7 +291,7 @@ addHook({
224
291
 
225
292
  const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
226
293
  testSuiteAsyncResource.runInAsyncScope(() => {
227
- testSuiteStartCh.publish(testPath[0])
294
+ testSuiteStartCh.publish({ testSuiteAbsolutePath: testPath[0], frameworkVersion })
228
295
  })
229
296
  const startTestsResponse = await startTests.apply(this, arguments)
230
297
 
@@ -235,6 +302,7 @@ addHook({
235
302
 
236
303
  const testTasks = getTypeTasks(startTestsResponse[0].tasks)
237
304
 
305
+ // Only one test task per test, even if there are retries
238
306
  testTasks.forEach(task => {
239
307
  const testAsyncResource = taskToAsync.get(task)
240
308
  const { result } = task
@@ -242,7 +310,7 @@ addHook({
242
310
  if (result) {
243
311
  const { state, duration, errors } = result
244
312
  if (state === 'skip') { // programmatic skip
245
- testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
313
+ testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
246
314
  } else if (state === 'pass') {
247
315
  if (testAsyncResource) {
248
316
  testAsyncResource.runInAsyncScope(() => {
@@ -258,8 +326,10 @@ addHook({
258
326
  }
259
327
 
260
328
  if (testAsyncResource) {
329
+ const isRetry = task.result?.retryCount > 0
330
+ // `duration` is the duration of all the retries, so it can't be used if there are retries
261
331
  testAsyncResource.runInAsyncScope(() => {
262
- testErrorCh.publish({ duration, error: testError })
332
+ testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError })
263
333
  })
264
334
  }
265
335
  if (errors?.length) {
@@ -267,7 +337,7 @@ addHook({
267
337
  }
268
338
  }
269
339
  } else { // test.skip or test.todo
270
- testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
340
+ testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
271
341
  }
272
342
  })
273
343
 
@@ -4,6 +4,7 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
4
4
  const ClientPlugin = require('../../dd-trace/src/plugins/client')
5
5
  const { storage } = require('../../datadog-core')
6
6
  const { isTrue } = require('../../dd-trace/src/util')
7
+ const coalesce = require('koalas')
7
8
 
8
9
  class BaseAwsSdkPlugin extends ClientPlugin {
9
10
  static get id () { return 'aws' }
@@ -73,7 +74,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
73
74
  if (!cbExists && this.serviceIdentifier === 'sqs') {
74
75
  const params = response.request.params
75
76
  const operation = response.request.operation
76
- this.responseExtractDSMContext(operation, params, response.data, span)
77
+ this.responseExtractDSMContext(operation, params, response.data ?? response, span)
77
78
  }
78
79
  this.addResponseTags(span, response)
79
80
  this.finish(span, response, response.error)
@@ -163,9 +164,22 @@ function normalizeConfig (config, serviceIdentifier) {
163
164
  break
164
165
  }
165
166
 
167
+ // check if AWS batch propagation or AWS_[SERVICE] batch propagation is enabled via env variable
168
+ const serviceId = serviceIdentifier.toUpperCase()
169
+ const batchPropagationEnabled = isTrue(
170
+ coalesce(
171
+ specificConfig.batchPropagationEnabled,
172
+ process.env[`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`],
173
+ config.batchPropagationEnabled,
174
+ process.env.DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED,
175
+ false
176
+ )
177
+ )
178
+
179
+ // Merge the specific config back into the main config
166
180
  return Object.assign({}, config, specificConfig, {
167
181
  splitByAwsService: config.splitByAwsService !== false,
168
- batchPropagationEnabled: config.batchPropagationEnabled !== false,
182
+ batchPropagationEnabled,
169
183
  hooks
170
184
  })
171
185
  }
@@ -21,7 +21,7 @@ class DynamoDb extends BaseAwsSdkPlugin {
21
21
  // batch operations have different format, collect table name for batch
22
22
  // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#batchGetItem-property`
23
23
  // dynamoDB batch TableName
24
- if (params.RequestItems) {
24
+ if (params.RequestItems !== null) {
25
25
  if (typeof params.RequestItems === 'object') {
26
26
  if (Object.keys(params.RequestItems).length === 1) {
27
27
  const tableName = Object.keys(params.RequestItems)[0]
@@ -156,7 +156,7 @@ class Kinesis extends BaseAwsSdkPlugin {
156
156
  span,
157
157
  params.Records[i],
158
158
  stream,
159
- i === 0 || (this.config.kinesis && this.config.kinesis.batchPropagationEnabled)
159
+ i === 0 || (this.config.batchPropagationEnabled)
160
160
  )
161
161
  }
162
162
  }
@@ -63,7 +63,7 @@ class Sns extends BaseAwsSdkPlugin {
63
63
  span,
64
64
  params.PublishBatchRequestEntries[i],
65
65
  params.TopicArn,
66
- i === 0 || (this.config.sns && this.config.sns.batchPropagationEnabled)
66
+ i === 0 || (this.config.batchPropagationEnabled)
67
67
  )
68
68
  }
69
69
  break
@@ -157,8 +157,8 @@ class Sqs extends BaseAwsSdkPlugin {
157
157
  if (attributes.StringValue) {
158
158
  const textMap = attributes.StringValue
159
159
  return JSON.parse(textMap)
160
- } else if (attributes.Type === 'Binary') {
161
- const buffer = Buffer.from(attributes.Value, 'base64')
160
+ } else if (attributes.Type === 'Binary' || attributes.DataType === 'Binary') {
161
+ const buffer = Buffer.from(attributes.Value ?? attributes.BinaryValue, 'base64')
162
162
  return JSON.parse(buffer)
163
163
  }
164
164
  } catch (e) {
@@ -222,7 +222,7 @@ class Sqs extends BaseAwsSdkPlugin {
222
222
  span,
223
223
  params.Entries[i],
224
224
  params.QueueUrl,
225
- i === 0 || (this.config.sqs && this.config.sqs.batchPropagationEnabled)
225
+ i === 0 || (this.config.batchPropagationEnabled)
226
226
  )
227
227
  }
228
228
  break
@@ -47,7 +47,7 @@ class Stepfunctions extends BaseAwsSdkPlugin {
47
47
 
48
48
  try {
49
49
  const inputObj = JSON.parse(input)
50
- if (inputObj && typeof inputObj === 'object') {
50
+ if (inputObj !== null && typeof inputObj === 'object') {
51
51
  // We've parsed the input JSON string
52
52
  inputObj._datadog = {}
53
53
  this.tracer.inject(span, 'text_map', inputObj._datadog)
@@ -7,10 +7,10 @@ const PROCESS_DENYLIST = ['md5']
7
7
 
8
8
  const VARNAMES_REGEX = /\$([\w\d_]*)(?:[^\w\d_]|$)/gmi
9
9
  // eslint-disable-next-line max-len
10
- const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|api_?key|secret|a(?:ccess|uth)_token|mysql_pwd|credentials|(?:stripe)?token)$'
10
+ const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|address|api[-_]?key|e?mail|secret(?:[-_]?key)?|a(?:ccess|uth)[-_]?token|mysql_pwd|credentials|(?:stripe)?token)$'
11
11
  const regexParam = new RegExp(PARAM_PATTERN, 'i')
12
12
  const ENV_PATTERN = '^(\\w+=\\w+;)*\\w+=\\w+;?$'
13
- const envvarRegex = new RegExp(ENV_PATTERN)
13
+ const envVarRegex = new RegExp(ENV_PATTERN)
14
14
  const REDACTED = '?'
15
15
 
16
16
  function extractVarNames (expression) {
@@ -61,7 +61,9 @@ function scrubChildProcessCmd (expression) {
61
61
  for (let index = 0; index < expressionTokens.length; index++) {
62
62
  const token = expressionTokens[index]
63
63
 
64
- if (typeof token === 'object') {
64
+ if (token === null) {
65
+ continue
66
+ } else if (typeof token === 'object') {
65
67
  if (token.pattern) {
66
68
  result.push(token.pattern)
67
69
  } else if (token.op) {
@@ -70,7 +72,7 @@ function scrubChildProcessCmd (expression) {
70
72
  result.push(`#${token.comment}`)
71
73
  }
72
74
  } else if (!foundBinary) {
73
- if (envvarRegex.test(token)) {
75
+ if (envVarRegex.test(token)) {
74
76
  const envSplit = token.split('=')
75
77
 
76
78
  if (!ALLOWED_ENV_VARIABLES.includes(envSplit[0])) {