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
@@ -46,7 +46,6 @@ dev,eslint-plugin-import,MIT,Copyright 2015 Ben Mosher
46
46
  dev,eslint-plugin-mocha,MIT,Copyright 2014 Mathias Schreck
47
47
  dev,eslint-plugin-n,MIT,Copyright 2015 Toru Nagashima
48
48
  dev,eslint-plugin-promise,ISC,jden and other contributors
49
- dev,eslint-plugin-standard,MIT,Copyright 2015 Jamund Ferguson
50
49
  dev,express,MIT,Copyright 2009-2014 TJ Holowaychuk 2013-2014 Roman Shtylman 2014-2015 Douglas Christopher Wilson
51
50
  dev,get-port,MIT,Copyright Sindre Sorhus
52
51
  dev,glob,ISC,Copyright Isaac Z. Schlueter and Contributors
@@ -63,7 +62,6 @@ dev,rimraf,ISC,Copyright Isaac Z. Schlueter and Contributors
63
62
  dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen
64
63
  dev,sinon-chai,WTFPL and BSD-2-Clause,Copyright 2004 Sam Hocevar 2012–2017 Domenic Denicola
65
64
  dev,tap,ISC,Copyright 2011-2022 Isaac Z. Schlueter and Contributors
66
- dev,tape,MIT,Copyright James Halliday
67
65
  dev,tiktoken,MIT,Copyright (c) 2022 OpenAI, Shantanu Jain
68
66
  file,aws-lambda-nodejs-runtime-interface-client,Apache 2.0,Copyright 2019 Amazon.com Inc. or its affiliates. All Rights Reserved.
69
67
  file,profile.proto,Apache license 2.0,Copyright 2016 Google Inc.
package/ext/formats.d.ts CHANGED
@@ -5,6 +5,7 @@ declare const formats: {
5
5
  HTTP_HEADERS: typeof opentracing.FORMAT_HTTP_HEADERS
6
6
  BINARY: typeof opentracing.FORMAT_BINARY
7
7
  LOG: 'log'
8
+ TEXT_MAP_DSM: 'text_map_dsm'
8
9
  }
9
10
 
10
11
  export = formats
package/ext/formats.js CHANGED
@@ -4,5 +4,6 @@ module.exports = {
4
4
  TEXT_MAP: 'text_map',
5
5
  HTTP_HEADERS: 'http_headers',
6
6
  BINARY: 'binary',
7
- LOG: 'log'
7
+ LOG: 'log',
8
+ TEXT_MAP_DSM: 'text_map_dsm'
8
9
  }
package/index.d.ts CHANGED
@@ -518,45 +518,7 @@ declare namespace tracer {
518
518
  /**
519
519
  * Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
520
520
  */
521
- iast?: boolean | {
522
- /**
523
- * Whether to enable IAST.
524
- * @default false
525
- */
526
- enabled?: boolean,
527
- /**
528
- * Controls the percentage of requests that iast will analyze
529
- * @default 30
530
- */
531
- requestSampling?: number,
532
- /**
533
- * Controls how many request can be analyzing code vulnerabilities at the same time
534
- * @default 2
535
- */
536
- maxConcurrentRequests?: number,
537
- /**
538
- * Controls how many code vulnerabilities can be detected in the same request
539
- * @default 2
540
- */
541
- maxContextOperations?: number,
542
- /**
543
- * Whether to enable vulnerability deduplication
544
- */
545
- deduplicationEnabled?: boolean,
546
- /**
547
- * Whether to enable vulnerability redaction
548
- * @default true
549
- */
550
- redactionEnabled?: boolean,
551
- /**
552
- * Specifies a regex that will redact sensitive source names in vulnerability reports.
553
- */
554
- redactionNamePattern?: string,
555
- /**
556
- * Specifies a regex that will redact sensitive source values in vulnerability reports.
557
- */
558
- redactionValuePattern?: string
559
- }
521
+ iast?: boolean | IastOptions
560
522
 
561
523
  appsec?: {
562
524
  /**
@@ -736,6 +698,11 @@ declare namespace tracer {
736
698
  }
737
699
  };
738
700
 
701
+ /**
702
+ * Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
703
+ */
704
+ iast?: boolean | IastOptions
705
+
739
706
  /**
740
707
  * Configuration of ASM Remote Configuration
741
708
  */
@@ -2137,6 +2104,61 @@ declare namespace tracer {
2137
2104
  export type TimeInput = otel.TimeInput;
2138
2105
  export type TraceState = otel.TraceState;
2139
2106
  }
2107
+
2108
+ /**
2109
+ * Iast configuration used in `tracer` and `tracer.experimental` options
2110
+ */
2111
+ interface IastOptions {
2112
+ /**
2113
+ * Whether to enable IAST.
2114
+ * @default false
2115
+ */
2116
+ enabled?: boolean,
2117
+
2118
+ /**
2119
+ * Controls the percentage of requests that iast will analyze
2120
+ * @default 30
2121
+ */
2122
+ requestSampling?: number,
2123
+
2124
+ /**
2125
+ * Controls how many request can be analyzing code vulnerabilities at the same time
2126
+ * @default 2
2127
+ */
2128
+ maxConcurrentRequests?: number,
2129
+
2130
+ /**
2131
+ * Controls how many code vulnerabilities can be detected in the same request
2132
+ * @default 2
2133
+ */
2134
+ maxContextOperations?: number,
2135
+
2136
+ /**
2137
+ * Whether to enable vulnerability deduplication
2138
+ */
2139
+ deduplicationEnabled?: boolean,
2140
+
2141
+ /**
2142
+ * Whether to enable vulnerability redaction
2143
+ * @default true
2144
+ */
2145
+ redactionEnabled?: boolean,
2146
+
2147
+ /**
2148
+ * Specifies a regex that will redact sensitive source names in vulnerability reports.
2149
+ */
2150
+ redactionNamePattern?: string,
2151
+
2152
+ /**
2153
+ * Specifies a regex that will redact sensitive source values in vulnerability reports.
2154
+ */
2155
+ redactionValuePattern?: string,
2156
+
2157
+ /**
2158
+ * Specifies the verbosity of the sent telemetry. Default 'INFORMATION'
2159
+ */
2160
+ telemetryVerbosity?: string
2161
+ }
2140
2162
  }
2141
2163
 
2142
2164
  /**
package/init.js CHANGED
@@ -2,22 +2,10 @@
2
2
 
3
3
  const path = require('path')
4
4
  const Module = require('module')
5
- const telemetry = require('./packages/dd-trace/src/telemetry/init-telemetry')
6
5
  const semver = require('semver')
7
-
8
- function isTrue (envVar) {
9
- return ['1', 'true', 'True'].includes(envVar)
10
- }
11
-
12
- // eslint-disable-next-line no-console
13
- let log = { info: isTrue(process.env.DD_TRACE_DEBUG) ? console.log : () => {} }
14
- if (semver.satisfies(process.versions.node, '>=16')) {
15
- const Config = require('./packages/dd-trace/src/config')
16
- log = require('./packages/dd-trace/src/log')
17
-
18
- // eslint-disable-next-line no-new
19
- new Config() // we need this to initialize the logger
20
- }
6
+ const log = require('./packages/dd-trace/src/log')
7
+ const { isTrue } = require('./packages/dd-trace/src/util')
8
+ const telemetry = require('./packages/dd-trace/src/telemetry/init-telemetry')
21
9
 
22
10
  let initBailout = false
23
11
  let clobberBailout = false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.18.0",
3
+ "version": "5.20.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -33,6 +33,7 @@
33
33
  "test:profiler": "tap \"packages/dd-trace/test/profiling/**/*.spec.js\"",
34
34
  "test:profiler:ci": "npm run test:profiler -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/profiling/**/*.js\"",
35
35
  "test:integration": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/*.spec.js\"",
36
+ "test:integration:appsec": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/appsec/*.spec.js\"",
36
37
  "test:integration:cucumber": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cucumber/*.spec.js\"",
37
38
  "test:integration:cypress": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cypress/*.spec.js\"",
38
39
  "test:integration:jest": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/jest/*.spec.js\"",
@@ -45,9 +46,7 @@
45
46
  "test:integration:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
46
47
  "test:unit:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
47
48
  "test:shimmer": "mocha 'packages/datadog-shimmer/test/**/*.spec.js'",
48
- "test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer",
49
- "leak:core": "node ./scripts/install_plugin_modules && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape 'packages/dd-trace/test/leak/**/*.js'",
50
- "leak:plugins": "yarn services && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape \"packages/datadog-plugin-@($(echo $PLUGINS))/test/leak.js\""
49
+ "test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer"
51
50
  },
52
51
  "repository": {
53
52
  "type": "git",
@@ -74,7 +73,7 @@
74
73
  },
75
74
  "dependencies": {
76
75
  "@datadog/native-appsec": "8.0.1",
77
- "@datadog/native-iast-rewriter": "2.3.1",
76
+ "@datadog/native-iast-rewriter": "2.4.0",
78
77
  "@datadog/native-iast-taint-tracking": "3.0.0",
79
78
  "@datadog/native-metrics": "^2.0.0",
80
79
  "@datadog/pprof": "5.3.0",
@@ -116,13 +115,12 @@
116
115
  "cli-table3": "^0.6.3",
117
116
  "dotenv": "16.3.1",
118
117
  "esbuild": "0.16.12",
119
- "eslint": "^8.23.0",
118
+ "eslint": "^8.57.0",
120
119
  "eslint-config-standard": "^17.1.0",
121
- "eslint-plugin-import": "^2.8.0",
122
- "eslint-plugin-mocha": "^10.1.0",
123
- "eslint-plugin-n": "^15.7.0",
124
- "eslint-plugin-promise": "^3.6.0",
125
- "eslint-plugin-standard": "^3.0.1",
120
+ "eslint-plugin-import": "^2.29.1",
121
+ "eslint-plugin-mocha": "^10.4.3",
122
+ "eslint-plugin-n": "^16.6.2",
123
+ "eslint-plugin-promise": "^6.4.0",
126
124
  "express": "^4.18.2",
127
125
  "get-port": "^3.2.0",
128
126
  "glob": "^7.1.6",
@@ -139,7 +137,6 @@
139
137
  "sinon": "^15.2.0",
140
138
  "sinon-chai": "^3.7.0",
141
139
  "tap": "^16.3.7",
142
- "tape": "^5.6.5",
143
140
  "tiktoken": "^1.0.15"
144
141
  }
145
142
  }
@@ -39,10 +39,10 @@ function normalizeArgs (args, shell) {
39
39
 
40
40
  if (Array.isArray(args[1])) {
41
41
  childProcessInfo.command = childProcessInfo.command + ' ' + args[1].join(' ')
42
- if (args[2] != null && typeof args[2] === 'object') {
42
+ if (args[2] !== null && typeof args[2] === 'object') {
43
43
  childProcessInfo.options = args[2]
44
44
  }
45
- } else if (args[1] != null && typeof args[1] === 'object') {
45
+ } else if (args[1] !== null && typeof args[1] === 'object') {
46
46
  childProcessInfo.options = args[1]
47
47
  }
48
48
  childProcessInfo.shell = shell ||
@@ -272,7 +272,7 @@ function createWrapFunction (prefix = '', override = '') {
272
272
  const outerResource = new AsyncResource('bound-anonymous-fn')
273
273
 
274
274
  arguments[lastIndex] = innerResource.bind(function (e) {
275
- if (typeof e === 'object') { // fs.exists receives a boolean
275
+ if (e !== null && typeof e === 'object') { // fs.exists receives a boolean
276
276
  errorChannel.publish(e)
277
277
  }
278
278
 
@@ -38,7 +38,7 @@ function wrapStart (start) {
38
38
 
39
39
  function wrapExt (ext) {
40
40
  return function (events, method, options) {
41
- if (typeof events === 'object') {
41
+ if (events !== null && typeof events === 'object') {
42
42
  arguments[0] = wrapEvents(events)
43
43
  } else {
44
44
  arguments[1] = wrapExtension(method)
@@ -29,6 +29,10 @@ if (!disabledInstrumentations.has('fetch')) {
29
29
  require('../fetch')
30
30
  }
31
31
 
32
+ if (!disabledInstrumentations.has('process')) {
33
+ require('../process')
34
+ }
35
+
32
36
  const HOOK_SYMBOL = Symbol('hookExportsMap')
33
37
 
34
38
  if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') {
@@ -88,16 +92,13 @@ for (const packageName of names) {
88
92
  if (matchesFile) {
89
93
  const version = moduleVersion || getVersion(moduleBaseDir)
90
94
  if (!Object.hasOwnProperty(namesAndSuccesses, name)) {
91
- namesAndSuccesses[name] = {
92
- success: false,
93
- version
94
- }
95
+ namesAndSuccesses[`${name}@${version}`] = false
95
96
  }
96
97
 
97
98
  if (matchVersion(version, versions)) {
98
99
  // Check if the hook already has a set moduleExport
99
100
  if (hook[HOOK_SYMBOL].has(moduleExports)) {
100
- namesAndSuccesses[name].success = true
101
+ namesAndSuccesses[`${name}@${version}`] = true
101
102
  return moduleExports
102
103
  }
103
104
 
@@ -117,19 +118,20 @@ for (const packageName of names) {
117
118
  `integration_version:${version}`
118
119
  ])
119
120
  }
120
- namesAndSuccesses[name].success = true
121
+ namesAndSuccesses[`${name}@${version}`] = true
121
122
  }
122
123
  }
123
124
  }
124
- for (const name of Object.keys(namesAndSuccesses)) {
125
- const { success, version } = namesAndSuccesses[name]
126
- if (!success && !seenCombo.has(`${name}@${version}`)) {
125
+ for (const nameVersion of Object.keys(namesAndSuccesses)) {
126
+ const [name, version] = nameVersion.split('@')
127
+ const success = namesAndSuccesses[nameVersion]
128
+ if (!success && !seenCombo.has(nameVersion)) {
127
129
  telemetry('abort.integration', [
128
130
  `integration:${name}`,
129
131
  `integration_version:${version}`
130
132
  ])
131
- log.info(`Found incompatible integration version: ${name}@${version}`)
132
- seenCombo.add(`${name}@${version}`)
133
+ log.info(`Found incompatible integration version: ${nameVersion}`)
134
+ seenCombo.add(nameVersion)
133
135
  }
134
136
  }
135
137
 
@@ -43,7 +43,9 @@ function patch (http, methodName) {
43
43
  return request.apply(this, arguments)
44
44
  }
45
45
 
46
- const ctx = { args, http }
46
+ const abortController = new AbortController()
47
+
48
+ const ctx = { args, http, abortController }
47
49
 
48
50
  return startChannel.runStores(ctx, () => {
49
51
  let finished = false
@@ -107,6 +109,10 @@ function patch (http, methodName) {
107
109
  return emit.apply(this, arguments)
108
110
  }
109
111
 
112
+ if (abortController.signal.aborted) {
113
+ req.destroy(abortController.signal.reason || new Error('Aborted'))
114
+ }
115
+
110
116
  return req
111
117
  } catch (e) {
112
118
  ctx.error = e
@@ -132,7 +138,7 @@ function patch (http, methodName) {
132
138
  }
133
139
 
134
140
  function combineOptions (inputURL, inputOptions) {
135
- if (typeof inputOptions === 'object') {
141
+ if (inputOptions !== null && typeof inputOptions === 'object') {
136
142
  return Object.assign(inputURL || {}, inputOptions)
137
143
  } else {
138
144
  return inputURL
@@ -12,6 +12,7 @@ const errorServerCh = channel('apm:http:server:request:error')
12
12
  const finishServerCh = channel('apm:http:server:request:finish')
13
13
  const startWriteHeadCh = channel('apm:http:server:response:writeHead:start')
14
14
  const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finish')
15
+ const startSetHeaderCh = channel('datadog:http:server:response:set-header:start')
15
16
 
16
17
  const requestFinishedSet = new WeakSet()
17
18
 
@@ -24,6 +25,12 @@ addHook({ name: httpNames }, http => {
24
25
  shimmer.wrap(http.ServerResponse.prototype, 'writeHead', wrapWriteHead)
25
26
  shimmer.wrap(http.ServerResponse.prototype, 'write', wrapWrite)
26
27
  shimmer.wrap(http.ServerResponse.prototype, 'end', wrapEnd)
28
+ shimmer.wrap(http.ServerResponse.prototype, 'setHeader', wrapSetHeader)
29
+ shimmer.wrap(http.ServerResponse.prototype, 'removeHeader', wrapAppendOrRemoveHeader)
30
+ // Added in node v16.17.0
31
+ if (http.ServerResponse.prototype.appendHeader) {
32
+ shimmer.wrap(http.ServerResponse.prototype, 'appendHeader', wrapAppendOrRemoveHeader)
33
+ }
27
34
  return http
28
35
  })
29
36
 
@@ -65,9 +72,7 @@ function wrapEmit (emit) {
65
72
  // TODO: should this always return true ?
66
73
  return this.listenerCount(eventName) > 0
67
74
  }
68
- if (finishSetHeaderCh.hasSubscribers) {
69
- wrapSetHeader(res)
70
- }
75
+
71
76
  return emit.apply(this, arguments)
72
77
  } catch (err) {
73
78
  errorServerCh.publish(err)
@@ -81,16 +86,6 @@ function wrapEmit (emit) {
81
86
  }
82
87
  }
83
88
 
84
- function wrapSetHeader (res) {
85
- shimmer.wrap(res, 'setHeader', setHeader => {
86
- return function (name, value) {
87
- const setHeaderResult = setHeader.apply(this, arguments)
88
- finishSetHeaderCh.publish({ name, value, res })
89
- return setHeaderResult
90
- }
91
- })
92
- }
93
-
94
89
  function wrapWriteHead (writeHead) {
95
90
  return function wrappedWriteHead (statusCode, reason, obj) {
96
91
  if (!startWriteHeadCh.hasSubscribers) {
@@ -159,6 +154,48 @@ function wrapWrite (write) {
159
154
  }
160
155
  }
161
156
 
157
+ function wrapSetHeader (setHeader) {
158
+ return function wrappedSetHeader (name, value) {
159
+ if (!startSetHeaderCh.hasSubscribers && !finishSetHeaderCh.hasSubscribers) {
160
+ return setHeader.apply(this, arguments)
161
+ }
162
+
163
+ if (startSetHeaderCh.hasSubscribers) {
164
+ const abortController = new AbortController()
165
+ startSetHeaderCh.publish({ res: this, abortController })
166
+
167
+ if (abortController.signal.aborted) {
168
+ return
169
+ }
170
+ }
171
+
172
+ const setHeaderResult = setHeader.apply(this, arguments)
173
+
174
+ if (finishSetHeaderCh.hasSubscribers) {
175
+ finishSetHeaderCh.publish({ name, value, res: this })
176
+ }
177
+
178
+ return setHeaderResult
179
+ }
180
+ }
181
+
182
+ function wrapAppendOrRemoveHeader (originalMethod) {
183
+ return function wrappedAppendOrRemoveHeader () {
184
+ if (!startSetHeaderCh.hasSubscribers) {
185
+ return originalMethod.apply(this, arguments)
186
+ }
187
+
188
+ const abortController = new AbortController()
189
+ startSetHeaderCh.publish({ res: this, abortController })
190
+
191
+ if (abortController.signal.aborted) {
192
+ return this
193
+ }
194
+
195
+ return originalMethod.apply(this, arguments)
196
+ }
197
+ }
198
+
162
199
  function wrapEnd (end) {
163
200
  return function wrappedEnd () {
164
201
  if (!startWriteHeadCh.hasSubscribers) {
@@ -12,7 +12,8 @@ const {
12
12
  getTestParametersString,
13
13
  addEfdStringToTestName,
14
14
  removeEfdStringFromTestName,
15
- getIsFaultyEarlyFlakeDetection
15
+ getIsFaultyEarlyFlakeDetection,
16
+ NUM_FAILED_TEST_RETRIES
16
17
  } = require('../../dd-trace/src/plugins/util/test')
17
18
  const {
18
19
  getFormattedJestTestParameters,
@@ -49,6 +50,9 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
49
50
  const CHILD_MESSAGE_CALL = 1
50
51
  // Maximum time we'll wait for the tracer to flush
51
52
  const FLUSH_TIMEOUT = 10000
53
+ // eslint-disable-next-line
54
+ // https://github.com/jestjs/jest/blob/41f842a46bb2691f828c3a5f27fc1d6290495b82/packages/jest-circus/src/types.ts#L9C8-L9C54
55
+ const RETRY_TIMES = Symbol.for('RETRY_TIMES')
52
56
 
53
57
  let skippableSuites = []
54
58
  let knownTests = {}
@@ -127,6 +131,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
127
131
  }
128
132
 
129
133
  this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
134
+ this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled
130
135
 
131
136
  if (this.isEarlyFlakeDetectionEnabled) {
132
137
  const hasKnownTests = !!knownTests.jest
@@ -140,6 +145,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
140
145
  this.isEarlyFlakeDetectionEnabled = false
141
146
  }
142
147
  }
148
+
149
+ if (this.isFlakyTestRetriesEnabled) {
150
+ const currentNumRetries = this.global[RETRY_TIMES]
151
+ if (!currentNumRetries) {
152
+ this.global[RETRY_TIMES] = NUM_FAILED_TEST_RETRIES
153
+ }
154
+ }
143
155
  }
144
156
 
145
157
  getHasSnapshotTests () {
@@ -218,6 +230,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
218
230
  retriedTestsToNumAttempts.set(originalTestName, numEfdRetry + 1)
219
231
  }
220
232
  }
233
+ const isJestRetry = event.test?.invocations > 1
221
234
  asyncResource.runInAsyncScope(() => {
222
235
  testStartCh.publish({
223
236
  name: removeEfdStringFromTestName(testName),
@@ -228,7 +241,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
228
241
  testParameters,
229
242
  frameworkVersion: jestVersion,
230
243
  isNew: isNewTest,
231
- isEfdRetry: numEfdRetry > 0
244
+ isEfdRetry: numEfdRetry > 0,
245
+ isJestRetry
232
246
  })
233
247
  originalTestFns.set(event.test, event.test.fn)
234
248
  event.test.fn = asyncResource.bind(event.test.fn)
@@ -758,6 +772,7 @@ addHook({
758
772
  _ddIsEarlyFlakeDetectionEnabled,
759
773
  _ddEarlyFlakeDetectionNumRetries,
760
774
  _ddRepositoryRoot,
775
+ _ddIsFlakyTestRetriesEnabled,
761
776
  ...restOfTestEnvironmentOptions
762
777
  } = testEnvironmentOptions
763
778
 
@@ -59,7 +59,7 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
59
59
  try {
60
60
  const { topic, messages = [] } = arguments[0]
61
61
  for (const message of messages) {
62
- if (typeof message === 'object') {
62
+ if (message !== null && typeof message === 'object') {
63
63
  message.headers = message.headers || {}
64
64
  }
65
65
  }
@@ -61,7 +61,7 @@ addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
61
61
  let filter
62
62
  if (isString(options)) {
63
63
  filter = options
64
- } else if (typeof options === 'object' && options.filter) {
64
+ } else if (options !== null && typeof options === 'object' && options.filter) {
65
65
  if (isString(options.filter)) {
66
66
  filter = options.filter
67
67
  }
@@ -78,7 +78,7 @@ addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
78
78
  const callback = arguments[callbackIndex]
79
79
  // eslint-disable-next-line n/handle-callback-err
80
80
  arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) {
81
- if (typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
81
+ if (corkedEmitter !== null && typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
82
82
  wrapEmitter(corkedEmitter)
83
83
  }
84
84
  callback.apply(this, arguments)
@@ -395,9 +395,13 @@ addHook({
395
395
  }
396
396
 
397
397
  const asyncResource = testFileToSuiteAr.get(suite.file)
398
- asyncResource.runInAsyncScope(() => {
399
- testSuiteFinishCh.publish(status)
400
- })
398
+ if (asyncResource) {
399
+ asyncResource.runInAsyncScope(() => {
400
+ testSuiteFinishCh.publish(status)
401
+ })
402
+ } else {
403
+ log.warn(() => `No AsyncResource found for suite ${suite.file}`)
404
+ }
401
405
  })
402
406
 
403
407
  return run.apply(this, arguments)
@@ -424,23 +428,29 @@ addHook({
424
428
  versions: ['>=6.0.0'],
425
429
  file: 'src/WorkerHandler.js'
426
430
  }, (workerHandlerPackage) => {
427
- shimmer.wrap(workerHandlerPackage.prototype, 'exec', exec => function (message, [testSuiteAbsolutePath]) {
431
+ shimmer.wrap(workerHandlerPackage.prototype, 'exec', exec => function (_, path) {
428
432
  if (!testStartCh.hasSubscribers) {
429
433
  return exec.apply(this, arguments)
430
434
  }
435
+ if (!path?.length) {
436
+ return exec.apply(this, arguments)
437
+ }
438
+ const [testSuiteAbsolutePath] = path
439
+ const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
431
440
 
432
- this.worker.on('message', function (message) {
441
+ function onMessage (message) {
433
442
  if (Array.isArray(message)) {
434
443
  const [messageCode, payload] = message
435
444
  if (messageCode === MOCHA_WORKER_TRACE_PAYLOAD_CODE) {
436
- testSessionAsyncResource.runInAsyncScope(() => {
445
+ testSuiteAsyncResource.runInAsyncScope(() => {
437
446
  workerReportTraceCh.publish(payload)
438
447
  })
439
448
  }
440
449
  }
441
- })
450
+ }
451
+
452
+ this.worker.on('message', onMessage)
442
453
 
443
- const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
444
454
  testSuiteAsyncResource.runInAsyncScope(() => {
445
455
  testSuiteStartCh.publish({
446
456
  testSuiteAbsolutePath
@@ -455,12 +465,14 @@ addHook({
455
465
  testSuiteAsyncResource.runInAsyncScope(() => {
456
466
  testSuiteFinishCh.publish(status)
457
467
  })
468
+ this.worker.off('message', onMessage)
458
469
  },
459
470
  (err) => {
460
471
  testSuiteAsyncResource.runInAsyncScope(() => {
461
472
  testSuiteErrorCh.publish(err)
462
473
  testSuiteFinishCh.publish('fail')
463
474
  })
475
+ this.worker.off('message', onMessage)
464
476
  }
465
477
  )
466
478
  return promise
@@ -469,6 +481,7 @@ addHook({
469
481
  testSuiteErrorCh.publish(err)
470
482
  testSuiteFinishCh.publish('fail')
471
483
  })
484
+ this.worker.off('message', onMessage)
472
485
  throw err
473
486
  }
474
487
  })
@@ -25,9 +25,9 @@ const methodsOptionalArgs = ['findOneAndUpdate']
25
25
  function getFilters (args, methodName) {
26
26
  const [arg0, arg1] = args
27
27
 
28
- const filters = arg0 && typeof arg0 === 'object' ? [arg0] : []
28
+ const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : []
29
29
 
30
- if (arg1 && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
30
+ if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
31
31
  filters.push(arg1)
32
32
  }
33
33
 
@@ -46,7 +46,7 @@ function wrapHandleApiRequest (handleApiRequest) {
46
46
  function wrapHandleApiRequestWithMatch (handleApiRequest) {
47
47
  return function (req, res, query, match) {
48
48
  return instrument(req, res, () => {
49
- const page = (typeof match === 'object' && typeof match.definition === 'object')
49
+ const page = (match !== null && typeof match === 'object' && typeof match.definition === 'object')
50
50
  ? match.definition.pathname
51
51
  : undefined
52
52