dd-trace 2.5.0 → 2.6.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/index.d.ts +17 -1
- package/package.json +4 -4
- package/packages/datadog-instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +170 -0
- package/packages/datadog-plugin-cucumber/src/index.js +9 -21
- package/packages/datadog-plugin-cypress/src/plugin.js +6 -17
- package/packages/datadog-plugin-fs/src/index.js +2 -0
- package/packages/datadog-plugin-http/src/server.js +0 -8
- package/packages/datadog-plugin-jest/src/index.js +101 -3
- package/packages/datadog-plugin-jest/src/util.js +1 -29
- package/packages/datadog-plugin-mocha/src/index.js +5 -15
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +26 -11
- package/packages/dd-trace/src/appsec/index.js +7 -3
- package/packages/dd-trace/src/appsec/reporter.js +29 -3
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -2
- package/packages/dd-trace/src/config.js +25 -3
- package/packages/dd-trace/src/format.js +9 -5
- package/packages/dd-trace/src/plugins/util/test.js +20 -1
- package/packages/dd-trace/src/plugins/util/web.js +11 -10
- package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
- package/packages/dd-trace/src/span_processor.js +4 -1
- package/scripts/install_plugin_modules.js +1 -0
- package/packages/datadog-plugin-jest/src/jest-environment.js +0 -272
- package/packages/datadog-plugin-jest/src/jest-jasmine2.js +0 -185
package/index.d.ts
CHANGED
|
@@ -428,7 +428,23 @@ export declare interface TracerOptions {
|
|
|
428
428
|
* Controls the maximum amount of traces sampled by AppSec attacks, per second.
|
|
429
429
|
* @default 100
|
|
430
430
|
*/
|
|
431
|
-
rateLimit?: number
|
|
431
|
+
rateLimit?: number,
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Controls the maximum amount of time in microseconds the WAF is allowed to run synchronously for.
|
|
435
|
+
* @default 5000
|
|
436
|
+
*/
|
|
437
|
+
wafTimeout?: number,
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Specifies a regex that will redact sensitive data by its key in attack reports.
|
|
441
|
+
*/
|
|
442
|
+
obfuscatorKeyRegex?: string,
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Specifies a regex that will redact sensitive data by its value in attack reports.
|
|
446
|
+
*/
|
|
447
|
+
obfuscatorValueRegex?: string
|
|
432
448
|
};
|
|
433
449
|
}
|
|
434
450
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"node": ">=12"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@datadog/native-appsec": "^1.
|
|
65
|
-
"@datadog/native-metrics": "^1.
|
|
66
|
-
"@datadog/pprof": "^0.
|
|
64
|
+
"@datadog/native-appsec": "^1.1.1",
|
|
65
|
+
"@datadog/native-metrics": "^1.2.0",
|
|
66
|
+
"@datadog/pprof": "^0.4.0",
|
|
67
67
|
"@datadog/sketches-js": "^1.0.4",
|
|
68
68
|
"@types/node": ">=12",
|
|
69
69
|
"crypto-randomuuid": "^1.0.0",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
4
|
+
const shimmer = require('../../datadog-shimmer')
|
|
5
|
+
|
|
6
|
+
const testStartCh = channel('ci:jest:test:start')
|
|
7
|
+
const testSkippedCh = channel('ci:jest:test:skip')
|
|
8
|
+
const testRunEndCh = channel('ci:jest:test:end')
|
|
9
|
+
const testErrCh = channel('ci:jest:test:err')
|
|
10
|
+
const testSuiteEnd = channel('ci:jest:test-suite:end')
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
getTestSuitePath,
|
|
14
|
+
getTestParametersString
|
|
15
|
+
} = require('../../dd-trace/src/plugins/util/test')
|
|
16
|
+
|
|
17
|
+
const { getFormattedJestTestParameters } = require('../../datadog-plugin-jest/src/util')
|
|
18
|
+
|
|
19
|
+
const specStatusToTestStatus = {
|
|
20
|
+
'pending': 'skip',
|
|
21
|
+
'disabled': 'skip',
|
|
22
|
+
'todo': 'skip',
|
|
23
|
+
'passed': 'pass',
|
|
24
|
+
'failed': 'fail'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const asyncResources = new WeakMap()
|
|
28
|
+
const originalTestFns = new WeakMap()
|
|
29
|
+
|
|
30
|
+
// based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
|
|
31
|
+
function formatJestError (errors) {
|
|
32
|
+
let error
|
|
33
|
+
if (Array.isArray(errors)) {
|
|
34
|
+
const [originalError, asyncError] = errors
|
|
35
|
+
if (originalError === null || !originalError.stack) {
|
|
36
|
+
error = asyncError
|
|
37
|
+
error.message = originalError
|
|
38
|
+
} else {
|
|
39
|
+
error = originalError
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
error = errors
|
|
43
|
+
}
|
|
44
|
+
return error
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getWrappedEnvironment (BaseEnvironment) {
|
|
48
|
+
return class DatadogEnvironment extends BaseEnvironment {
|
|
49
|
+
constructor (config, context) {
|
|
50
|
+
super(config, context)
|
|
51
|
+
this.testSuite = getTestSuitePath(context.testPath, config.rootDir)
|
|
52
|
+
this.nameToParams = {}
|
|
53
|
+
this.global._ddtrace = global._ddtrace
|
|
54
|
+
}
|
|
55
|
+
async teardown () {
|
|
56
|
+
super.teardown().finally(() => {
|
|
57
|
+
testSuiteEnd.publish()
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async handleTestEvent (event, state) {
|
|
62
|
+
if (super.handleTestEvent) {
|
|
63
|
+
await super.handleTestEvent(event, state)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let context
|
|
67
|
+
if (this.getVmContext) {
|
|
68
|
+
context = this.getVmContext()
|
|
69
|
+
} else {
|
|
70
|
+
context = this.context
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const setNameToParams = (name, params) => { this.nameToParams[name] = params }
|
|
74
|
+
|
|
75
|
+
if (event.name === 'setup') {
|
|
76
|
+
shimmer.wrap(this.global.test, 'each', each => function () {
|
|
77
|
+
const testParameters = getFormattedJestTestParameters(arguments)
|
|
78
|
+
const eachBind = each.apply(this, arguments)
|
|
79
|
+
return function () {
|
|
80
|
+
const [testName] = arguments
|
|
81
|
+
setNameToParams(testName, testParameters)
|
|
82
|
+
return eachBind.apply(this, arguments)
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
if (event.name === 'test_start') {
|
|
87
|
+
const testParameters = getTestParametersString(this.nameToParams, event.test.name)
|
|
88
|
+
|
|
89
|
+
// Async resource for this test is created here
|
|
90
|
+
// It is used later on by the test_done handler
|
|
91
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
92
|
+
asyncResources.set(event.test, asyncResource)
|
|
93
|
+
asyncResource.runInAsyncScope(() => {
|
|
94
|
+
testStartCh.publish({
|
|
95
|
+
name: context.expect.getState().currentTestName,
|
|
96
|
+
suite: this.testSuite,
|
|
97
|
+
runner: 'jest-circus',
|
|
98
|
+
testParameters
|
|
99
|
+
})
|
|
100
|
+
originalTestFns.set(event.test, event.test.fn)
|
|
101
|
+
event.test.fn = asyncResource.bind(event.test.fn)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
if (event.name === 'test_done') {
|
|
105
|
+
const asyncResource = asyncResources.get(event.test)
|
|
106
|
+
asyncResource.runInAsyncScope(() => {
|
|
107
|
+
let status = 'pass'
|
|
108
|
+
if (event.test.errors && event.test.errors.length) {
|
|
109
|
+
status = 'fail'
|
|
110
|
+
const formattedError = formatJestError(event.test.errors[0])
|
|
111
|
+
testErrCh.publish(formattedError)
|
|
112
|
+
}
|
|
113
|
+
testRunEndCh.publish(status)
|
|
114
|
+
// restore in case it is retried
|
|
115
|
+
event.test.fn = originalTestFns.get(event.test)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
if (event.name === 'test_skip' || event.name === 'test_todo') {
|
|
119
|
+
testSkippedCh.publish({
|
|
120
|
+
name: context.expect.getState().currentTestName,
|
|
121
|
+
suite: this.testSuite,
|
|
122
|
+
runner: 'jest-circus'
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
addHook({
|
|
130
|
+
name: 'jest-environment-node',
|
|
131
|
+
versions: ['>=24.8.0']
|
|
132
|
+
}, getWrappedEnvironment)
|
|
133
|
+
|
|
134
|
+
addHook({
|
|
135
|
+
name: 'jest-environment-jsdom',
|
|
136
|
+
versions: ['>=24.8.0']
|
|
137
|
+
}, getWrappedEnvironment)
|
|
138
|
+
|
|
139
|
+
addHook({
|
|
140
|
+
name: 'jest-jasmine2',
|
|
141
|
+
versions: ['>=24.8.0'],
|
|
142
|
+
file: 'build/jasmineAsyncInstall.js'
|
|
143
|
+
}, (jasmineAsyncInstallExport) => {
|
|
144
|
+
return function (globalConfig, globalInput) {
|
|
145
|
+
globalInput._ddtrace = global._ddtrace
|
|
146
|
+
shimmer.wrap(globalInput.jasmine.Spec.prototype, 'execute', execute => function (onComplete) {
|
|
147
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
148
|
+
asyncResource.runInAsyncScope(() => {
|
|
149
|
+
const testSuite = getTestSuitePath(this.result.testPath, globalConfig.rootDir)
|
|
150
|
+
testStartCh.publish({
|
|
151
|
+
name: this.getFullName(),
|
|
152
|
+
suite: testSuite,
|
|
153
|
+
runner: 'jest-jasmine2'
|
|
154
|
+
})
|
|
155
|
+
const spec = this
|
|
156
|
+
const callback = asyncResource.bind(function () {
|
|
157
|
+
if (spec.result.failedExpectations && spec.result.failedExpectations.length) {
|
|
158
|
+
const formattedError = formatJestError(spec.result.failedExpectations[0].error)
|
|
159
|
+
testErrCh.publish(formattedError)
|
|
160
|
+
}
|
|
161
|
+
testRunEndCh.publish(specStatusToTestStatus[spec.result.status])
|
|
162
|
+
onComplete.apply(this, arguments)
|
|
163
|
+
})
|
|
164
|
+
arguments[0] = callback
|
|
165
|
+
execute.apply(this, arguments)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
return jasmineAsyncInstallExport.default(globalConfig, globalInput)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
@@ -5,11 +5,7 @@ const { storage } = require('../../datadog-core')
|
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
7
|
CI_APP_ORIGIN,
|
|
8
|
-
TEST_TYPE,
|
|
9
|
-
TEST_NAME,
|
|
10
|
-
TEST_SUITE,
|
|
11
8
|
TEST_SKIP_REASON,
|
|
12
|
-
TEST_FRAMEWORK_VERSION,
|
|
13
9
|
ERROR_MESSAGE,
|
|
14
10
|
TEST_STATUS,
|
|
15
11
|
TEST_CODE_OWNERS,
|
|
@@ -17,11 +13,10 @@ const {
|
|
|
17
13
|
getTestEnvironmentMetadata,
|
|
18
14
|
getTestSuitePath,
|
|
19
15
|
getCodeOwnersFileEntries,
|
|
20
|
-
getCodeOwnersForFilename
|
|
16
|
+
getCodeOwnersForFilename,
|
|
17
|
+
getTestCommonTags
|
|
21
18
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
22
|
-
const {
|
|
23
|
-
const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
|
|
24
|
-
const { AUTO_KEEP } = require('../../../ext/priority')
|
|
19
|
+
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
25
20
|
|
|
26
21
|
class CucumberPlugin extends Plugin {
|
|
27
22
|
static get name () {
|
|
@@ -40,26 +35,19 @@ class CucumberPlugin extends Plugin {
|
|
|
40
35
|
const childOf = store ? store.span : store
|
|
41
36
|
const testSuite = getTestSuitePath(pickleUri, sourceRoot)
|
|
42
37
|
|
|
43
|
-
const
|
|
44
|
-
[SPAN_TYPE]: 'test',
|
|
45
|
-
[RESOURCE_NAME]: pickleName,
|
|
46
|
-
[TEST_TYPE]: 'test',
|
|
47
|
-
[TEST_NAME]: pickleName,
|
|
48
|
-
[TEST_SUITE]: testSuite,
|
|
49
|
-
[SAMPLING_RULE_DECISION]: 1,
|
|
50
|
-
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
51
|
-
[TEST_FRAMEWORK_VERSION]: this.tracer._version,
|
|
52
|
-
...testEnvironmentMetadata
|
|
53
|
-
}
|
|
38
|
+
const commonTags = getTestCommonTags(pickleName, testSuite, this.tracer._version)
|
|
54
39
|
|
|
55
40
|
const codeOwners = getCodeOwnersForFilename(testSuite, codeOwnersEntries)
|
|
56
41
|
if (codeOwners) {
|
|
57
|
-
|
|
42
|
+
commonTags[TEST_CODE_OWNERS] = codeOwners
|
|
58
43
|
}
|
|
59
44
|
|
|
60
45
|
const span = this.tracer.startSpan('cucumber.test', {
|
|
61
46
|
childOf,
|
|
62
|
-
tags:
|
|
47
|
+
tags: {
|
|
48
|
+
...commonTags,
|
|
49
|
+
...testEnvironmentMetadata
|
|
50
|
+
}
|
|
63
51
|
})
|
|
64
52
|
|
|
65
53
|
span.context()._trace.origin = CI_APP_ORIGIN
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
const {
|
|
2
|
-
TEST_TYPE,
|
|
3
|
-
TEST_NAME,
|
|
4
|
-
TEST_SUITE,
|
|
5
2
|
TEST_STATUS,
|
|
6
|
-
TEST_FRAMEWORK_VERSION,
|
|
7
3
|
TEST_IS_RUM_ACTIVE,
|
|
8
4
|
TEST_CODE_OWNERS,
|
|
9
5
|
getTestEnvironmentMetadata,
|
|
10
6
|
CI_APP_ORIGIN,
|
|
11
7
|
getTestParentSpan,
|
|
12
8
|
getCodeOwnersFileEntries,
|
|
13
|
-
getCodeOwnersForFilename
|
|
9
|
+
getCodeOwnersForFilename,
|
|
10
|
+
getTestCommonTags
|
|
14
11
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
15
12
|
|
|
16
|
-
const {
|
|
17
|
-
const { SAMPLING_PRIORITY, SPAN_TYPE, RESOURCE_NAME } = require('../../../ext/tags')
|
|
18
|
-
const { AUTO_KEEP } = require('../../../ext/priority')
|
|
13
|
+
const { ORIGIN_KEY } = require('../../dd-trace/src/constants')
|
|
19
14
|
|
|
20
15
|
const CYPRESS_STATUS_TO_TEST_STATUS = {
|
|
21
16
|
passed: 'pass',
|
|
@@ -27,15 +22,11 @@ const CYPRESS_STATUS_TO_TEST_STATUS = {
|
|
|
27
22
|
function getTestSpanMetadata (tracer, testName, testSuite, cypressConfig) {
|
|
28
23
|
const childOf = getTestParentSpan(tracer)
|
|
29
24
|
|
|
25
|
+
const commonTags = getTestCommonTags(testName, testSuite, cypressConfig.version)
|
|
26
|
+
|
|
30
27
|
return {
|
|
31
28
|
childOf,
|
|
32
|
-
|
|
33
|
-
[TEST_TYPE]: 'test',
|
|
34
|
-
[TEST_NAME]: testName,
|
|
35
|
-
[TEST_SUITE]: testSuite,
|
|
36
|
-
[SAMPLING_RULE_DECISION]: 1,
|
|
37
|
-
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
38
|
-
[TEST_FRAMEWORK_VERSION]: cypressConfig.version
|
|
29
|
+
...commonTags
|
|
39
30
|
}
|
|
40
31
|
}
|
|
41
32
|
|
|
@@ -71,8 +62,6 @@ module.exports = (on, config) => {
|
|
|
71
62
|
activeSpan = tracer.startSpan('cypress.test', {
|
|
72
63
|
childOf,
|
|
73
64
|
tags: {
|
|
74
|
-
[SPAN_TYPE]: 'test',
|
|
75
|
-
[RESOURCE_NAME]: resource,
|
|
76
65
|
[ORIGIN_KEY]: CI_APP_ORIGIN,
|
|
77
66
|
...testSpanMetadata,
|
|
78
67
|
...testEnvironmentMetadata
|
|
@@ -60,6 +60,7 @@ function createWrapCreateReadStream (config, tracer) {
|
|
|
60
60
|
const tags = makeFSFlagTags('ReadStream', path, options, 'r', config, tracer)
|
|
61
61
|
return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => {
|
|
62
62
|
const stream = createReadStream.apply(this, arguments)
|
|
63
|
+
stream.once('close', done)
|
|
63
64
|
stream.once('end', done)
|
|
64
65
|
stream.once('error', done)
|
|
65
66
|
return stream
|
|
@@ -74,6 +75,7 @@ function createWrapCreateWriteStream (config, tracer) {
|
|
|
74
75
|
const tags = makeFSFlagTags('WriteStream', path, options, 'w', config, tracer)
|
|
75
76
|
return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => {
|
|
76
77
|
const stream = createWriteStream.apply(this, arguments)
|
|
78
|
+
stream.once('close', done)
|
|
77
79
|
stream.once('finish', done)
|
|
78
80
|
stream.once('error', done)
|
|
79
81
|
return stream
|
|
@@ -4,9 +4,6 @@ const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
6
6
|
const { incomingHttpRequestStart } = require('../../dd-trace/src/appsec/gateway/channels')
|
|
7
|
-
const tags = require('../../../ext/tags')
|
|
8
|
-
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
9
|
-
const SERVICE_NAME = tags.SERVICE_NAME
|
|
10
7
|
|
|
11
8
|
class HttpServerPlugin extends Plugin {
|
|
12
9
|
static get name () {
|
|
@@ -20,11 +17,6 @@ class HttpServerPlugin extends Plugin {
|
|
|
20
17
|
const store = storage.getStore()
|
|
21
18
|
const span = web.startSpan(this.tracer, this.config, req, res, 'http.request')
|
|
22
19
|
|
|
23
|
-
if (this.config.service) {
|
|
24
|
-
span.setTag(SERVICE_NAME, this.config.service)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
analyticsSampler.sample(span, this.config.measured, true)
|
|
28
20
|
this.enter(span, store)
|
|
29
21
|
|
|
30
22
|
const context = web.getContext(req)
|
|
@@ -1,4 +1,102 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
2
|
+
const { storage } = require('../../datadog-core')
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const {
|
|
5
|
+
CI_APP_ORIGIN,
|
|
6
|
+
TEST_STATUS,
|
|
7
|
+
JEST_TEST_RUNNER,
|
|
8
|
+
finishAllTraceSpans,
|
|
9
|
+
getTestEnvironmentMetadata,
|
|
10
|
+
getTestParentSpan,
|
|
11
|
+
getTestCommonTags,
|
|
12
|
+
TEST_PARAMETERS,
|
|
13
|
+
getCodeOwnersFileEntries,
|
|
14
|
+
getCodeOwnersForFilename,
|
|
15
|
+
TEST_CODE_OWNERS
|
|
16
|
+
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
|
+
|
|
18
|
+
function getTestSpanMetadata (tracer, test) {
|
|
19
|
+
const childOf = getTestParentSpan(tracer)
|
|
20
|
+
|
|
21
|
+
const { suite, name, runner, testParameters } = test
|
|
22
|
+
|
|
23
|
+
const commonTags = getTestCommonTags(name, suite, tracer._version)
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
childOf,
|
|
27
|
+
...commonTags,
|
|
28
|
+
[JEST_TEST_RUNNER]: runner,
|
|
29
|
+
[TEST_PARAMETERS]: testParameters
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class JestPlugin extends Plugin {
|
|
34
|
+
static get name () {
|
|
35
|
+
return 'jest'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor (...args) {
|
|
39
|
+
super(...args)
|
|
40
|
+
|
|
41
|
+
this.testEnvironmentMetadata = getTestEnvironmentMetadata('jest', this.config)
|
|
42
|
+
this.codeOwnersEntries = getCodeOwnersFileEntries()
|
|
43
|
+
|
|
44
|
+
this.addSub('ci:jest:test:start', (test) => {
|
|
45
|
+
const store = storage.getStore()
|
|
46
|
+
const span = this.startTestSpan(test)
|
|
47
|
+
|
|
48
|
+
this.enter(span, store)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
this.addSub('ci:jest:test:end', (status) => {
|
|
52
|
+
const span = storage.getStore().span
|
|
53
|
+
span.setTag(TEST_STATUS, status)
|
|
54
|
+
span.finish()
|
|
55
|
+
finishAllTraceSpans(span)
|
|
56
|
+
this.exit()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
this.addSub('ci:jest:test-suite:end', () => {
|
|
60
|
+
this.tracer._exporter._writer.flush()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
this.addSub('ci:jest:test:err', (error) => {
|
|
64
|
+
if (error) {
|
|
65
|
+
const span = storage.getStore().span
|
|
66
|
+
span.setTag(TEST_STATUS, 'fail')
|
|
67
|
+
span.setTag('error', error)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
this.addSub('ci:jest:test:skip', (test) => {
|
|
72
|
+
const span = this.startTestSpan(test)
|
|
73
|
+
span.setTag(TEST_STATUS, 'skip')
|
|
74
|
+
span.finish()
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
startTestSpan (test) {
|
|
79
|
+
const { childOf, ...testSpanMetadata } = getTestSpanMetadata(this.tracer, test)
|
|
80
|
+
|
|
81
|
+
const codeOwners = getCodeOwnersForFilename(test.suite, this.codeOwnersEntries)
|
|
82
|
+
|
|
83
|
+
if (codeOwners) {
|
|
84
|
+
testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const testSpan = this.tracer
|
|
88
|
+
.startSpan('jest.test', {
|
|
89
|
+
childOf,
|
|
90
|
+
tags: {
|
|
91
|
+
...this.testEnvironmentMetadata,
|
|
92
|
+
...testSpanMetadata
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
testSpan.context()._trace.origin = CI_APP_ORIGIN
|
|
97
|
+
|
|
98
|
+
return testSpan
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = JestPlugin
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
|
|
2
|
-
const { SAMPLING_PRIORITY, SPAN_TYPE } = require('../../../ext/tags')
|
|
3
|
-
const { AUTO_KEEP } = require('../../../ext/priority')
|
|
4
|
-
const { TEST_TYPE, TEST_STATUS, getTestParentSpan } = require('../../dd-trace/src/plugins/util/test')
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* There are two ways to call `test.each` in `jest`:
|
|
8
3
|
* 1. With an array of arrays: https://jestjs.io/docs/api#1-testeachtablename-fn-timeout
|
|
@@ -38,27 +33,4 @@ function getFormattedJestTestParameters (testParameters) {
|
|
|
38
33
|
return formattedParameters
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
const childOf = getTestParentSpan(tracer)
|
|
43
|
-
|
|
44
|
-
const commonSpanTags = {
|
|
45
|
-
[TEST_TYPE]: 'test',
|
|
46
|
-
[SAMPLING_RULE_DECISION]: 1,
|
|
47
|
-
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
48
|
-
[SPAN_TYPE]: 'test',
|
|
49
|
-
...testEnvironmentMetadata
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
childOf,
|
|
53
|
-
commonSpanTags
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function setSuppressedErrors (suppressedErrors, testSpan) {
|
|
58
|
-
if (suppressedErrors && suppressedErrors.length) {
|
|
59
|
-
testSpan.setTag('error', suppressedErrors[0])
|
|
60
|
-
testSpan.setTag(TEST_STATUS, 'fail')
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
module.exports = { getFormattedJestTestParameters, getTestSpanTags, setSuppressedErrors }
|
|
36
|
+
module.exports = { getFormattedJestTestParameters }
|
|
@@ -6,10 +6,7 @@ const { storage } = require('../../datadog-core')
|
|
|
6
6
|
const {
|
|
7
7
|
CI_APP_ORIGIN,
|
|
8
8
|
TEST_CODE_OWNERS,
|
|
9
|
-
TEST_TYPE,
|
|
10
|
-
TEST_NAME,
|
|
11
9
|
TEST_SUITE,
|
|
12
|
-
TEST_FRAMEWORK_VERSION,
|
|
13
10
|
TEST_STATUS,
|
|
14
11
|
TEST_PARAMETERS,
|
|
15
12
|
finishAllTraceSpans,
|
|
@@ -18,11 +15,9 @@ const {
|
|
|
18
15
|
getTestParentSpan,
|
|
19
16
|
getTestParametersString,
|
|
20
17
|
getCodeOwnersFileEntries,
|
|
21
|
-
getCodeOwnersForFilename
|
|
18
|
+
getCodeOwnersForFilename,
|
|
19
|
+
getTestCommonTags
|
|
22
20
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
23
|
-
const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../ext/tags')
|
|
24
|
-
const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
|
|
25
|
-
const { AUTO_KEEP } = require('../../../ext/priority')
|
|
26
21
|
|
|
27
22
|
const skippedTests = new WeakSet()
|
|
28
23
|
|
|
@@ -33,16 +28,11 @@ function getTestSpanMetadata (tracer, test, sourceRoot) {
|
|
|
33
28
|
const fullTestName = test.fullTitle()
|
|
34
29
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, sourceRoot)
|
|
35
30
|
|
|
31
|
+
const commonTags = getTestCommonTags(fullTestName, testSuite, tracer._version)
|
|
32
|
+
|
|
36
33
|
return {
|
|
37
34
|
childOf,
|
|
38
|
-
|
|
39
|
-
[TEST_TYPE]: 'test',
|
|
40
|
-
[TEST_NAME]: fullTestName,
|
|
41
|
-
[TEST_SUITE]: testSuite,
|
|
42
|
-
[SAMPLING_RULE_DECISION]: 1,
|
|
43
|
-
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
44
|
-
[TEST_FRAMEWORK_VERSION]: tracer._version,
|
|
45
|
-
[RESOURCE_NAME]: `${testSuite}.${fullTestName}`
|
|
35
|
+
...commonTags
|
|
46
36
|
}
|
|
47
37
|
}
|
|
48
38
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = '2.
|
|
1
|
+
module.exports = '2.6.0'
|
|
@@ -7,16 +7,14 @@ const Reporter = require('../reporter')
|
|
|
7
7
|
|
|
8
8
|
const validAddressSet = new Set(Object.values(addresses))
|
|
9
9
|
|
|
10
|
-
const DEFAULT_MAX_BUDGET = 5e3 // µs
|
|
11
|
-
|
|
12
10
|
// TODO: put reusable code in a base class
|
|
13
11
|
class WAFCallback {
|
|
14
|
-
static loadDDWAF (rules) {
|
|
12
|
+
static loadDDWAF (rules, config) {
|
|
15
13
|
try {
|
|
16
14
|
// require in `try/catch` because this can throw at require time
|
|
17
15
|
const { DDWAF } = require('@datadog/native-appsec')
|
|
18
16
|
|
|
19
|
-
return new DDWAF(rules)
|
|
17
|
+
return new DDWAF(rules, config)
|
|
20
18
|
} catch (err) {
|
|
21
19
|
log.error('AppSec could not load native package. In-app WAF features will not be available.')
|
|
22
20
|
|
|
@@ -24,8 +22,24 @@ class WAFCallback {
|
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
constructor (rules) {
|
|
28
|
-
|
|
25
|
+
constructor (rules, config) {
|
|
26
|
+
const { wafTimeout, obfuscatorKeyRegex, obfuscatorValueRegex } = config
|
|
27
|
+
|
|
28
|
+
this.ddwaf = WAFCallback.loadDDWAF(rules, { obfuscatorKeyRegex, obfuscatorValueRegex })
|
|
29
|
+
|
|
30
|
+
this.wafTimeout = wafTimeout
|
|
31
|
+
|
|
32
|
+
const version = this.ddwaf.constructor.version()
|
|
33
|
+
|
|
34
|
+
Reporter.metricsQueue.set('_dd.appsec.waf.version', `${version.major}.${version.minor}.${version.patch}`)
|
|
35
|
+
|
|
36
|
+
const { loaded, failed } = this.ddwaf.rulesInfo
|
|
37
|
+
|
|
38
|
+
Reporter.metricsQueue.set('_dd.appsec.event_rules.loaded', loaded)
|
|
39
|
+
Reporter.metricsQueue.set('_dd.appsec.event_rules.error_count', failed)
|
|
40
|
+
|
|
41
|
+
Reporter.metricsQueue.set('manual.keep', true)
|
|
42
|
+
|
|
29
43
|
this.wafContextCache = new WeakMap()
|
|
30
44
|
|
|
31
45
|
// closures are faster than binds
|
|
@@ -81,7 +95,7 @@ class WAFCallback {
|
|
|
81
95
|
|
|
82
96
|
try {
|
|
83
97
|
// TODO: possible optimizaion: only send params that haven't already been sent to this wafContext
|
|
84
|
-
const result = wafContext.run(params,
|
|
98
|
+
const result = wafContext.run(params, this.wafTimeout)
|
|
85
99
|
|
|
86
100
|
return this.applyResult(result, store)
|
|
87
101
|
} catch (err) {
|
|
@@ -93,13 +107,14 @@ class WAFCallback {
|
|
|
93
107
|
}
|
|
94
108
|
|
|
95
109
|
applyResult (result, store) {
|
|
110
|
+
Reporter.reportMetrics({
|
|
111
|
+
duration: result.totalRuntime,
|
|
112
|
+
rulesVersion: this.ddwaf.rulesInfo.version
|
|
113
|
+
}, store)
|
|
114
|
+
|
|
96
115
|
if (result.data && result.data !== '[]') {
|
|
97
116
|
Reporter.reportAttack(result.data, store)
|
|
98
117
|
}
|
|
99
|
-
|
|
100
|
-
// TODO: use these values later for budget management
|
|
101
|
-
// result.perfData
|
|
102
|
-
// result.perfTotalRuntime
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
clear () {
|
|
@@ -16,7 +16,7 @@ function enable (config) {
|
|
|
16
16
|
let rules = fs.readFileSync(config.appsec.rules)
|
|
17
17
|
rules = JSON.parse(rules)
|
|
18
18
|
|
|
19
|
-
RuleManager.applyRules(rules)
|
|
19
|
+
RuleManager.applyRules(rules, config.appsec)
|
|
20
20
|
} catch (err) {
|
|
21
21
|
log.error('Unable to start AppSec')
|
|
22
22
|
log.error(err)
|
|
@@ -94,12 +94,16 @@ function incomingHttpEndTranslator (data) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
if (data.req.cookies && typeof data.req.cookies === 'object') {
|
|
97
|
-
payload[addresses.HTTP_INCOMING_COOKIES] =
|
|
97
|
+
payload[addresses.HTTP_INCOMING_COOKIES] = {}
|
|
98
|
+
|
|
99
|
+
for (const k of Object.keys(data.req.cookies)) {
|
|
100
|
+
payload[addresses.HTTP_INCOMING_COOKIES][k] = [ data.req.cookies[k] ]
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
Gateway.propagate(payload, context)
|
|
101
105
|
|
|
102
|
-
Reporter.
|
|
106
|
+
Reporter.finishRequest(data.req, context)
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
function disable () {
|