dd-trace 2.45.1 → 2.46.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.
- package/package.json +1 -1
- package/packages/datadog-esbuild/index.js +10 -4
- package/packages/datadog-instrumentations/src/cucumber.js +30 -11
- package/packages/datadog-instrumentations/src/jest.js +22 -11
- package/packages/datadog-instrumentations/src/mocha.js +30 -8
- package/packages/datadog-instrumentations/src/next.js +16 -14
- package/packages/datadog-plugin-cucumber/src/index.js +14 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +17 -8
- package/packages/datadog-plugin-jest/src/index.js +10 -2
- package/packages/datadog-plugin-jest/src/util.js +10 -4
- package/packages/datadog-plugin-mocha/src/index.js +14 -2
- package/packages/datadog-plugin-next/src/index.js +11 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +14 -5
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +120 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +0 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +22 -1
- package/packages/dd-trace/src/plugins/util/test.js +18 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +7 -5
- package/packages/dd-trace/src/proxy.js +3 -0
- package/packages/dd-trace/src/ritm.js +10 -2
package/package.json
CHANGED
|
@@ -50,12 +50,18 @@ for (const pkg of INSTRUMENTED) {
|
|
|
50
50
|
module.exports.name = 'datadog-esbuild'
|
|
51
51
|
|
|
52
52
|
module.exports.setup = function (build) {
|
|
53
|
+
const externalModules = new Set(build.initialOptions.external || [])
|
|
53
54
|
build.onResolve({ filter: /.*/ }, args => {
|
|
55
|
+
if (externalModules.has(args.path)) {
|
|
56
|
+
if (DEBUG) console.log(`EXTERNAL: ${args.path}`)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
let fullPathToModule
|
|
55
61
|
try {
|
|
56
62
|
fullPathToModule = dotFriendlyResolve(args.path, args.resolveDir)
|
|
57
63
|
} catch (err) {
|
|
58
|
-
console.warn(`Unable to find "${args.path}". Is the package dead code?`)
|
|
64
|
+
console.warn(`MISSING: Unable to find "${args.path}". Is the package dead code?`)
|
|
59
65
|
return
|
|
60
66
|
}
|
|
61
67
|
const extracted = extractPackageAndModulePath(fullPathToModule)
|
|
@@ -74,7 +80,7 @@ module.exports.setup = function (build) {
|
|
|
74
80
|
} catch (err) {
|
|
75
81
|
if (err.code === 'MODULE_NOT_FOUND') {
|
|
76
82
|
if (!internal) {
|
|
77
|
-
console.warn(`Unable to find "${extracted.pkg}/package.json". Is the package dead code?`)
|
|
83
|
+
console.warn(`MISSING: Unable to find "${extracted.pkg}/package.json". Is the package dead code?`)
|
|
78
84
|
}
|
|
79
85
|
return
|
|
80
86
|
} else {
|
|
@@ -84,7 +90,7 @@ module.exports.setup = function (build) {
|
|
|
84
90
|
|
|
85
91
|
const packageJson = require(pathToPackageJson)
|
|
86
92
|
|
|
87
|
-
if (DEBUG) console.log(`RESOLVE ${packageName}@${packageJson.version}`)
|
|
93
|
+
if (DEBUG) console.log(`RESOLVE: ${packageName}@${packageJson.version}`)
|
|
88
94
|
|
|
89
95
|
// https://esbuild.github.io/plugins/#on-resolve-arguments
|
|
90
96
|
return {
|
|
@@ -114,7 +120,7 @@ module.exports.setup = function (build) {
|
|
|
114
120
|
build.onLoad({ filter: /.*/, namespace: NAMESPACE }, args => {
|
|
115
121
|
const data = args.pluginData
|
|
116
122
|
|
|
117
|
-
if (DEBUG) console.log(`LOAD ${data.pkg}@${data.version}, pkg "${data.path}"`)
|
|
123
|
+
if (DEBUG) console.log(`LOAD: ${data.pkg}@${data.version}, pkg "${data.path}"`)
|
|
118
124
|
|
|
119
125
|
const path = data.raw !== data.pkg
|
|
120
126
|
? `${data.pkg}/${data.path}`
|
|
@@ -21,6 +21,8 @@ const skippableSuitesCh = channel('ci:cucumber:test-suite:skippable')
|
|
|
21
21
|
const sessionStartCh = channel('ci:cucumber:session:start')
|
|
22
22
|
const sessionFinishCh = channel('ci:cucumber:session:finish')
|
|
23
23
|
|
|
24
|
+
const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
|
|
25
|
+
|
|
24
26
|
const {
|
|
25
27
|
getCoveredFilenamesFromCoverage,
|
|
26
28
|
resetCoverage,
|
|
@@ -37,7 +39,6 @@ const patched = new WeakSet()
|
|
|
37
39
|
|
|
38
40
|
let pickleByFile = {}
|
|
39
41
|
const pickleResultByFile = {}
|
|
40
|
-
let isSuitesSkipped = false
|
|
41
42
|
|
|
42
43
|
function getSuiteStatusFromTestStatuses (testStatuses) {
|
|
43
44
|
if (testStatuses.some(status => status === 'fail')) {
|
|
@@ -216,11 +217,18 @@ addHook({
|
|
|
216
217
|
file: 'lib/runtime/test_case_runner.js'
|
|
217
218
|
}, testCaseHook)
|
|
218
219
|
|
|
219
|
-
function
|
|
220
|
-
return runtime.pickleIds.
|
|
220
|
+
function getFilteredPickles (runtime, suitesToSkip) {
|
|
221
|
+
return runtime.pickleIds.reduce((acc, pickleId) => {
|
|
221
222
|
const test = runtime.eventDataCollector.getPickle(pickleId)
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
const testSuitePath = getTestSuitePath(test.uri, process.cwd())
|
|
224
|
+
const isSkipped = suitesToSkip.includes(testSuitePath)
|
|
225
|
+
if (isSkipped) {
|
|
226
|
+
acc.skippedSuites.add(testSuitePath)
|
|
227
|
+
} else {
|
|
228
|
+
acc.picklesToRun.push(pickleId)
|
|
229
|
+
}
|
|
230
|
+
return acc
|
|
231
|
+
}, { skippedSuites: new Set(), picklesToRun: [] })
|
|
224
232
|
}
|
|
225
233
|
|
|
226
234
|
function getPickleByFile (runtime) {
|
|
@@ -239,7 +247,7 @@ addHook({
|
|
|
239
247
|
name: '@cucumber/cucumber',
|
|
240
248
|
versions: ['>=7.0.0'],
|
|
241
249
|
file: 'lib/runtime/index.js'
|
|
242
|
-
}, (runtimePackage,
|
|
250
|
+
}, (runtimePackage, frameworkVersion) => {
|
|
243
251
|
shimmer.wrap(runtimePackage.default.prototype, 'start', start => async function () {
|
|
244
252
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
245
253
|
let onDone
|
|
@@ -263,11 +271,16 @@ addHook({
|
|
|
263
271
|
})
|
|
264
272
|
|
|
265
273
|
const { err, skippableSuites } = await skippableSuitesPromise
|
|
274
|
+
let skippedSuites = []
|
|
275
|
+
let isSuitesSkipped = false
|
|
266
276
|
|
|
267
277
|
if (!err) {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
this.pickleIds
|
|
278
|
+
const filteredPickles = getFilteredPickles(this, skippableSuites)
|
|
279
|
+
const { picklesToRun } = filteredPickles
|
|
280
|
+
isSuitesSkipped = picklesToRun.length !== this.pickleIds.length
|
|
281
|
+
this.pickleIds = picklesToRun
|
|
282
|
+
|
|
283
|
+
skippedSuites = Array.from(filteredPickles.skippedSuites)
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
pickleByFile = getPickleByFile(this)
|
|
@@ -276,8 +289,13 @@ addHook({
|
|
|
276
289
|
const command = process.env.npm_lifecycle_script || `cucumber-js ${processArgv}`
|
|
277
290
|
|
|
278
291
|
asyncResource.runInAsyncScope(() => {
|
|
279
|
-
sessionStartCh.publish({ command, frameworkVersion
|
|
292
|
+
sessionStartCh.publish({ command, frameworkVersion })
|
|
280
293
|
})
|
|
294
|
+
|
|
295
|
+
if (!err && skippedSuites.length) {
|
|
296
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
297
|
+
}
|
|
298
|
+
|
|
281
299
|
const success = await start.apply(this, arguments)
|
|
282
300
|
|
|
283
301
|
let testCodeCoverageLinesTotal
|
|
@@ -296,7 +314,8 @@ addHook({
|
|
|
296
314
|
sessionFinishCh.publish({
|
|
297
315
|
status: success ? 'pass' : 'fail',
|
|
298
316
|
isSuitesSkipped,
|
|
299
|
-
testCodeCoverageLinesTotal
|
|
317
|
+
testCodeCoverageLinesTotal,
|
|
318
|
+
numSkippedSuites: skippedSuites.length
|
|
300
319
|
})
|
|
301
320
|
})
|
|
302
321
|
return success
|
|
@@ -39,10 +39,13 @@ const testErrCh = channel('ci:jest:test:err')
|
|
|
39
39
|
const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
|
|
40
40
|
const jestItrConfigurationCh = channel('ci:jest:itr-configuration')
|
|
41
41
|
|
|
42
|
+
const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
|
|
43
|
+
|
|
42
44
|
let skippableSuites = []
|
|
43
45
|
let isCodeCoverageEnabled = false
|
|
44
46
|
let isSuitesSkippingEnabled = false
|
|
45
47
|
let isSuitesSkipped = false
|
|
48
|
+
let numSkippedSuites = 0
|
|
46
49
|
|
|
47
50
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
48
51
|
|
|
@@ -102,7 +105,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
102
105
|
await super.handleTestEvent(event, state)
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
const setNameToParams = (name, params) => { this.nameToParams[name] = params }
|
|
108
|
+
const setNameToParams = (name, params) => { this.nameToParams[name] = [...params] }
|
|
106
109
|
|
|
107
110
|
if (event.name === 'setup') {
|
|
108
111
|
if (this.global.test) {
|
|
@@ -191,7 +194,7 @@ addHook({
|
|
|
191
194
|
addHook({
|
|
192
195
|
name: '@jest/test-sequencer',
|
|
193
196
|
versions: ['>=24.8.0']
|
|
194
|
-
}, sequencerPackage => {
|
|
197
|
+
}, (sequencerPackage, frameworkVersion) => {
|
|
195
198
|
shimmer.wrap(sequencerPackage.default.prototype, 'shard', shard => function () {
|
|
196
199
|
const shardedTests = shard.apply(this, arguments)
|
|
197
200
|
|
|
@@ -202,13 +205,15 @@ addHook({
|
|
|
202
205
|
const [test] = shardedTests
|
|
203
206
|
const rootDir = test && test.context && test.context.config && test.context.config.rootDir
|
|
204
207
|
|
|
205
|
-
const
|
|
208
|
+
const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, shardedTests, rootDir || process.cwd())
|
|
206
209
|
|
|
207
|
-
isSuitesSkipped =
|
|
210
|
+
isSuitesSkipped = suitesToRun.length !== shardedTests.length
|
|
211
|
+
numSkippedSuites = skippedSuites.length
|
|
208
212
|
|
|
209
|
-
|
|
213
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
210
214
|
|
|
211
|
-
|
|
215
|
+
skippableSuites = []
|
|
216
|
+
return suitesToRun
|
|
212
217
|
})
|
|
213
218
|
return sequencerPackage
|
|
214
219
|
})
|
|
@@ -279,10 +284,13 @@ function cliWrapper (cli, jestVersion) {
|
|
|
279
284
|
isSuitesSkipped,
|
|
280
285
|
isSuitesSkippingEnabled,
|
|
281
286
|
isCodeCoverageEnabled,
|
|
282
|
-
testCodeCoverageLinesTotal
|
|
287
|
+
testCodeCoverageLinesTotal,
|
|
288
|
+
numSkippedSuites
|
|
283
289
|
})
|
|
284
290
|
})
|
|
285
291
|
|
|
292
|
+
numSkippedSuites = 0
|
|
293
|
+
|
|
286
294
|
return result
|
|
287
295
|
})
|
|
288
296
|
|
|
@@ -468,7 +476,7 @@ addHook({
|
|
|
468
476
|
name: '@jest/core',
|
|
469
477
|
versions: ['>=24.8.0'],
|
|
470
478
|
file: 'build/SearchSource.js'
|
|
471
|
-
}, searchSourcePackage => {
|
|
479
|
+
}, (searchSourcePackage, frameworkVersion) => {
|
|
472
480
|
const SearchSource = searchSourcePackage.default ? searchSourcePackage.default : searchSourcePackage
|
|
473
481
|
|
|
474
482
|
shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
|
|
@@ -492,13 +500,16 @@ addHook({
|
|
|
492
500
|
const testPaths = await getTestPaths.apply(this, arguments)
|
|
493
501
|
const { tests } = testPaths
|
|
494
502
|
|
|
495
|
-
const
|
|
503
|
+
const { skippedSuites, suitesToRun } = getJestSuitesToRun(skippableSuites, tests, rootDir)
|
|
504
|
+
|
|
505
|
+
isSuitesSkipped = suitesToRun.length !== tests.length
|
|
506
|
+
numSkippedSuites = skippedSuites.length
|
|
496
507
|
|
|
497
|
-
|
|
508
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
498
509
|
|
|
499
510
|
skippableSuites = []
|
|
500
511
|
|
|
501
|
-
return { ...testPaths, tests:
|
|
512
|
+
return { ...testPaths, tests: suitesToRun }
|
|
502
513
|
})
|
|
503
514
|
|
|
504
515
|
return searchSourcePackage
|
|
@@ -30,6 +30,8 @@ const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
|
|
|
30
30
|
const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
|
|
31
31
|
const testSuiteCodeCoverageCh = channel('ci:mocha:test-suite:code-coverage')
|
|
32
32
|
|
|
33
|
+
const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
|
|
34
|
+
|
|
33
35
|
// TODO: remove when root hooks and fixtures are implemented
|
|
34
36
|
const patched = new WeakSet()
|
|
35
37
|
|
|
@@ -47,6 +49,7 @@ const originalCoverageMap = createCoverageMap()
|
|
|
47
49
|
let suitesToSkip = []
|
|
48
50
|
let frameworkVersion
|
|
49
51
|
let isSuitesSkipped = false
|
|
52
|
+
let skippedSuites = []
|
|
50
53
|
|
|
51
54
|
function getSuitesByTestFile (root) {
|
|
52
55
|
const suitesByTestFile = {}
|
|
@@ -97,10 +100,17 @@ function getTestAsyncResource (test) {
|
|
|
97
100
|
return testToAr.get(originalFn)
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
function
|
|
101
|
-
return originalSuites.
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
function getFilteredSuites (originalSuites) {
|
|
104
|
+
return originalSuites.reduce((acc, suite) => {
|
|
105
|
+
const testPath = getTestSuitePath(suite.file, process.cwd())
|
|
106
|
+
const shouldSkip = suitesToSkip.includes(testPath)
|
|
107
|
+
if (shouldSkip) {
|
|
108
|
+
acc.skippedSuites.add(testPath)
|
|
109
|
+
} else {
|
|
110
|
+
acc.suitesToRun.push(suite)
|
|
111
|
+
}
|
|
112
|
+
return acc
|
|
113
|
+
}, { suitesToRun: [], skippedSuites: new Set() })
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
function mochaHook (Runner) {
|
|
@@ -137,13 +147,21 @@ function mochaHook (Runner) {
|
|
|
137
147
|
global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
testSessionFinishCh.publish({
|
|
150
|
+
testSessionFinishCh.publish({
|
|
151
|
+
status,
|
|
152
|
+
isSuitesSkipped,
|
|
153
|
+
testCodeCoverageLinesTotal,
|
|
154
|
+
numSkippedSuites: skippedSuites.length
|
|
155
|
+
})
|
|
141
156
|
}))
|
|
142
157
|
|
|
143
158
|
this.once('start', testRunAsyncResource.bind(function () {
|
|
144
159
|
const processArgv = process.argv.slice(2).join(' ')
|
|
145
160
|
const command = `mocha ${processArgv}`
|
|
146
161
|
testSessionStartCh.publish({ command, frameworkVersion })
|
|
162
|
+
if (skippedSuites.length) {
|
|
163
|
+
itrSkippedSuitesCh.publish({ skippedSuites, frameworkVersion })
|
|
164
|
+
}
|
|
147
165
|
}))
|
|
148
166
|
|
|
149
167
|
this.on('suite', function (suite) {
|
|
@@ -359,9 +377,13 @@ addHook({
|
|
|
359
377
|
suitesToSkip = skippableSuites
|
|
360
378
|
}
|
|
361
379
|
// We remove the suites that we skip through ITR
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
380
|
+
const filteredSuites = getFilteredSuites(runner.suite.suites)
|
|
381
|
+
const { suitesToRun } = filteredSuites
|
|
382
|
+
|
|
383
|
+
isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length
|
|
384
|
+
runner.suite.suites = suitesToRun
|
|
385
|
+
|
|
386
|
+
skippedSuites = Array.from(filteredSuites.skippedSuites)
|
|
365
387
|
|
|
366
388
|
global.run()
|
|
367
389
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// TODO: either instrument all or none of the render functions
|
|
4
4
|
|
|
5
|
-
const { channel, addHook
|
|
5
|
+
const { channel, addHook } = require('./helpers/instrument')
|
|
6
6
|
const shimmer = require('../../datadog-shimmer')
|
|
7
7
|
const { DD_MAJOR } = require('../../../version')
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ const finishChannel = channel('apm:next:request:finish')
|
|
|
11
11
|
const errorChannel = channel('apm:next:request:error')
|
|
12
12
|
const pageLoadChannel = channel('apm:next:page:load')
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const requests = new WeakSet()
|
|
15
15
|
|
|
16
16
|
function wrapHandleRequest (handleRequest) {
|
|
17
17
|
return function (req, res, pathname, query) {
|
|
@@ -105,38 +105,40 @@ function getPageFromPath (page, dynamicRoutes = []) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function instrument (req, res, handler) {
|
|
108
|
-
|
|
108
|
+
req = req.originalRequest || req
|
|
109
|
+
res = res.originalResponse || res
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
if (requests.has(req)) return handler()
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
requests.add(req)
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
startChannel.publish({ req, res })
|
|
115
|
+
const ctx = { req, res }
|
|
116
116
|
|
|
117
|
+
return startChannel.runStores(ctx, () => {
|
|
117
118
|
try {
|
|
118
|
-
const promise = handler()
|
|
119
|
+
const promise = handler(ctx)
|
|
119
120
|
|
|
120
121
|
// promise should only reject when propagateError is true:
|
|
121
122
|
// https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L547
|
|
122
123
|
return promise.then(
|
|
123
|
-
result => finish(
|
|
124
|
-
err => finish(
|
|
124
|
+
result => finish(ctx, result),
|
|
125
|
+
err => finish(ctx, null, err)
|
|
125
126
|
)
|
|
126
127
|
} catch (e) {
|
|
127
128
|
// this will probably never happen as the handler caller is an async function:
|
|
128
129
|
// https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L420
|
|
129
|
-
return finish(
|
|
130
|
+
return finish(ctx, null, e)
|
|
130
131
|
}
|
|
131
132
|
})
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
function finish (
|
|
135
|
+
function finish (ctx, result, err) {
|
|
135
136
|
if (err) {
|
|
136
|
-
|
|
137
|
+
ctx.error = err
|
|
138
|
+
errorChannel.publish(ctx)
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
finishChannel.publish(
|
|
141
|
+
finishChannel.publish(ctx)
|
|
140
142
|
|
|
141
143
|
if (err) {
|
|
142
144
|
throw err
|
|
@@ -25,12 +25,24 @@ class CucumberPlugin extends CiPlugin {
|
|
|
25
25
|
|
|
26
26
|
this.sourceRoot = process.cwd()
|
|
27
27
|
|
|
28
|
-
this.addSub('ci:cucumber:session:finish', ({
|
|
28
|
+
this.addSub('ci:cucumber:session:finish', ({
|
|
29
|
+
status,
|
|
30
|
+
isSuitesSkipped,
|
|
31
|
+
numSkippedSuites,
|
|
32
|
+
testCodeCoverageLinesTotal
|
|
33
|
+
}) => {
|
|
29
34
|
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.itrConfig || {}
|
|
30
35
|
addIntelligentTestRunnerSpanTags(
|
|
31
36
|
this.testSessionSpan,
|
|
32
37
|
this.testModuleSpan,
|
|
33
|
-
{
|
|
38
|
+
{
|
|
39
|
+
isSuitesSkipped,
|
|
40
|
+
isSuitesSkippingEnabled,
|
|
41
|
+
isCodeCoverageEnabled,
|
|
42
|
+
testCodeCoverageLinesTotal,
|
|
43
|
+
skippingCount: numSkippedSuites,
|
|
44
|
+
skippingType: 'suite'
|
|
45
|
+
}
|
|
34
46
|
)
|
|
35
47
|
|
|
36
48
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
@@ -20,7 +20,8 @@ const {
|
|
|
20
20
|
finishAllTraceSpans,
|
|
21
21
|
getCoveredFilenamesFromCoverage,
|
|
22
22
|
getTestSuitePath,
|
|
23
|
-
addIntelligentTestRunnerSpanTags
|
|
23
|
+
addIntelligentTestRunnerSpanTags,
|
|
24
|
+
TEST_SKIPPED_BY_ITR
|
|
24
25
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
25
26
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
26
27
|
const log = require('../../dd-trace/src/log')
|
|
@@ -120,6 +121,7 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
|
120
121
|
|
|
121
122
|
module.exports = (on, config) => {
|
|
122
123
|
let isTestsSkipped = false
|
|
124
|
+
const skippedTests = []
|
|
123
125
|
const tracer = require('../../dd-trace')
|
|
124
126
|
const testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
|
|
125
127
|
|
|
@@ -248,19 +250,23 @@ module.exports = (on, config) => {
|
|
|
248
250
|
const cypressTests = tests || []
|
|
249
251
|
const finishedTests = finishedTestsByFile[spec.relative] || []
|
|
250
252
|
|
|
251
|
-
// Get tests that didn't go through `dd:afterEach`
|
|
253
|
+
// Get tests that didn't go through `dd:afterEach`
|
|
252
254
|
// and create a skipped test span for each of them
|
|
253
255
|
cypressTests.filter(({ title }) => {
|
|
254
256
|
const cypressTestName = title.join(' ')
|
|
255
|
-
const isSkippedByItr = testsToSkip.find(test =>
|
|
256
|
-
cypressTestName === test.name && spec.relative === test.suite
|
|
257
|
-
)
|
|
258
257
|
const isTestFinished = finishedTests.find(({ testName }) => cypressTestName === testName)
|
|
259
258
|
|
|
260
|
-
return !
|
|
259
|
+
return !isTestFinished
|
|
261
260
|
}).forEach(({ title }) => {
|
|
262
|
-
const
|
|
261
|
+
const cypressTestName = title.join(' ')
|
|
262
|
+
const isSkippedByItr = testsToSkip.find(test =>
|
|
263
|
+
cypressTestName === test.name && spec.relative === test.suite
|
|
264
|
+
)
|
|
265
|
+
const skippedTestSpan = getTestSpan(cypressTestName, spec.relative)
|
|
263
266
|
skippedTestSpan.setTag(TEST_STATUS, 'skip')
|
|
267
|
+
if (isSkippedByItr) {
|
|
268
|
+
skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
|
|
269
|
+
}
|
|
264
270
|
skippedTestSpan.finish()
|
|
265
271
|
})
|
|
266
272
|
|
|
@@ -309,7 +315,9 @@ module.exports = (on, config) => {
|
|
|
309
315
|
{
|
|
310
316
|
isSuitesSkipped: isTestsSkipped,
|
|
311
317
|
isSuitesSkippingEnabled,
|
|
312
|
-
isCodeCoverageEnabled
|
|
318
|
+
isCodeCoverageEnabled,
|
|
319
|
+
skippingType: 'test',
|
|
320
|
+
skippingCount: skippedTests.length
|
|
313
321
|
}
|
|
314
322
|
)
|
|
315
323
|
|
|
@@ -353,6 +361,7 @@ module.exports = (on, config) => {
|
|
|
353
361
|
if (testsToSkip.find(test => {
|
|
354
362
|
return testName === test.name && testSuite === test.suite
|
|
355
363
|
})) {
|
|
364
|
+
skippedTests.push(test)
|
|
356
365
|
isTestsSkipped = true
|
|
357
366
|
return { shouldSkip: true }
|
|
358
367
|
}
|
|
@@ -49,7 +49,8 @@ class JestPlugin extends CiPlugin {
|
|
|
49
49
|
isSuitesSkipped,
|
|
50
50
|
isSuitesSkippingEnabled,
|
|
51
51
|
isCodeCoverageEnabled,
|
|
52
|
-
testCodeCoverageLinesTotal
|
|
52
|
+
testCodeCoverageLinesTotal,
|
|
53
|
+
numSkippedSuites
|
|
53
54
|
}) => {
|
|
54
55
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
55
56
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
@@ -57,7 +58,14 @@ class JestPlugin extends CiPlugin {
|
|
|
57
58
|
addIntelligentTestRunnerSpanTags(
|
|
58
59
|
this.testSessionSpan,
|
|
59
60
|
this.testModuleSpan,
|
|
60
|
-
{
|
|
61
|
+
{
|
|
62
|
+
isSuitesSkipped,
|
|
63
|
+
isSuitesSkippingEnabled,
|
|
64
|
+
isCodeCoverageEnabled,
|
|
65
|
+
testCodeCoverageLinesTotal,
|
|
66
|
+
skippingType: 'suite',
|
|
67
|
+
skippingCount: numSkippedSuites
|
|
68
|
+
}
|
|
61
69
|
)
|
|
62
70
|
|
|
63
71
|
this.testModuleSpan.finish()
|
|
@@ -48,10 +48,16 @@ function getJestTestName (test) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function getJestSuitesToRun (skippableSuites, originalTests, rootDir) {
|
|
51
|
-
return originalTests.
|
|
52
|
-
const relativePath = getTestSuitePath(
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
return originalTests.reduce((acc, test) => {
|
|
52
|
+
const relativePath = getTestSuitePath(test.path, rootDir)
|
|
53
|
+
const shouldBeSkipped = skippableSuites.includes(relativePath)
|
|
54
|
+
if (shouldBeSkipped) {
|
|
55
|
+
acc.skippedSuites.push(relativePath)
|
|
56
|
+
} else {
|
|
57
|
+
acc.suitesToRun.push(test)
|
|
58
|
+
}
|
|
59
|
+
return acc
|
|
60
|
+
}, { skippedSuites: [], suitesToRun: [] })
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
module.exports = { getFormattedJestTestParameters, getJestTestName, getJestSuitesToRun }
|
|
@@ -135,7 +135,12 @@ class MochaPlugin extends CiPlugin {
|
|
|
135
135
|
this._testNameToParams[name] = params
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
-
this.addSub('ci:mocha:session:finish', ({
|
|
138
|
+
this.addSub('ci:mocha:session:finish', ({
|
|
139
|
+
status,
|
|
140
|
+
isSuitesSkipped,
|
|
141
|
+
testCodeCoverageLinesTotal,
|
|
142
|
+
numSkippedSuites
|
|
143
|
+
}) => {
|
|
139
144
|
if (this.testSessionSpan) {
|
|
140
145
|
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.itrConfig || {}
|
|
141
146
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
@@ -144,7 +149,14 @@ class MochaPlugin extends CiPlugin {
|
|
|
144
149
|
addIntelligentTestRunnerSpanTags(
|
|
145
150
|
this.testSessionSpan,
|
|
146
151
|
this.testModuleSpan,
|
|
147
|
-
{
|
|
152
|
+
{
|
|
153
|
+
isSuitesSkipped,
|
|
154
|
+
isSuitesSkippingEnabled,
|
|
155
|
+
isCodeCoverageEnabled,
|
|
156
|
+
testCodeCoverageLinesTotal,
|
|
157
|
+
skippingCount: numSkippedSuites,
|
|
158
|
+
skippingType: 'suite'
|
|
159
|
+
}
|
|
148
160
|
)
|
|
149
161
|
|
|
150
162
|
this.testModuleSpan.finish()
|
|
@@ -4,6 +4,7 @@ const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
6
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
7
|
+
const web = require('../../dd-trace/src/plugins/util/web')
|
|
7
8
|
|
|
8
9
|
class NextPlugin extends ServerPlugin {
|
|
9
10
|
static get id () {
|
|
@@ -16,7 +17,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
16
17
|
this.addSub('apm:next:page:load', message => this.pageLoad(message))
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
bindStart ({ req, res }) {
|
|
20
21
|
const store = storage.getStore()
|
|
21
22
|
const childOf = store ? store.span : store
|
|
22
23
|
const span = this.tracer.startSpan(this.operationName(), {
|
|
@@ -33,9 +34,13 @@ class NextPlugin extends ServerPlugin {
|
|
|
33
34
|
|
|
34
35
|
analyticsSampler.sample(span, this.config.measured, true)
|
|
35
36
|
|
|
36
|
-
this.enter(span, store)
|
|
37
|
-
|
|
38
37
|
this._requests.set(span, req)
|
|
38
|
+
|
|
39
|
+
return { ...store, span }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
error ({ span, error }) {
|
|
43
|
+
this.addError(error, span)
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
finish ({ req, res }) {
|
|
@@ -45,6 +50,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
45
50
|
|
|
46
51
|
const span = store.span
|
|
47
52
|
const error = span.context()._tags['error']
|
|
53
|
+
const page = span.context()._tags['next.page']
|
|
48
54
|
|
|
49
55
|
if (!this.config.validateStatus(res.statusCode) && !error) {
|
|
50
56
|
span.setTag('error', true)
|
|
@@ -54,6 +60,8 @@ class NextPlugin extends ServerPlugin {
|
|
|
54
60
|
'http.status_code': res.statusCode
|
|
55
61
|
})
|
|
56
62
|
|
|
63
|
+
if (page) web.setRoute(req, page)
|
|
64
|
+
|
|
57
65
|
this.config.hooks.request(span, req, res)
|
|
58
66
|
|
|
59
67
|
span.finish()
|
|
@@ -66,7 +66,7 @@ function taintObject (iastContext, object, type, keyTainting, keyType) {
|
|
|
66
66
|
const taintedProperty = TaintedUtils.newTaintedString(transactionId, key, property, keyType)
|
|
67
67
|
parent[taintedProperty] = tainted
|
|
68
68
|
} else {
|
|
69
|
-
parent[
|
|
69
|
+
parent[key] = tainted
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
} else if (typeof value === 'object' && !visited.has(value)) {
|
|
@@ -5,11 +5,20 @@ const telemetryLogs = require('./log')
|
|
|
5
5
|
const { Verbosity, getVerbosity } = require('./verbosity')
|
|
6
6
|
const { initRequestNamespace, finalizeRequestNamespace, globalNamespace } = require('./namespaces')
|
|
7
7
|
|
|
8
|
+
function isIastMetricsEnabled (metrics) {
|
|
9
|
+
// TODO: let DD_TELEMETRY_METRICS_ENABLED as undefined in config.js to avoid read here the env property
|
|
10
|
+
return process.env.DD_TELEMETRY_METRICS_ENABLED !== undefined ? metrics : true
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
class Telemetry {
|
|
9
14
|
configure (config, verbosity) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
const telemetryAndMetricsEnabled = config &&
|
|
16
|
+
config.telemetry &&
|
|
17
|
+
config.telemetry.enabled &&
|
|
18
|
+
isIastMetricsEnabled(config.telemetry.metrics)
|
|
19
|
+
|
|
20
|
+
this.verbosity = telemetryAndMetricsEnabled ? getVerbosity(verbosity) : Verbosity.OFF
|
|
21
|
+
this.enabled = this.verbosity !== Verbosity.OFF
|
|
13
22
|
|
|
14
23
|
if (this.enabled) {
|
|
15
24
|
telemetryMetrics.manager.set('iast', globalNamespace)
|
|
@@ -30,13 +39,13 @@ class Telemetry {
|
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
onRequestStart (context) {
|
|
33
|
-
if (this.isEnabled()
|
|
42
|
+
if (this.isEnabled()) {
|
|
34
43
|
initRequestNamespace(context)
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
onRequestEnd (context, rootSpan) {
|
|
39
|
-
if (this.isEnabled()
|
|
48
|
+
if (this.isEnabled()) {
|
|
40
49
|
finalizeRequestNamespace(context, rootSpan)
|
|
41
50
|
}
|
|
42
51
|
}
|
|
@@ -14,6 +14,8 @@ const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phra
|
|
|
14
14
|
// eslint-disable-next-line max-len
|
|
15
15
|
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|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[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,}))'
|
|
16
16
|
|
|
17
|
+
const REDACTED_SOURCE_BUFFER = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
18
|
+
|
|
17
19
|
class SensitiveHandler {
|
|
18
20
|
constructor () {
|
|
19
21
|
this._namePattern = new RegExp(DEFAULT_IAST_REDACTION_NAME_PATTERN, 'gmi')
|
|
@@ -54,6 +56,7 @@ class SensitiveHandler {
|
|
|
54
56
|
toRedactedJson (evidence, sensitive, sourcesIndexes, sources) {
|
|
55
57
|
const valueParts = []
|
|
56
58
|
const redactedSources = []
|
|
59
|
+
const redactedSourcesContext = []
|
|
57
60
|
|
|
58
61
|
const { value, ranges } = evidence
|
|
59
62
|
|
|
@@ -71,21 +74,41 @@ class SensitiveHandler {
|
|
|
71
74
|
sourceIndex = sourcesIndexes[nextTaintedIndex]
|
|
72
75
|
|
|
73
76
|
while (nextSensitive != null && contains(nextTainted, nextSensitive)) {
|
|
74
|
-
|
|
77
|
+
const redactionStart = nextSensitive.start - nextTainted.start
|
|
78
|
+
const redactionEnd = nextSensitive.end - nextTainted.start
|
|
79
|
+
this.redactSource(sources, redactedSources, redactedSourcesContext, sourceIndex, redactionStart, redactionEnd)
|
|
75
80
|
nextSensitive = sensitive.shift()
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
if (nextSensitive != null && intersects(nextSensitive, nextTainted)) {
|
|
79
|
-
|
|
84
|
+
const redactionStart = nextSensitive.start - nextTainted.start
|
|
85
|
+
const redactionEnd = nextSensitive.end - nextTainted.start
|
|
86
|
+
this.redactSource(sources, redactedSources, redactedSourcesContext, sourceIndex, redactionStart, redactionEnd)
|
|
80
87
|
|
|
81
88
|
const entries = remove(nextSensitive, nextTainted)
|
|
82
89
|
nextSensitive = entries.length > 0 ? entries[0] : null
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
this.isSensibleSource(sources[sourceIndex])
|
|
92
|
+
if (this.isSensibleSource(sources[sourceIndex])) {
|
|
93
|
+
if (!sources[sourceIndex].redacted) {
|
|
94
|
+
redactedSources.push(sourceIndex)
|
|
95
|
+
sources[sourceIndex].pattern = ''.padEnd(sources[sourceIndex].value.length, REDACTED_SOURCE_BUFFER)
|
|
96
|
+
sources[sourceIndex].redacted = true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
86
99
|
|
|
87
100
|
if (redactedSources.indexOf(sourceIndex) > -1) {
|
|
88
|
-
|
|
101
|
+
const partValue = value.substring(i, i + (nextTainted.end - nextTainted.start))
|
|
102
|
+
this.writeRedactedValuePart(
|
|
103
|
+
valueParts,
|
|
104
|
+
partValue.length,
|
|
105
|
+
sourceIndex,
|
|
106
|
+
partValue,
|
|
107
|
+
sources[sourceIndex],
|
|
108
|
+
redactedSourcesContext[sourceIndex],
|
|
109
|
+
this.isSensibleSource(sources[sourceIndex])
|
|
110
|
+
)
|
|
111
|
+
redactedSourcesContext[sourceIndex] = []
|
|
89
112
|
} else {
|
|
90
113
|
const substringEnd = Math.min(nextTainted.end, value.length)
|
|
91
114
|
this.writeValuePart(valueParts, value.substring(nextTainted.start, substringEnd), sourceIndex)
|
|
@@ -100,7 +123,10 @@ class SensitiveHandler {
|
|
|
100
123
|
this.writeValuePart(valueParts, value.substring(start, i), sourceIndex)
|
|
101
124
|
if (nextTainted != null && intersects(nextSensitive, nextTainted)) {
|
|
102
125
|
sourceIndex = sourcesIndexes[nextTaintedIndex]
|
|
103
|
-
|
|
126
|
+
|
|
127
|
+
const redactionStart = nextSensitive.start - nextTainted.start
|
|
128
|
+
const redactionEnd = nextSensitive.end - nextTainted.start
|
|
129
|
+
this.redactSource(sources, redactedSources, redactedSourcesContext, sourceIndex, redactionStart, redactionEnd)
|
|
104
130
|
|
|
105
131
|
for (const entry of remove(nextSensitive, nextTainted)) {
|
|
106
132
|
if (entry.start === i) {
|
|
@@ -111,9 +137,10 @@ class SensitiveHandler {
|
|
|
111
137
|
}
|
|
112
138
|
}
|
|
113
139
|
|
|
114
|
-
|
|
140
|
+
const _length = nextSensitive.end - nextSensitive.start
|
|
141
|
+
this.writeRedactedValuePart(valueParts, _length)
|
|
115
142
|
|
|
116
|
-
start = i +
|
|
143
|
+
start = i + _length
|
|
117
144
|
i = start - 1
|
|
118
145
|
nextSensitive = sensitive.shift()
|
|
119
146
|
}
|
|
@@ -126,6 +153,24 @@ class SensitiveHandler {
|
|
|
126
153
|
return { redactedValueParts: valueParts, redactedSources }
|
|
127
154
|
}
|
|
128
155
|
|
|
156
|
+
redactSource (sources, redactedSources, redactedSourcesContext, sourceIndex, start, end) {
|
|
157
|
+
if (sourceIndex != null) {
|
|
158
|
+
if (!sources[sourceIndex].redacted) {
|
|
159
|
+
redactedSources.push(sourceIndex)
|
|
160
|
+
sources[sourceIndex].pattern = ''.padEnd(sources[sourceIndex].value.length, REDACTED_SOURCE_BUFFER)
|
|
161
|
+
sources[sourceIndex].redacted = true
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!redactedSourcesContext[sourceIndex]) {
|
|
165
|
+
redactedSourcesContext[sourceIndex] = []
|
|
166
|
+
}
|
|
167
|
+
redactedSourcesContext[sourceIndex].push({
|
|
168
|
+
start,
|
|
169
|
+
end
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
129
174
|
writeValuePart (valueParts, value, source) {
|
|
130
175
|
if (value.length > 0) {
|
|
131
176
|
if (source != null) {
|
|
@@ -136,9 +181,74 @@ class SensitiveHandler {
|
|
|
136
181
|
}
|
|
137
182
|
}
|
|
138
183
|
|
|
139
|
-
writeRedactedValuePart (
|
|
140
|
-
|
|
141
|
-
|
|
184
|
+
writeRedactedValuePart (
|
|
185
|
+
valueParts,
|
|
186
|
+
length,
|
|
187
|
+
sourceIndex,
|
|
188
|
+
partValue,
|
|
189
|
+
source,
|
|
190
|
+
sourceRedactionContext,
|
|
191
|
+
isSensibleSource
|
|
192
|
+
) {
|
|
193
|
+
if (sourceIndex != null) {
|
|
194
|
+
const placeholder = source.value.includes(partValue)
|
|
195
|
+
? source.pattern
|
|
196
|
+
: '*'.repeat(length)
|
|
197
|
+
|
|
198
|
+
if (isSensibleSource) {
|
|
199
|
+
valueParts.push({ redacted: true, source: sourceIndex, pattern: placeholder })
|
|
200
|
+
} else {
|
|
201
|
+
let _value = partValue
|
|
202
|
+
const dedupedSourceRedactionContexts = []
|
|
203
|
+
|
|
204
|
+
sourceRedactionContext.forEach(_sourceRedactionContext => {
|
|
205
|
+
const isPresentInDeduped = dedupedSourceRedactionContexts.some(_dedupedSourceRedactionContext =>
|
|
206
|
+
_dedupedSourceRedactionContext.start === _sourceRedactionContext.start &&
|
|
207
|
+
_dedupedSourceRedactionContext.end === _sourceRedactionContext.end
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
if (!isPresentInDeduped) {
|
|
211
|
+
dedupedSourceRedactionContexts.push(_sourceRedactionContext)
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
let offset = 0
|
|
216
|
+
dedupedSourceRedactionContexts.forEach((_sourceRedactionContext) => {
|
|
217
|
+
if (_sourceRedactionContext.start > 0) {
|
|
218
|
+
valueParts.push({
|
|
219
|
+
source: sourceIndex,
|
|
220
|
+
value: _value.substring(0, _sourceRedactionContext.start - offset)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
_value = _value.substring(_sourceRedactionContext.start - offset)
|
|
224
|
+
offset = _sourceRedactionContext.start
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const sensitive =
|
|
228
|
+
_value.substring(_sourceRedactionContext.start - offset, _sourceRedactionContext.end - offset)
|
|
229
|
+
const indexOfPartValueInPattern = source.value.indexOf(sensitive)
|
|
230
|
+
|
|
231
|
+
const pattern = indexOfPartValueInPattern > -1
|
|
232
|
+
? placeholder.substring(indexOfPartValueInPattern, indexOfPartValueInPattern + sensitive.length)
|
|
233
|
+
: placeholder.substring(_sourceRedactionContext.start, _sourceRedactionContext.end)
|
|
234
|
+
|
|
235
|
+
valueParts.push({
|
|
236
|
+
redacted: true,
|
|
237
|
+
source: sourceIndex,
|
|
238
|
+
pattern
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
_value = _value.substring(pattern.length)
|
|
242
|
+
offset += pattern.length
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
if (_value.length) {
|
|
246
|
+
valueParts.push({
|
|
247
|
+
source: sourceIndex,
|
|
248
|
+
value: _value
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
}
|
|
142
252
|
} else {
|
|
143
253
|
valueParts.push({ redacted: true })
|
|
144
254
|
}
|
|
@@ -12,7 +12,10 @@ const {
|
|
|
12
12
|
TEST_MODULE_ID,
|
|
13
13
|
TEST_SESSION_ID,
|
|
14
14
|
TEST_COMMAND,
|
|
15
|
-
TEST_MODULE
|
|
15
|
+
TEST_MODULE,
|
|
16
|
+
getTestSuiteCommonTags,
|
|
17
|
+
TEST_STATUS,
|
|
18
|
+
TEST_SKIPPED_BY_ITR
|
|
16
19
|
} = require('./util/test')
|
|
17
20
|
const Plugin = require('./plugin')
|
|
18
21
|
const { COMPONENT } = require('../constants')
|
|
@@ -77,6 +80,24 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
77
80
|
}
|
|
78
81
|
})
|
|
79
82
|
})
|
|
83
|
+
|
|
84
|
+
this.addSub(`ci:${this.constructor.id}:itr:skipped-suites`, ({ skippedSuites, frameworkVersion }) => {
|
|
85
|
+
const testCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
|
|
86
|
+
skippedSuites.forEach((testSuite) => {
|
|
87
|
+
const testSuiteMetadata = getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, this.constructor.id)
|
|
88
|
+
|
|
89
|
+
this.tracer.startSpan(`${this.constructor.id}.test_suite`, {
|
|
90
|
+
childOf: this.testModuleSpan,
|
|
91
|
+
tags: {
|
|
92
|
+
[COMPONENT]: this.constructor.id,
|
|
93
|
+
...this.testEnvironmentMetadata,
|
|
94
|
+
...testSuiteMetadata,
|
|
95
|
+
[TEST_STATUS]: 'skip',
|
|
96
|
+
[TEST_SKIPPED_BY_ITR]: 'true'
|
|
97
|
+
}
|
|
98
|
+
}).finish()
|
|
99
|
+
})
|
|
100
|
+
})
|
|
80
101
|
}
|
|
81
102
|
|
|
82
103
|
configure (config) {
|
|
@@ -47,6 +47,7 @@ const TEST_SESSION_ID = 'test_session_id'
|
|
|
47
47
|
const TEST_MODULE_ID = 'test_module_id'
|
|
48
48
|
const TEST_SUITE_ID = 'test_suite_id'
|
|
49
49
|
const TEST_TOOLCHAIN = 'test.toolchain'
|
|
50
|
+
const TEST_SKIPPED_BY_ITR = 'test.skipped_by_itr'
|
|
50
51
|
|
|
51
52
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
52
53
|
|
|
@@ -54,6 +55,8 @@ const JEST_TEST_RUNNER = 'test.jest.test_runner'
|
|
|
54
55
|
|
|
55
56
|
const TEST_ITR_TESTS_SKIPPED = '_dd.ci.itr.tests_skipped'
|
|
56
57
|
const TEST_ITR_SKIPPING_ENABLED = 'test.itr.tests_skipping.enabled'
|
|
58
|
+
const TEST_ITR_SKIPPING_TYPE = 'test.itr.tests_skipping.type'
|
|
59
|
+
const TEST_ITR_SKIPPING_COUNT = 'test.itr.tests_skipping.count'
|
|
57
60
|
const TEST_CODE_COVERAGE_ENABLED = 'test.code_coverage.enabled'
|
|
58
61
|
|
|
59
62
|
const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
|
|
@@ -80,6 +83,7 @@ module.exports = {
|
|
|
80
83
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
81
84
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
82
85
|
TEST_SOURCE_START,
|
|
86
|
+
TEST_SKIPPED_BY_ITR,
|
|
83
87
|
getTestEnvironmentMetadata,
|
|
84
88
|
getTestParametersString,
|
|
85
89
|
finishAllTraceSpans,
|
|
@@ -99,6 +103,8 @@ module.exports = {
|
|
|
99
103
|
TEST_ITR_TESTS_SKIPPED,
|
|
100
104
|
TEST_MODULE,
|
|
101
105
|
TEST_ITR_SKIPPING_ENABLED,
|
|
106
|
+
TEST_ITR_SKIPPING_TYPE,
|
|
107
|
+
TEST_ITR_SKIPPING_COUNT,
|
|
102
108
|
TEST_CODE_COVERAGE_ENABLED,
|
|
103
109
|
TEST_CODE_COVERAGE_LINES_PCT,
|
|
104
110
|
addIntelligentTestRunnerSpanTags,
|
|
@@ -354,14 +360,25 @@ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite, testF
|
|
|
354
360
|
function addIntelligentTestRunnerSpanTags (
|
|
355
361
|
testSessionSpan,
|
|
356
362
|
testModuleSpan,
|
|
357
|
-
{
|
|
363
|
+
{
|
|
364
|
+
isSuitesSkipped,
|
|
365
|
+
isSuitesSkippingEnabled,
|
|
366
|
+
isCodeCoverageEnabled,
|
|
367
|
+
testCodeCoverageLinesTotal,
|
|
368
|
+
skippingCount,
|
|
369
|
+
skippingType = 'suite'
|
|
370
|
+
}
|
|
358
371
|
) {
|
|
359
372
|
testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
|
|
360
373
|
testSessionSpan.setTag(TEST_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
|
|
374
|
+
testSessionSpan.setTag(TEST_ITR_SKIPPING_TYPE, skippingType)
|
|
375
|
+
testSessionSpan.setTag(TEST_ITR_SKIPPING_COUNT, skippingCount)
|
|
361
376
|
testSessionSpan.setTag(TEST_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
|
|
362
377
|
|
|
363
378
|
testModuleSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
|
|
364
379
|
testModuleSpan.setTag(TEST_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
|
|
380
|
+
testModuleSpan.setTag(TEST_ITR_SKIPPING_TYPE, skippingType)
|
|
381
|
+
testModuleSpan.setTag(TEST_ITR_SKIPPING_COUNT, skippingCount)
|
|
365
382
|
testModuleSpan.setTag(TEST_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
|
|
366
383
|
|
|
367
384
|
// If suites have been skipped we don't want to report the total coverage, as it will be wrong
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const { storage } = require('../../../../datadog-core')
|
|
4
4
|
|
|
5
5
|
const dc = require('../../../../diagnostics_channel')
|
|
6
|
+
const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../../ext/tags')
|
|
7
|
+
const { WEB } = require('../../../../../ext/types')
|
|
6
8
|
|
|
7
9
|
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
8
10
|
const enterCh = dc.channel('dd-trace:storage:enter')
|
|
@@ -41,13 +43,13 @@ function getSpanContextTags (span) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
function isWebServerSpan (tags) {
|
|
44
|
-
return tags[
|
|
46
|
+
return tags[SPAN_TYPE] === WEB
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function endpointNameFromTags (tags) {
|
|
48
|
-
return tags[
|
|
49
|
-
tags[
|
|
50
|
-
tags[
|
|
50
|
+
return tags[RESOURCE_NAME] || [
|
|
51
|
+
tags[HTTP_METHOD],
|
|
52
|
+
tags[HTTP_ROUTE]
|
|
51
53
|
].filter(v => v).join(' ')
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -99,7 +101,7 @@ class NativeWallProfiler {
|
|
|
99
101
|
|
|
100
102
|
if (this._codeHotspotsEnabled && !this._emittedFFMessage && this._logger) {
|
|
101
103
|
this._logger.debug(
|
|
102
|
-
`Wall profiler: Enable
|
|
104
|
+
`Wall profiler: Enable trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
|
|
103
105
|
this._emittedFFMessage = true
|
|
104
106
|
}
|
|
105
107
|
|
|
@@ -21,6 +21,9 @@ class Tracer extends NoopProxy {
|
|
|
21
21
|
init (options) {
|
|
22
22
|
if (this._initialized) return this
|
|
23
23
|
|
|
24
|
+
process.emitWarning('The dd-trace@v2.x release line reached end of life on ' +
|
|
25
|
+
'2023-08-15 and will no longer receive updates. Please upgrade.')
|
|
26
|
+
|
|
24
27
|
this._initialized = true
|
|
25
28
|
|
|
26
29
|
try {
|
|
@@ -117,8 +117,16 @@ function Hook (modules, options, onrequire) {
|
|
|
117
117
|
// abort if _resolveLookupPaths return null
|
|
118
118
|
return exports
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
|
|
121
|
+
let res
|
|
122
|
+
try {
|
|
123
|
+
res = Module._findPath(name, [basedir, ...paths])
|
|
124
|
+
} catch (e) {
|
|
125
|
+
// case where the file specified in package.json "main" doesn't exist
|
|
126
|
+
// in this case, the file is treated as module-internal
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!res || res !== filename) {
|
|
122
130
|
// this is a module-internal file
|
|
123
131
|
// use the module-relative path to the file, prefixed by original module name
|
|
124
132
|
name = name + path.sep + path.relative(basedir, filename)
|