dd-trace 3.2.0 → 3.3.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/LICENSE-3rdparty.csv +2 -1
- package/README.md +4 -0
- package/package.json +5 -4
- package/packages/datadog-instrumentations/src/crypto.js +14 -12
- package/packages/datadog-instrumentations/src/grpc/server.js +15 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/jest.js +136 -14
- package/packages/datadog-instrumentations/src/mocha.js +77 -31
- package/packages/datadog-instrumentations/src/next.js +7 -3
- package/packages/datadog-plugin-jest/src/index.js +106 -6
- package/packages/datadog-plugin-mocha/src/index.js +15 -7
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +27 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +13 -5
- package/packages/dd-trace/src/appsec/recommended.json +1144 -275
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -3
- package/packages/dd-trace/src/config.js +15 -1
- package/packages/dd-trace/src/encode/0.4.js +7 -1
- package/packages/dd-trace/src/encode/0.5.js +7 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +2 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -20
- package/packages/dd-trace/src/exporters/common/request.js +3 -3
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +30 -4
- package/packages/dd-trace/src/plugins/util/redis.js +0 -74
- package/packages/dd-trace/src/plugins/util/tx.js +0 -75
|
@@ -9,10 +9,15 @@ const {
|
|
|
9
9
|
getTestEnvironmentMetadata,
|
|
10
10
|
getTestParentSpan,
|
|
11
11
|
getTestCommonTags,
|
|
12
|
+
getTestSessionCommonTags,
|
|
13
|
+
getTestSuiteCommonTags,
|
|
12
14
|
TEST_PARAMETERS,
|
|
13
15
|
getCodeOwnersFileEntries,
|
|
14
16
|
getCodeOwnersForFilename,
|
|
15
|
-
TEST_CODE_OWNERS
|
|
17
|
+
TEST_CODE_OWNERS,
|
|
18
|
+
TEST_SESSION_ID,
|
|
19
|
+
TEST_SUITE_ID,
|
|
20
|
+
TEST_COMMAND
|
|
16
21
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
22
|
|
|
18
23
|
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
@@ -58,12 +63,93 @@ class JestPlugin extends Plugin {
|
|
|
58
63
|
this.testEnvironmentMetadata = getTestEnvironmentMetadata('jest', this.config)
|
|
59
64
|
this.codeOwnersEntries = getCodeOwnersFileEntries()
|
|
60
65
|
|
|
61
|
-
this.addSub('ci:jest:
|
|
66
|
+
this.addSub('ci:jest:session:start', (command) => {
|
|
67
|
+
if (!this.config.isAgentlessEnabled) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
const store = storage.getStore()
|
|
71
|
+
const childOf = getTestParentSpan(this.tracer)
|
|
72
|
+
const testSessionSpanMetadata = getTestSessionCommonTags(command, this.tracer._version)
|
|
73
|
+
|
|
74
|
+
const testSessionSpan = this.tracer.startSpan('jest.test_session', {
|
|
75
|
+
childOf,
|
|
76
|
+
tags: {
|
|
77
|
+
...this.testEnvironmentMetadata,
|
|
78
|
+
...testSessionSpanMetadata
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
this.enter(testSessionSpan, store)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
this.addSub('ci:jest:session:finish', (status) => {
|
|
85
|
+
if (!this.config.isAgentlessEnabled) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
const testSessionSpan = storage.getStore().span
|
|
89
|
+
testSessionSpan.setTag(TEST_STATUS, status)
|
|
90
|
+
testSessionSpan.finish()
|
|
91
|
+
finishAllTraceSpans(testSessionSpan)
|
|
92
|
+
this.tracer._exporter._writer.flush()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Test suites can be run in a different process from jest's main one.
|
|
96
|
+
// This subscriber changes the configuration objects from jest to inject the trace id
|
|
97
|
+
// of the test session to the processes that run the test suites.
|
|
98
|
+
this.addSub('ci:jest:session:configuration', configs => {
|
|
99
|
+
if (!this.config.isAgentlessEnabled) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
const testSessionSpan = storage.getStore().span
|
|
103
|
+
configs.forEach(config => {
|
|
104
|
+
config._ddTestSessionId = testSessionSpan.context()._traceId.toString(10)
|
|
105
|
+
config._ddTestCommand = testSessionSpan.context()._tags[TEST_COMMAND]
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
this.addSub('ci:jest:test-suite:start', ({ testSuite, testEnvironmentOptions }) => {
|
|
110
|
+
if (!this.config.isAgentlessEnabled) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { _ddTestSessionId: testSessionId, _ddTestCommand: testCommand } = testEnvironmentOptions
|
|
115
|
+
|
|
116
|
+
const store = storage.getStore()
|
|
117
|
+
|
|
118
|
+
const testSessionSpanContext = this.tracer.extract('text_map', {
|
|
119
|
+
'x-datadog-trace-id': testSessionId,
|
|
120
|
+
'x-datadog-parent-id': '0000000000000000'
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const testSuiteMetadata = getTestSuiteCommonTags(testCommand, this.tracer._version, testSuite)
|
|
124
|
+
|
|
125
|
+
const testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
|
|
126
|
+
childOf: testSessionSpanContext,
|
|
127
|
+
tags: {
|
|
128
|
+
...this.testEnvironmentMetadata,
|
|
129
|
+
...testSuiteMetadata
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
this.enter(testSuiteSpan, store)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage }) => {
|
|
136
|
+
if (!this.config.isAgentlessEnabled) {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
const testSuiteSpan = storage.getStore().span
|
|
140
|
+
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
141
|
+
if (errorMessage) {
|
|
142
|
+
testSuiteSpan.setTag('error', new Error(errorMessage))
|
|
143
|
+
}
|
|
144
|
+
testSuiteSpan.finish()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
this.addSub('ci:jest:test-suite:code-coverage', (coverageFiles) => {
|
|
62
148
|
if (!this.config.isAgentlessEnabled || !this.config.isIntelligentTestRunnerEnabled) {
|
|
63
149
|
return
|
|
64
150
|
}
|
|
65
|
-
const
|
|
66
|
-
this.tracer._exporter.exportCoverage({
|
|
151
|
+
const testSuiteSpan = storage.getStore().span
|
|
152
|
+
this.tracer._exporter.exportCoverage({ span: testSuiteSpan, coverageFiles })
|
|
67
153
|
})
|
|
68
154
|
|
|
69
155
|
this.addSub('ci:jest:test:start', (test) => {
|
|
@@ -96,7 +182,20 @@ class JestPlugin extends Plugin {
|
|
|
96
182
|
}
|
|
97
183
|
|
|
98
184
|
startTestSpan (test) {
|
|
99
|
-
const
|
|
185
|
+
const suiteTags = {}
|
|
186
|
+
const store = storage.getStore()
|
|
187
|
+
const testSuiteSpan = store ? store.span : undefined
|
|
188
|
+
if (testSuiteSpan) {
|
|
189
|
+
const testSuiteId = testSuiteSpan.context()._spanId.toString(10)
|
|
190
|
+
suiteTags[TEST_SUITE_ID] = testSuiteId
|
|
191
|
+
suiteTags[TEST_SESSION_ID] = testSuiteSpan.context()._traceId.toString(10)
|
|
192
|
+
suiteTags[TEST_COMMAND] = testSuiteSpan.context()._tags[TEST_COMMAND]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const {
|
|
196
|
+
childOf,
|
|
197
|
+
...testSpanMetadata
|
|
198
|
+
} = getTestSpanMetadata(this.tracer, test)
|
|
100
199
|
|
|
101
200
|
const codeOwners = getCodeOwnersForFilename(test.suite, this.codeOwnersEntries)
|
|
102
201
|
|
|
@@ -109,7 +208,8 @@ class JestPlugin extends Plugin {
|
|
|
109
208
|
childOf,
|
|
110
209
|
tags: {
|
|
111
210
|
...this.testEnvironmentMetadata,
|
|
112
|
-
...testSpanMetadata
|
|
211
|
+
...testSpanMetadata,
|
|
212
|
+
...suiteTags
|
|
113
213
|
}
|
|
114
214
|
})
|
|
115
215
|
|
|
@@ -47,7 +47,7 @@ class MochaPlugin extends Plugin {
|
|
|
47
47
|
constructor (...args) {
|
|
48
48
|
super(...args)
|
|
49
49
|
|
|
50
|
-
this._testSuites = new
|
|
50
|
+
this._testSuites = new Map()
|
|
51
51
|
this._testNameToParams = {}
|
|
52
52
|
this.testEnvironmentMetadata = getTestEnvironmentMetadata('mocha', this.config)
|
|
53
53
|
this.sourceRoot = process.cwd()
|
|
@@ -75,7 +75,11 @@ class MochaPlugin extends Plugin {
|
|
|
75
75
|
return
|
|
76
76
|
}
|
|
77
77
|
const store = storage.getStore()
|
|
78
|
-
const testSuiteMetadata = getTestSuiteCommonTags(
|
|
78
|
+
const testSuiteMetadata = getTestSuiteCommonTags(
|
|
79
|
+
this.command,
|
|
80
|
+
this.tracer._version,
|
|
81
|
+
getTestSuitePath(suite.file, this.sourceRoot)
|
|
82
|
+
)
|
|
79
83
|
const testSuiteSpan = this.tracer.startSpan('mocha.test_suite', {
|
|
80
84
|
childOf: this.testSessionSpan,
|
|
81
85
|
tags: {
|
|
@@ -84,7 +88,7 @@ class MochaPlugin extends Plugin {
|
|
|
84
88
|
}
|
|
85
89
|
})
|
|
86
90
|
this.enter(testSuiteSpan, store)
|
|
87
|
-
this._testSuites.set(suite, testSuiteSpan)
|
|
91
|
+
this._testSuites.set(suite.file, testSuiteSpan)
|
|
88
92
|
})
|
|
89
93
|
|
|
90
94
|
this.addSub('ci:mocha:test-suite:finish', (status) => {
|
|
@@ -92,7 +96,10 @@ class MochaPlugin extends Plugin {
|
|
|
92
96
|
return
|
|
93
97
|
}
|
|
94
98
|
const span = storage.getStore().span
|
|
95
|
-
|
|
99
|
+
// the test status of the suite may have been set in ci:mocha:test-suite:error already
|
|
100
|
+
if (!span.context()._tags[TEST_STATUS]) {
|
|
101
|
+
span.setTag(TEST_STATUS, status)
|
|
102
|
+
}
|
|
96
103
|
span.finish()
|
|
97
104
|
})
|
|
98
105
|
|
|
@@ -102,6 +109,7 @@ class MochaPlugin extends Plugin {
|
|
|
102
109
|
}
|
|
103
110
|
const span = storage.getStore().span
|
|
104
111
|
span.setTag('error', err)
|
|
112
|
+
span.setTag(TEST_STATUS, 'fail')
|
|
105
113
|
})
|
|
106
114
|
|
|
107
115
|
this.addSub('ci:mocha:test:start', (test) => {
|
|
@@ -158,15 +166,15 @@ class MochaPlugin extends Plugin {
|
|
|
158
166
|
|
|
159
167
|
startTestSpan (test) {
|
|
160
168
|
const testSuiteTags = {}
|
|
161
|
-
const testSuiteSpan = this._testSuites.get(test.parent)
|
|
169
|
+
const testSuiteSpan = this._testSuites.get(test.parent.file)
|
|
162
170
|
|
|
163
171
|
if (testSuiteSpan) {
|
|
164
|
-
const testSuiteId = testSuiteSpan.context()._spanId.toString(
|
|
172
|
+
const testSuiteId = testSuiteSpan.context()._spanId.toString(10)
|
|
165
173
|
testSuiteTags[TEST_SUITE_ID] = testSuiteId
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
if (this.testSessionSpan) {
|
|
169
|
-
const testSessionId = this.testSessionSpan.context()._traceId.toString(
|
|
177
|
+
const testSessionId = this.testSessionSpan.context()._traceId.toString(10)
|
|
170
178
|
testSuiteTags[TEST_SESSION_ID] = testSessionId
|
|
171
179
|
testSuiteTags[TEST_COMMAND] = this.command
|
|
172
180
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
+
|
|
4
|
+
const INSECURE_CIPHERS = new Set([
|
|
5
|
+
'des', 'des-cbc', 'des-cfb', 'des-cfb1', 'des-cfb8', 'des-ecb', 'des-ede', 'des-ede-cbc', 'des-ede-cfb',
|
|
6
|
+
'des-ede-ecb', 'des-ede-ofb', 'des-ede3', 'des-ede3-cbc', 'des-ede3-cfb', 'des-ede3-cfb1', 'des-ede3-cfb8',
|
|
7
|
+
'des-ede3-ecb', 'des-ede3-ofb', 'des-ofb', 'des3', 'des3-wrap',
|
|
8
|
+
'rc2', 'rc2-128', 'rc2-40', 'rc2-40-cbc', 'rc2-64', 'rc2-64-cbc', 'rc2-cbc', 'rc2-cfb', 'rc2-ecb', 'rc2-ofb',
|
|
9
|
+
'blowfish',
|
|
10
|
+
'rc4', 'rc4-40', 'rc4-hmac-md5'
|
|
11
|
+
].map(algorithm => algorithm.toLowerCase()))
|
|
12
|
+
|
|
13
|
+
class WeakCipherAnalyzer extends Analyzer {
|
|
14
|
+
constructor () {
|
|
15
|
+
super('WEAK_CIPHER')
|
|
16
|
+
this.addSub('datadog:crypto:cipher:start', ({ algorithm }) => this.analyze(algorithm))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_isVulnerable (algorithm) {
|
|
20
|
+
if (algorithm && typeof algorithm === 'string') {
|
|
21
|
+
return INSECURE_CIPHERS.has(algorithm.toLowerCase())
|
|
22
|
+
}
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = new WeakCipherAnalyzer()
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
2
|
+
const LRU = require('lru-cache')
|
|
2
3
|
const VULNERABILITIES_KEY = 'vulnerabilities'
|
|
3
4
|
const IAST_JSON_TAG_KEY = '_dd.iast.json'
|
|
5
|
+
const VULNERABILITY_HASHES_MAX_SIZE = 1000
|
|
6
|
+
const VULNERABILITY_HASHES = new LRU({ max: VULNERABILITY_HASHES_MAX_SIZE })
|
|
4
7
|
|
|
5
8
|
function createVulnerability (type, evidence, spanId, location) {
|
|
6
9
|
if (type && evidence && spanId) {
|
|
@@ -94,20 +97,25 @@ function sendVulnerabilities (iastContext) {
|
|
|
94
97
|
return IAST_JSON_TAG_KEY
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
function clearCache () { // only for test purposes
|
|
101
|
+
VULNERABILITY_HASHES.clear()
|
|
102
|
+
}
|
|
103
|
+
|
|
97
104
|
function deduplicateVulnerabilities (vulnerabilities) {
|
|
98
|
-
const
|
|
99
|
-
return vulnerabilities.filter((vulnerability) => {
|
|
105
|
+
const deduplicated = vulnerabilities.filter((vulnerability) => {
|
|
100
106
|
const key = `${vulnerability.type}${vulnerability.hash}`
|
|
101
|
-
if (!
|
|
102
|
-
|
|
107
|
+
if (!VULNERABILITY_HASHES.get(key)) {
|
|
108
|
+
VULNERABILITY_HASHES.set(key, true)
|
|
103
109
|
return true
|
|
104
110
|
}
|
|
105
111
|
return false
|
|
106
112
|
})
|
|
113
|
+
return deduplicated
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
module.exports = {
|
|
110
117
|
createVulnerability,
|
|
111
118
|
addVulnerability,
|
|
112
|
-
sendVulnerabilities
|
|
119
|
+
sendVulnerabilities,
|
|
120
|
+
clearCache
|
|
113
121
|
}
|