dd-trace 4.19.0 → 4.21.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 +1 -0
- package/index.d.ts +24 -0
- package/package.json +6 -5
- package/packages/datadog-instrumentations/src/aerospike.js +47 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/http/client.js +22 -0
- package/packages/datadog-instrumentations/src/jest.js +11 -5
- package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
- package/packages/datadog-instrumentations/src/next.js +3 -1
- package/packages/datadog-instrumentations/src/restify.js +1 -1
- package/packages/datadog-plugin-aerospike/src/index.js +113 -0
- package/packages/datadog-plugin-http/src/client.js +19 -2
- package/packages/datadog-plugin-kafkajs/src/consumer.js +51 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +55 -0
- package/packages/datadog-plugin-next/src/index.js +7 -7
- package/packages/dd-trace/src/appsec/addresses.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +14 -2
- package/packages/dd-trace/src/appsec/recommended.json +1395 -2
- package/packages/dd-trace/src/appsec/reporter.js +19 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -3
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +15 -9
- package/packages/dd-trace/src/config.js +37 -3
- package/packages/dd-trace/src/datastreams/processor.js +107 -12
- package/packages/dd-trace/src/id.js +12 -0
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
- package/packages/dd-trace/src/opentracing/span.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/git.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +3 -2
- package/packages/dd-trace/src/profiler.js +5 -3
- package/packages/dd-trace/src/profiling/config.js +17 -10
- package/packages/dd-trace/src/profiling/profiler.js +10 -4
- package/packages/dd-trace/src/profiling/profilers/events.js +171 -73
- package/packages/dd-trace/src/profiling/profilers/wall.js +93 -67
- package/packages/dd-trace/src/proxy.js +21 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/spanleak.js +98 -0
- package/packages/dd-trace/src/startup-log.js +7 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +55 -9
- package/packages/dd-trace/src/telemetry/index.js +135 -43
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +47 -5
- package/packages/dd-trace/src/tracer.js +4 -0
- package/scripts/install_plugin_modules.js +11 -3
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
incrementWafUpdatesMetric,
|
|
11
11
|
incrementWafRequestsMetric
|
|
12
12
|
} = require('./telemetry')
|
|
13
|
+
const zlib = require('zlib')
|
|
13
14
|
|
|
14
15
|
// default limiter, configurable with setRateLimit()
|
|
15
16
|
let limiter = new Limiter(100)
|
|
@@ -140,6 +141,23 @@ function reportAttack (attackData) {
|
|
|
140
141
|
rootSpan.addTags(newTags)
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
function reportSchemas (derivatives) {
|
|
145
|
+
if (!derivatives) return
|
|
146
|
+
|
|
147
|
+
const req = storage.getStore()?.req
|
|
148
|
+
const rootSpan = web.root(req)
|
|
149
|
+
|
|
150
|
+
if (!rootSpan) return
|
|
151
|
+
|
|
152
|
+
const tags = {}
|
|
153
|
+
for (const [address, value] of Object.entries(derivatives)) {
|
|
154
|
+
const gzippedValue = zlib.gzipSync(JSON.stringify(value))
|
|
155
|
+
tags[address] = gzippedValue.toString('base64')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
rootSpan.addTags(tags)
|
|
159
|
+
}
|
|
160
|
+
|
|
143
161
|
function finishRequest (req, res) {
|
|
144
162
|
const rootSpan = web.root(req)
|
|
145
163
|
if (!rootSpan) return
|
|
@@ -175,6 +193,7 @@ module.exports = {
|
|
|
175
193
|
reportMetrics,
|
|
176
194
|
reportAttack,
|
|
177
195
|
reportWafUpdate: incrementWafUpdatesMetric,
|
|
196
|
+
reportSchemas,
|
|
178
197
|
finishRequest,
|
|
179
198
|
setRateLimit,
|
|
180
199
|
mapHeaderAndTags
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const fs = require('fs')
|
|
3
4
|
const waf = require('./waf')
|
|
4
5
|
const { ACKNOWLEDGED, ERROR } = require('./remote_config/apply_states')
|
|
5
6
|
const blocking = require('./blocking')
|
|
@@ -13,13 +14,15 @@ let appliedExclusions = new Map()
|
|
|
13
14
|
let appliedCustomRules = new Map()
|
|
14
15
|
let appliedActions = new Map()
|
|
15
16
|
|
|
16
|
-
function
|
|
17
|
-
defaultRules = rules
|
|
17
|
+
function loadRules (config) {
|
|
18
|
+
defaultRules = config.rules
|
|
19
|
+
? JSON.parse(fs.readFileSync(config.rules))
|
|
20
|
+
: require('./recommended.json')
|
|
18
21
|
|
|
19
|
-
waf.init(
|
|
22
|
+
waf.init(defaultRules, config)
|
|
20
23
|
|
|
21
|
-
if (
|
|
22
|
-
blocking.updateBlockingConfiguration(
|
|
24
|
+
if (defaultRules.actions) {
|
|
25
|
+
blocking.updateBlockingConfiguration(defaultRules.actions.find(action => action.id === 'block'))
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -252,7 +255,7 @@ function clearAllRules () {
|
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
module.exports = {
|
|
255
|
-
|
|
258
|
+
loadRules,
|
|
256
259
|
updateWafFromRC,
|
|
257
260
|
clearAllRules
|
|
258
261
|
}
|
|
@@ -10,9 +10,8 @@ const preventDuplicateAddresses = new Set([
|
|
|
10
10
|
])
|
|
11
11
|
|
|
12
12
|
class WAFContextWrapper {
|
|
13
|
-
constructor (ddwafContext,
|
|
13
|
+
constructor (ddwafContext, wafTimeout, wafVersion, rulesVersion) {
|
|
14
14
|
this.ddwafContext = ddwafContext
|
|
15
|
-
this.requiredAddresses = requiredAddresses
|
|
16
15
|
this.wafTimeout = wafTimeout
|
|
17
16
|
this.wafVersion = wafVersion
|
|
18
17
|
this.rulesVersion = rulesVersion
|
|
@@ -26,7 +25,9 @@ class WAFContextWrapper {
|
|
|
26
25
|
|
|
27
26
|
// TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
|
|
28
27
|
for (const key of Object.keys(params)) {
|
|
29
|
-
|
|
28
|
+
// TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
|
|
29
|
+
// future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
|
|
30
|
+
if (!this.addressesToSkip.has(key)) {
|
|
30
31
|
inputs[key] = params[key]
|
|
31
32
|
if (preventDuplicateAddresses.has(key)) {
|
|
32
33
|
newAddressesToSkip.add(key)
|
|
@@ -63,6 +64,8 @@ class WAFContextWrapper {
|
|
|
63
64
|
Reporter.reportAttack(JSON.stringify(result.events))
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
Reporter.reportSchemas(result.derivatives)
|
|
68
|
+
|
|
66
69
|
return result.actions
|
|
67
70
|
} catch (err) {
|
|
68
71
|
log.error('Error while running the AppSec WAF')
|
|
@@ -143,7 +143,23 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
143
143
|
* where the tests run in a subprocess, because `getItrConfiguration` is called only once.
|
|
144
144
|
*/
|
|
145
145
|
this._itrConfig = itrConfig
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
if (err) {
|
|
148
|
+
callback(err, {})
|
|
149
|
+
} else if (itrConfig?.requireGit) {
|
|
150
|
+
// If the backend requires git, we'll wait for the upload to finish and request settings again
|
|
151
|
+
this._gitUploadPromise.then(gitUploadError => {
|
|
152
|
+
if (gitUploadError) {
|
|
153
|
+
return callback(gitUploadError, {})
|
|
154
|
+
}
|
|
155
|
+
getItrConfigurationRequest(configuration, (err, finalItrConfig) => {
|
|
156
|
+
this._itrConfig = finalItrConfig
|
|
157
|
+
callback(err, finalItrConfig)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
} else {
|
|
161
|
+
callback(null, itrConfig)
|
|
162
|
+
}
|
|
147
163
|
})
|
|
148
164
|
})
|
|
149
165
|
}
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
getLatestCommits,
|
|
11
11
|
getRepositoryUrl,
|
|
12
12
|
generatePackFilesForCommits,
|
|
13
|
-
|
|
13
|
+
getCommitsRevList,
|
|
14
14
|
isShallowRepository,
|
|
15
15
|
unshallowRepository
|
|
16
16
|
} = require('../../../plugins/util/git')
|
|
@@ -46,11 +46,7 @@ function getCommonRequestOptions (url) {
|
|
|
46
46
|
* The response are the commits for which the backend already has information
|
|
47
47
|
* This response is used to know which commits can be ignored from there on
|
|
48
48
|
*/
|
|
49
|
-
function
|
|
50
|
-
const latestCommits = getLatestCommits()
|
|
51
|
-
|
|
52
|
-
log.debug(`There were ${latestCommits.length} commits since last month.`)
|
|
53
|
-
|
|
49
|
+
function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy }, callback) {
|
|
54
50
|
const commonOptions = getCommonRequestOptions(url)
|
|
55
51
|
|
|
56
52
|
const options = {
|
|
@@ -83,13 +79,23 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
|
|
|
83
79
|
const error = new Error(`Error fetching commits to exclude: ${err.message}`)
|
|
84
80
|
return callback(error)
|
|
85
81
|
}
|
|
86
|
-
let
|
|
82
|
+
let alreadySeenCommits
|
|
87
83
|
try {
|
|
88
|
-
|
|
84
|
+
alreadySeenCommits = validateCommits(JSON.parse(response).data)
|
|
89
85
|
} catch (e) {
|
|
90
86
|
return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
|
|
91
87
|
}
|
|
92
|
-
|
|
88
|
+
log.debug(`There are ${alreadySeenCommits.length} commits to exclude.`)
|
|
89
|
+
const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
|
|
90
|
+
log.debug(`There are ${commitsToInclude.length} commits to include.`)
|
|
91
|
+
|
|
92
|
+
if (!commitsToInclude.length) {
|
|
93
|
+
return callback(null, [])
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const commitsToUpload = getCommitsRevList(alreadySeenCommits, commitsToInclude)
|
|
97
|
+
|
|
98
|
+
callback(null, commitsToUpload)
|
|
93
99
|
})
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -150,6 +156,53 @@ function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, hea
|
|
|
150
156
|
})
|
|
151
157
|
}
|
|
152
158
|
|
|
159
|
+
function generateAndUploadPackFiles ({
|
|
160
|
+
url,
|
|
161
|
+
isEvpProxy,
|
|
162
|
+
commitsToUpload,
|
|
163
|
+
repositoryUrl,
|
|
164
|
+
headCommit
|
|
165
|
+
}, callback) {
|
|
166
|
+
log.debug(`There are ${commitsToUpload.length} commits to upload`)
|
|
167
|
+
|
|
168
|
+
const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
|
|
169
|
+
|
|
170
|
+
log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
|
|
171
|
+
|
|
172
|
+
if (!packFilesToUpload.length) {
|
|
173
|
+
return callback(new Error('Failed to generate packfiles'))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let packFileIndex = 0
|
|
177
|
+
// This uploads packfiles sequentially
|
|
178
|
+
const uploadPackFileCallback = (err) => {
|
|
179
|
+
if (err || packFileIndex === packFilesToUpload.length) {
|
|
180
|
+
return callback(err)
|
|
181
|
+
}
|
|
182
|
+
return uploadPackFile(
|
|
183
|
+
{
|
|
184
|
+
packFileToUpload: packFilesToUpload[packFileIndex++],
|
|
185
|
+
url,
|
|
186
|
+
isEvpProxy,
|
|
187
|
+
repositoryUrl,
|
|
188
|
+
headCommit
|
|
189
|
+
},
|
|
190
|
+
uploadPackFileCallback
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
uploadPackFile(
|
|
195
|
+
{
|
|
196
|
+
packFileToUpload: packFilesToUpload[packFileIndex++],
|
|
197
|
+
url,
|
|
198
|
+
isEvpProxy,
|
|
199
|
+
repositoryUrl,
|
|
200
|
+
headCommit
|
|
201
|
+
},
|
|
202
|
+
uploadPackFileCallback
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
153
206
|
/**
|
|
154
207
|
* This function uploads git metadata to CI Visibility's backend.
|
|
155
208
|
*/
|
|
@@ -165,65 +218,31 @@ function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
|
|
|
165
218
|
return callback(new Error('Repository URL is empty'))
|
|
166
219
|
}
|
|
167
220
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
221
|
+
const latestCommits = getLatestCommits()
|
|
222
|
+
log.debug(`There were ${latestCommits.length} commits since last month.`)
|
|
223
|
+
const [headCommit] = latestCommits
|
|
172
224
|
|
|
173
|
-
|
|
225
|
+
const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
|
|
174
226
|
if (err) {
|
|
175
227
|
return callback(err)
|
|
176
228
|
}
|
|
177
|
-
log.debug(`There are ${commitsToExclude.length} commits to exclude.`)
|
|
178
|
-
const [headCommit] = latestCommits
|
|
179
|
-
const commitsToInclude = latestCommits.filter((commit) => !commitsToExclude.includes(commit))
|
|
180
|
-
log.debug(`There are ${commitsToInclude.length} commits to include.`)
|
|
181
|
-
|
|
182
|
-
const commitsToUpload = getCommitsToUpload(commitsToExclude, commitsToInclude)
|
|
183
229
|
|
|
184
230
|
if (!commitsToUpload.length) {
|
|
185
231
|
log.debug('No commits to upload')
|
|
186
232
|
return callback(null)
|
|
187
233
|
}
|
|
188
|
-
log.debug(`There are ${commitsToUpload.length} commits to upload`)
|
|
189
|
-
|
|
190
|
-
const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
|
|
191
|
-
|
|
192
|
-
log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
|
|
193
|
-
|
|
194
|
-
if (!packFilesToUpload.length) {
|
|
195
|
-
return callback(new Error('Failed to generate packfiles'))
|
|
196
|
-
}
|
|
197
234
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (err || packFileIndex === packFilesToUpload.length) {
|
|
202
|
-
return callback(err)
|
|
203
|
-
}
|
|
204
|
-
return uploadPackFile(
|
|
205
|
-
{
|
|
206
|
-
packFileToUpload: packFilesToUpload[packFileIndex++],
|
|
207
|
-
url,
|
|
208
|
-
isEvpProxy,
|
|
209
|
-
repositoryUrl,
|
|
210
|
-
headCommit
|
|
211
|
-
},
|
|
212
|
-
uploadPackFileCallback
|
|
213
|
-
)
|
|
235
|
+
// If it has already unshallowed or the clone is not shallow, we move on
|
|
236
|
+
if (hasCheckedShallow || !isShallowRepository()) {
|
|
237
|
+
return generateAndUploadPackFiles({ url, isEvpProxy, commitsToUpload, repositoryUrl, headCommit }, callback)
|
|
214
238
|
}
|
|
239
|
+
// Otherwise we unshallow and get commits to upload again
|
|
240
|
+
log.debug('It is shallow clone, unshallowing...')
|
|
241
|
+
unshallowRepository()
|
|
242
|
+
getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(true))
|
|
243
|
+
}
|
|
215
244
|
|
|
216
|
-
|
|
217
|
-
{
|
|
218
|
-
packFileToUpload: packFilesToUpload[packFileIndex++],
|
|
219
|
-
url,
|
|
220
|
-
isEvpProxy,
|
|
221
|
-
repositoryUrl,
|
|
222
|
-
headCommit
|
|
223
|
-
},
|
|
224
|
-
uploadPackFileCallback
|
|
225
|
-
)
|
|
226
|
-
})
|
|
245
|
+
getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(false))
|
|
227
246
|
}
|
|
228
247
|
|
|
229
248
|
module.exports = {
|
package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js
CHANGED
|
@@ -15,6 +15,7 @@ function getItrConfiguration ({
|
|
|
15
15
|
runtimeName,
|
|
16
16
|
runtimeVersion,
|
|
17
17
|
branch,
|
|
18
|
+
testLevel = 'suite',
|
|
18
19
|
custom
|
|
19
20
|
}, done) {
|
|
20
21
|
const options = {
|
|
@@ -23,7 +24,8 @@ function getItrConfiguration ({
|
|
|
23
24
|
headers: {
|
|
24
25
|
'Content-Type': 'application/json'
|
|
25
26
|
},
|
|
26
|
-
url
|
|
27
|
+
url,
|
|
28
|
+
timeout: 20000
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
if (isEvpProxy) {
|
|
@@ -42,7 +44,7 @@ function getItrConfiguration ({
|
|
|
42
44
|
id: id().toString(10),
|
|
43
45
|
type: 'ci_app_test_service_libraries_settings',
|
|
44
46
|
attributes: {
|
|
45
|
-
test_level:
|
|
47
|
+
test_level: testLevel,
|
|
46
48
|
configurations: {
|
|
47
49
|
'os.platform': osPlatform,
|
|
48
50
|
'os.version': osVersion,
|
|
@@ -67,25 +69,29 @@ function getItrConfiguration ({
|
|
|
67
69
|
try {
|
|
68
70
|
const {
|
|
69
71
|
data: {
|
|
70
|
-
attributes
|
|
72
|
+
attributes: {
|
|
73
|
+
code_coverage: isCodeCoverageEnabled,
|
|
74
|
+
tests_skipping: isSuitesSkippingEnabled,
|
|
75
|
+
itr_enabled: isItrEnabled,
|
|
76
|
+
require_git: requireGit
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
} = JSON.parse(res)
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
let isSuitesSkippingEnabled = attributes.tests_skipping
|
|
81
|
+
const settings = { isCodeCoverageEnabled, isSuitesSkippingEnabled, isItrEnabled, requireGit }
|
|
76
82
|
|
|
77
|
-
log.debug(() => `Remote settings: ${
|
|
83
|
+
log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
|
|
78
84
|
|
|
79
85
|
if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE) {
|
|
80
|
-
isCodeCoverageEnabled = true
|
|
86
|
+
settings.isCodeCoverageEnabled = true
|
|
81
87
|
log.debug(() => 'Dangerously set code coverage to true')
|
|
82
88
|
}
|
|
83
89
|
if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING) {
|
|
84
|
-
isSuitesSkippingEnabled = true
|
|
90
|
+
settings.isSuitesSkippingEnabled = true
|
|
85
91
|
log.debug(() => 'Dangerously set test skipping to true')
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
done(null,
|
|
94
|
+
done(null, settings)
|
|
89
95
|
} catch (err) {
|
|
90
96
|
done(err)
|
|
91
97
|
}
|
|
@@ -309,6 +309,10 @@ class Config {
|
|
|
309
309
|
options.tracePropagationStyle,
|
|
310
310
|
defaultPropagationStyle
|
|
311
311
|
)
|
|
312
|
+
const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce(
|
|
313
|
+
process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST,
|
|
314
|
+
false
|
|
315
|
+
)
|
|
312
316
|
const DD_TRACE_RUNTIME_ID_ENABLED = coalesce(
|
|
313
317
|
options.experimental && options.experimental.runtimeId,
|
|
314
318
|
process.env.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED,
|
|
@@ -395,7 +399,6 @@ class Config {
|
|
|
395
399
|
appsec.enabled,
|
|
396
400
|
process.env.DD_APPSEC_ENABLED && isTrue(process.env.DD_APPSEC_ENABLED)
|
|
397
401
|
)
|
|
398
|
-
|
|
399
402
|
const DD_APPSEC_RULES = coalesce(
|
|
400
403
|
appsec.rules,
|
|
401
404
|
process.env.DD_APPSEC_RULES
|
|
@@ -437,6 +440,16 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
437
440
|
process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING,
|
|
438
441
|
'safe'
|
|
439
442
|
).toLowerCase()
|
|
443
|
+
const DD_EXPERIMENTAL_API_SECURITY_ENABLED = coalesce(
|
|
444
|
+
appsec?.apiSecurity?.enabled,
|
|
445
|
+
isTrue(process.env.DD_EXPERIMENTAL_API_SECURITY_ENABLED),
|
|
446
|
+
false
|
|
447
|
+
)
|
|
448
|
+
const DD_API_SECURITY_REQUEST_SAMPLE_RATE = coalesce(
|
|
449
|
+
appsec?.apiSecurity?.requestSampling,
|
|
450
|
+
parseFloat(process.env.DD_API_SECURITY_REQUEST_SAMPLE_RATE),
|
|
451
|
+
0.1
|
|
452
|
+
)
|
|
440
453
|
|
|
441
454
|
const remoteConfigOptions = options.remoteConfig || {}
|
|
442
455
|
const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
|
|
@@ -461,6 +474,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
461
474
|
DD_IAST_ENABLED
|
|
462
475
|
)
|
|
463
476
|
|
|
477
|
+
const DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED = coalesce(
|
|
478
|
+
process.env.DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED,
|
|
479
|
+
true
|
|
480
|
+
)
|
|
481
|
+
|
|
464
482
|
const defaultIastRequestSampling = 30
|
|
465
483
|
const iastRequestSampling = coalesce(
|
|
466
484
|
parseInt(iastOptions?.requestSampling),
|
|
@@ -522,6 +540,12 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
522
540
|
true
|
|
523
541
|
)
|
|
524
542
|
|
|
543
|
+
// 0: disabled, 1: logging, 2: garbage collection + logging
|
|
544
|
+
const DD_TRACE_SPAN_LEAK_DEBUG = coalesce(
|
|
545
|
+
process.env.DD_TRACE_SPAN_LEAK_DEBUG,
|
|
546
|
+
0
|
|
547
|
+
)
|
|
548
|
+
|
|
525
549
|
const ingestion = options.ingestion || {}
|
|
526
550
|
const dogstatsd = coalesce(options.dogstatsd, {})
|
|
527
551
|
const sampler = {
|
|
@@ -579,6 +603,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
579
603
|
inject: DD_TRACE_PROPAGATION_STYLE_INJECT,
|
|
580
604
|
extract: DD_TRACE_PROPAGATION_STYLE_EXTRACT
|
|
581
605
|
}
|
|
606
|
+
this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST)
|
|
582
607
|
this.experimental = {
|
|
583
608
|
runtimeId: isTrue(DD_TRACE_RUNTIME_ID_ENABLED),
|
|
584
609
|
exporter: DD_TRACE_EXPORTER,
|
|
@@ -604,13 +629,14 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
604
629
|
heartbeatInterval: DD_TELEMETRY_HEARTBEAT_INTERVAL,
|
|
605
630
|
debug: isTrue(DD_TELEMETRY_DEBUG),
|
|
606
631
|
logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
|
|
607
|
-
metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED)
|
|
632
|
+
metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED),
|
|
633
|
+
dependencyCollection: DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED
|
|
608
634
|
}
|
|
609
635
|
this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
|
|
610
636
|
this.tagsHeaderMaxLength = parseInt(DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH)
|
|
611
637
|
this.appsec = {
|
|
612
638
|
enabled: DD_APPSEC_ENABLED,
|
|
613
|
-
rules: DD_APPSEC_RULES
|
|
639
|
+
rules: DD_APPSEC_RULES,
|
|
614
640
|
customRulesProvided: !!DD_APPSEC_RULES,
|
|
615
641
|
rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
|
|
616
642
|
wafTimeout: DD_APPSEC_WAF_TIMEOUT,
|
|
@@ -621,8 +647,14 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
621
647
|
eventTracking: {
|
|
622
648
|
enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING),
|
|
623
649
|
mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING
|
|
650
|
+
},
|
|
651
|
+
apiSecurity: {
|
|
652
|
+
enabled: DD_EXPERIMENTAL_API_SECURITY_ENABLED,
|
|
653
|
+
// Coerce value between 0 and 1
|
|
654
|
+
requestSampling: Math.min(1, Math.max(0, DD_API_SECURITY_REQUEST_SAMPLE_RATE))
|
|
624
655
|
}
|
|
625
656
|
}
|
|
657
|
+
|
|
626
658
|
this.remoteConfig = {
|
|
627
659
|
enabled: DD_REMOTE_CONFIGURATION_ENABLED,
|
|
628
660
|
pollInterval: DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
|
|
@@ -696,6 +728,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
696
728
|
this.isGCPFunction = isGCPFunction
|
|
697
729
|
this.isAzureFunctionConsumptionPlan = isAzureFunctionConsumptionPlan
|
|
698
730
|
|
|
731
|
+
this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG)
|
|
732
|
+
|
|
699
733
|
tagger.add(this.tags, {
|
|
700
734
|
service: this.service,
|
|
701
735
|
env: this.env,
|
|
@@ -45,14 +45,73 @@ class StatsPoint {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
class
|
|
48
|
+
class Backlog {
|
|
49
|
+
constructor ({ offset, ...tags }) {
|
|
50
|
+
this._tags = Object.keys(tags).sort().map(key => `${key}:${tags[key]}`)
|
|
51
|
+
this._hash = this._tags.join(',')
|
|
52
|
+
this._offset = offset
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get hash () { return this._hash }
|
|
56
|
+
|
|
57
|
+
get offset () { return this._offset }
|
|
58
|
+
|
|
59
|
+
get tags () { return this._tags }
|
|
60
|
+
|
|
61
|
+
encode () {
|
|
62
|
+
return {
|
|
63
|
+
Tags: this.tags,
|
|
64
|
+
Value: this.offset
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class StatsBucket {
|
|
70
|
+
constructor () {
|
|
71
|
+
this._checkpoints = new Map()
|
|
72
|
+
this._backlogs = new Map()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get checkpoints () {
|
|
76
|
+
return this._checkpoints
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get backlogs () {
|
|
80
|
+
return this._backlogs
|
|
81
|
+
}
|
|
82
|
+
|
|
49
83
|
forCheckpoint (checkpoint) {
|
|
50
84
|
const key = checkpoint.hash
|
|
51
|
-
if (!this.has(key)) {
|
|
52
|
-
this.set(
|
|
85
|
+
if (!this._checkpoints.has(key)) {
|
|
86
|
+
this._checkpoints.set(
|
|
87
|
+
key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)
|
|
88
|
+
)
|
|
53
89
|
}
|
|
54
90
|
|
|
55
|
-
return this.get(key)
|
|
91
|
+
return this._checkpoints.get(key)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Conditionally add a backlog to the bucket. If there is currently an offset
|
|
96
|
+
* matching the backlog's tags, overwrite the offset IFF the backlog's offset
|
|
97
|
+
* is greater than the recorded offset.
|
|
98
|
+
*
|
|
99
|
+
* @typedef {{[key: string]: string}} BacklogData
|
|
100
|
+
* @property {number} offset
|
|
101
|
+
*
|
|
102
|
+
* @param {BacklogData} backlogData
|
|
103
|
+
* @returns {Backlog}
|
|
104
|
+
*/
|
|
105
|
+
forBacklog (backlogData) {
|
|
106
|
+
const backlog = new Backlog(backlogData)
|
|
107
|
+
const existingBacklog = this._backlogs.get(backlog.hash)
|
|
108
|
+
if (existingBacklog !== undefined) {
|
|
109
|
+
if (existingBacklog.offset > backlog.offset) {
|
|
110
|
+
return existingBacklog
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this._backlogs.set(backlog.hash, backlog)
|
|
114
|
+
return backlog
|
|
56
115
|
}
|
|
57
116
|
}
|
|
58
117
|
|
|
@@ -122,12 +181,12 @@ class DataStreamsProcessor {
|
|
|
122
181
|
}
|
|
123
182
|
|
|
124
183
|
onInterval () {
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
184
|
+
const { Stats } = this._serializeBuckets()
|
|
185
|
+
if (Stats.length === 0) return
|
|
127
186
|
const payload = {
|
|
128
187
|
Env: this.env,
|
|
129
188
|
Service: this.service,
|
|
130
|
-
Stats
|
|
189
|
+
Stats,
|
|
131
190
|
TracerVersion: pkg.version,
|
|
132
191
|
Version: this.version,
|
|
133
192
|
Lang: 'javascript'
|
|
@@ -135,10 +194,19 @@ class DataStreamsProcessor {
|
|
|
135
194
|
this.writer.flush(payload)
|
|
136
195
|
}
|
|
137
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Given a timestamp in nanoseconds, compute and return the closest TimeBucket
|
|
199
|
+
* @param {number} timestamp
|
|
200
|
+
* @returns {StatsBucket}
|
|
201
|
+
*/
|
|
202
|
+
bucketFromTimestamp (timestamp) {
|
|
203
|
+
const bucketTime = Math.round(timestamp - (timestamp % this.bucketSizeNs))
|
|
204
|
+
return this.buckets.forTime(bucketTime)
|
|
205
|
+
}
|
|
206
|
+
|
|
138
207
|
recordCheckpoint (checkpoint, span = null) {
|
|
139
208
|
if (!this.enabled) return
|
|
140
|
-
|
|
141
|
-
this.buckets.forTime(bucketTime)
|
|
209
|
+
this.bucketFromTimestamp(checkpoint.currentTimestamp)
|
|
142
210
|
.forCheckpoint(checkpoint)
|
|
143
211
|
.addLatencies(checkpoint)
|
|
144
212
|
// set DSM pathway hash on span to enable related traces feature on DSM tab, convert from buffer to uint64
|
|
@@ -207,26 +275,52 @@ class DataStreamsProcessor {
|
|
|
207
275
|
return dataStreamsContext
|
|
208
276
|
}
|
|
209
277
|
|
|
278
|
+
recordOffset ({ timestamp, ...backlogData }) {
|
|
279
|
+
if (!this.enabled) return
|
|
280
|
+
return this.bucketFromTimestamp(timestamp)
|
|
281
|
+
.forBacklog(backlogData)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
setOffset (offsetObj) {
|
|
285
|
+
if (!this.enabled) return
|
|
286
|
+
const nowNs = Date.now() * 1e6
|
|
287
|
+
const backlogData = {
|
|
288
|
+
...offsetObj,
|
|
289
|
+
timestamp: nowNs
|
|
290
|
+
}
|
|
291
|
+
this.recordOffset(backlogData)
|
|
292
|
+
}
|
|
293
|
+
|
|
210
294
|
_serializeBuckets () {
|
|
295
|
+
// TimeBuckets
|
|
211
296
|
const serializedBuckets = []
|
|
212
297
|
|
|
213
298
|
for (const [ timeNs, bucket ] of this.buckets.entries()) {
|
|
214
299
|
const points = []
|
|
215
300
|
|
|
216
|
-
|
|
301
|
+
// bucket: StatsBucket
|
|
302
|
+
// stats: StatsPoint
|
|
303
|
+
for (const stats of bucket._checkpoints.values()) {
|
|
217
304
|
points.push(stats.encode())
|
|
218
305
|
}
|
|
219
306
|
|
|
307
|
+
const backlogs = []
|
|
308
|
+
for (const backlog of bucket._backlogs.values()) {
|
|
309
|
+
backlogs.push(backlog.encode())
|
|
310
|
+
}
|
|
220
311
|
serializedBuckets.push({
|
|
221
312
|
Start: new Uint64(timeNs),
|
|
222
313
|
Duration: new Uint64(this.bucketSizeNs),
|
|
223
|
-
Stats: points
|
|
314
|
+
Stats: points,
|
|
315
|
+
Backlogs: backlogs
|
|
224
316
|
})
|
|
225
317
|
}
|
|
226
318
|
|
|
227
319
|
this.buckets.clear()
|
|
228
320
|
|
|
229
|
-
return
|
|
321
|
+
return {
|
|
322
|
+
Stats: serializedBuckets
|
|
323
|
+
}
|
|
230
324
|
}
|
|
231
325
|
}
|
|
232
326
|
|
|
@@ -234,6 +328,7 @@ module.exports = {
|
|
|
234
328
|
DataStreamsProcessor: DataStreamsProcessor,
|
|
235
329
|
StatsPoint: StatsPoint,
|
|
236
330
|
StatsBucket: StatsBucket,
|
|
331
|
+
Backlog,
|
|
237
332
|
TimeBuckets,
|
|
238
333
|
getMessageSize,
|
|
239
334
|
getHeadersSize,
|
|
@@ -42,6 +42,18 @@ class Identifier {
|
|
|
42
42
|
toJSON () {
|
|
43
43
|
return this.toString()
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
equals (other) {
|
|
47
|
+
const length = this._buffer.length
|
|
48
|
+
const otherLength = other._buffer.length
|
|
49
|
+
|
|
50
|
+
// Only compare the bytes available in both IDs.
|
|
51
|
+
for (let i = length, j = otherLength; i >= 0 && j >= 0; i--, j--) {
|
|
52
|
+
if (this._buffer[i] !== other._buffer[j]) return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
// Create a buffer, using an optional hexadecimal value if provided.
|