dd-trace 4.25.0 → 4.27.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 -3
- package/README.md +1 -32
- package/ci/init.js +1 -4
- package/index.d.ts +21 -0
- package/package.json +6 -8
- package/packages/datadog-instrumentations/src/amqplib.js +2 -2
- package/packages/datadog-instrumentations/src/child_process.js +150 -0
- package/packages/datadog-instrumentations/src/cucumber.js +12 -12
- package/packages/datadog-instrumentations/src/express.js +20 -0
- package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -2
- package/packages/datadog-instrumentations/src/jest.js +147 -10
- package/packages/datadog-instrumentations/src/mocha.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +23 -10
- package/packages/datadog-instrumentations/src/mquery.js +65 -0
- package/packages/datadog-instrumentations/src/next.js +17 -3
- package/packages/datadog-instrumentations/src/playwright.js +41 -9
- package/packages/datadog-plugin-amqplib/src/consumer.js +10 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +14 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +134 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +19 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -10
- package/packages/datadog-plugin-child_process/src/index.js +91 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
- package/packages/datadog-plugin-cucumber/src/index.js +16 -11
- package/packages/datadog-plugin-cypress/src/plugin.js +25 -12
- package/packages/datadog-plugin-graphql/src/index.js +1 -6
- package/packages/datadog-plugin-grpc/src/client.js +16 -2
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +47 -6
- package/packages/datadog-plugin-mocha/src/index.js +14 -5
- package/packages/datadog-plugin-playwright/src/index.js +19 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +11 -1
- package/packages/datadog-plugin-rhea/src/producer.js +11 -0
- package/packages/dd-trace/src/appsec/addresses.js +2 -0
- package/packages/dd-trace/src/appsec/api_security_sampler.js +16 -3
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -1
- package/packages/dd-trace/src/appsec/index.js +17 -2
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -2
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +83 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +83 -41
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +30 -8
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -1
- package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → requests/get-library-configuration.js} +18 -6
- package/packages/dd-trace/src/config.js +22 -9
- package/packages/dd-trace/src/datastreams/processor.js +36 -5
- package/packages/dd-trace/src/datastreams/writer.js +11 -5
- package/packages/dd-trace/src/dogstatsd.js +3 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +5 -3
- package/packages/dd-trace/src/exporters/common/request.js +21 -3
- package/packages/dd-trace/src/format.js +25 -1
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +9 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -1
- package/packages/dd-trace/src/opentracing/span.js +38 -0
- package/packages/dd-trace/src/opentracing/span_context.js +12 -6
- package/packages/dd-trace/src/opentracing/tracer.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +24 -8
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -0
- package/packages/dd-trace/src/plugins/util/test.js +36 -7
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/profiling/config.js +22 -22
- package/packages/dd-trace/src/proxy.js +31 -23
- package/packages/dd-trace/src/span_processor.js +5 -1
- package/packages/dd-trace/src/telemetry/index.js +3 -0
- package/packages/dd-trace/src/tracer.js +1 -0
- package/packages/utils/src/kebabcase.js +16 -0
- package/packages/utils/src/pick.js +11 -0
- package/packages/utils/src/uniq.js +5 -0
- package/packages/datadog-instrumentations/src/child-process.js +0 -29
- package/packages/dd-trace/src/plugins/util/exec.js +0 -34
|
@@ -24,7 +24,8 @@ const {
|
|
|
24
24
|
TEST_SKIPPED_BY_ITR,
|
|
25
25
|
TEST_ITR_UNSKIPPABLE,
|
|
26
26
|
TEST_ITR_FORCED_RUN,
|
|
27
|
-
ITR_CORRELATION_ID
|
|
27
|
+
ITR_CORRELATION_ID,
|
|
28
|
+
TEST_SOURCE_FILE
|
|
28
29
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
29
30
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
30
31
|
const log = require('../../dd-trace/src/log')
|
|
@@ -45,7 +46,8 @@ const {
|
|
|
45
46
|
GIT_REPOSITORY_URL,
|
|
46
47
|
GIT_COMMIT_SHA,
|
|
47
48
|
GIT_BRANCH,
|
|
48
|
-
CI_PROVIDER_NAME
|
|
49
|
+
CI_PROVIDER_NAME,
|
|
50
|
+
CI_WORKSPACE_PATH
|
|
49
51
|
} = require('../../dd-trace/src/plugins/util/tags')
|
|
50
52
|
const {
|
|
51
53
|
OS_VERSION,
|
|
@@ -119,14 +121,14 @@ function getSuiteStatus (suiteStats) {
|
|
|
119
121
|
return 'pass'
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
function
|
|
124
|
+
function getLibraryConfiguration (tracer, testConfiguration) {
|
|
123
125
|
return new Promise(resolve => {
|
|
124
|
-
if (!tracer._tracer._exporter
|
|
126
|
+
if (!tracer._tracer._exporter?.getLibraryConfiguration) {
|
|
125
127
|
return resolve({ err: new Error('CI Visibility was not initialized correctly') })
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
tracer._tracer._exporter.
|
|
129
|
-
resolve({ err,
|
|
130
|
+
tracer._tracer._exporter.getLibraryConfiguration(testConfiguration, (err, libraryConfig) => {
|
|
131
|
+
resolve({ err, libraryConfig })
|
|
130
132
|
})
|
|
131
133
|
})
|
|
132
134
|
}
|
|
@@ -136,7 +138,7 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
|
136
138
|
return Promise.resolve({ skippableTests: [] })
|
|
137
139
|
}
|
|
138
140
|
return new Promise(resolve => {
|
|
139
|
-
if (!tracer._tracer._exporter
|
|
141
|
+
if (!tracer._tracer._exporter?.getLibraryConfiguration) {
|
|
140
142
|
return resolve({ err: new Error('CI Visibility was not initialized correctly') })
|
|
141
143
|
}
|
|
142
144
|
tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
|
|
@@ -186,7 +188,8 @@ module.exports = (on, config) => {
|
|
|
186
188
|
[RUNTIME_NAME]: runtimeName,
|
|
187
189
|
[RUNTIME_VERSION]: runtimeVersion,
|
|
188
190
|
[GIT_BRANCH]: branch,
|
|
189
|
-
[CI_PROVIDER_NAME]: ciProviderName
|
|
191
|
+
[CI_PROVIDER_NAME]: ciProviderName,
|
|
192
|
+
[CI_WORKSPACE_PATH]: repositoryRoot
|
|
190
193
|
} = testEnvironmentMetadata
|
|
191
194
|
|
|
192
195
|
const isUnsupportedCIProvider = !ciProviderName
|
|
@@ -205,7 +208,7 @@ module.exports = (on, config) => {
|
|
|
205
208
|
testLevel: 'test'
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
const codeOwnersEntries = getCodeOwnersFileEntries()
|
|
211
|
+
const codeOwnersEntries = getCodeOwnersFileEntries(repositoryRoot)
|
|
209
212
|
|
|
210
213
|
let activeSpan = null
|
|
211
214
|
let testSessionSpan = null
|
|
@@ -284,12 +287,12 @@ module.exports = (on, config) => {
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
on('before:run', (details) => {
|
|
287
|
-
return
|
|
290
|
+
return getLibraryConfiguration(tracer, testConfiguration).then(({ err, libraryConfig }) => {
|
|
288
291
|
if (err) {
|
|
289
292
|
log.error(err)
|
|
290
293
|
} else {
|
|
291
|
-
isSuitesSkippingEnabled =
|
|
292
|
-
isCodeCoverageEnabled =
|
|
294
|
+
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
|
|
295
|
+
isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
@@ -359,6 +362,11 @@ module.exports = (on, config) => {
|
|
|
359
362
|
cypressTestName === test.name && spec.relative === test.suite
|
|
360
363
|
)
|
|
361
364
|
const skippedTestSpan = getTestSpan(cypressTestName, spec.relative)
|
|
365
|
+
if (spec.absolute && repositoryRoot) {
|
|
366
|
+
skippedTestSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
|
|
367
|
+
} else {
|
|
368
|
+
skippedTestSpan.setTag(TEST_SOURCE_FILE, spec.relative)
|
|
369
|
+
}
|
|
362
370
|
skippedTestSpan.setTag(TEST_STATUS, 'skip')
|
|
363
371
|
if (isSkippedByItr) {
|
|
364
372
|
skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
|
|
@@ -390,6 +398,11 @@ module.exports = (on, config) => {
|
|
|
390
398
|
if (itrCorrelationId) {
|
|
391
399
|
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
|
|
392
400
|
}
|
|
401
|
+
if (spec.absolute && repositoryRoot) {
|
|
402
|
+
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
|
|
403
|
+
} else {
|
|
404
|
+
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative)
|
|
405
|
+
}
|
|
393
406
|
finishedTest.testSpan.finish(finishedTest.finishTime)
|
|
394
407
|
})
|
|
395
408
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const pick = require('../../utils/src/pick')
|
|
3
4
|
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
5
|
const log = require('../../dd-trace/src/log')
|
|
5
6
|
const GraphQLExecutePlugin = require('./execute')
|
|
@@ -63,10 +64,4 @@ function getHooks (config) {
|
|
|
63
64
|
return { execute, parse, validate }
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
// non-lodash pick
|
|
67
|
-
|
|
68
|
-
function pick (obj, selectors) {
|
|
69
|
-
return Object.fromEntries(Object.entries(obj).filter(([key]) => selectors.includes(key)))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
67
|
module.exports = GraphQLPlugin
|
|
@@ -41,7 +41,6 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
41
41
|
'grpc.status.code': 0
|
|
42
42
|
}
|
|
43
43
|
}, false)
|
|
44
|
-
|
|
45
44
|
// needed as precursor for peer.service
|
|
46
45
|
if (method.service && method.package) {
|
|
47
46
|
span.setTag('rpc.service', method.package + '.' + method.service)
|
|
@@ -68,7 +67,7 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
68
67
|
this.addError(error, span)
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
finish ({ span, result }) {
|
|
70
|
+
finish ({ span, result, peer }) {
|
|
72
71
|
if (!span) return
|
|
73
72
|
|
|
74
73
|
const { code, metadata } = result || {}
|
|
@@ -80,6 +79,21 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
80
79
|
addMetadataTags(span, metadata, metadataFilter, 'response')
|
|
81
80
|
}
|
|
82
81
|
|
|
82
|
+
if (peer) {
|
|
83
|
+
// The only scheme we want to support here is ipv[46]:port, although
|
|
84
|
+
// more are supported by the library
|
|
85
|
+
// https://github.com/grpc/grpc/blob/v1.60.0/doc/naming.md
|
|
86
|
+
const parts = peer.split(':')
|
|
87
|
+
if (parts[parts.length - 1].match(/^\d+/)) {
|
|
88
|
+
const port = parts[parts.length - 1]
|
|
89
|
+
const ip = parts.slice(0, -1).join(':')
|
|
90
|
+
span.setTag('network.destination.ip', ip)
|
|
91
|
+
span.setTag('network.destination.port', port)
|
|
92
|
+
} else {
|
|
93
|
+
span.setTag('network.destination.ip', peer)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
this.tagPeerService(span)
|
|
84
98
|
span.finish()
|
|
85
99
|
}
|
|
@@ -122,7 +122,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
122
122
|
// conditions for no error:
|
|
123
123
|
// 1. not using a custom agent instance with custom timeout specified
|
|
124
124
|
// 2. no invocation of `req.setTimeout`
|
|
125
|
-
if (!args.options.agent?.options
|
|
125
|
+
if (!args.options.agent?.options?.timeout && !customRequestTimeout) return
|
|
126
126
|
|
|
127
127
|
span.setTag('error', 1)
|
|
128
128
|
}
|
|
@@ -14,7 +14,12 @@ const {
|
|
|
14
14
|
TEST_ITR_UNSKIPPABLE,
|
|
15
15
|
TEST_ITR_FORCED_RUN,
|
|
16
16
|
TEST_CODE_OWNERS,
|
|
17
|
-
ITR_CORRELATION_ID
|
|
17
|
+
ITR_CORRELATION_ID,
|
|
18
|
+
TEST_SOURCE_FILE,
|
|
19
|
+
getTestSuitePath,
|
|
20
|
+
TEST_IS_NEW,
|
|
21
|
+
TEST_EARLY_FLAKE_IS_RETRY,
|
|
22
|
+
TEST_EARLY_FLAKE_IS_ENABLED
|
|
18
23
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
24
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
20
25
|
const id = require('../../dd-trace/src/id')
|
|
@@ -81,7 +86,9 @@ class JestPlugin extends CiPlugin {
|
|
|
81
86
|
numSkippedSuites,
|
|
82
87
|
hasUnskippableSuites,
|
|
83
88
|
hasForcedToRunSuites,
|
|
84
|
-
error
|
|
89
|
+
error,
|
|
90
|
+
isEarlyFlakeDetectionEnabled,
|
|
91
|
+
onDone
|
|
85
92
|
}) => {
|
|
86
93
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
87
94
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
@@ -106,23 +113,34 @@ class JestPlugin extends CiPlugin {
|
|
|
106
113
|
}
|
|
107
114
|
)
|
|
108
115
|
|
|
116
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
117
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_IS_ENABLED, 'true')
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
this.testModuleSpan.finish()
|
|
110
121
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
111
122
|
this.testSessionSpan.finish()
|
|
112
123
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
|
|
113
124
|
finishAllTraceSpans(this.testSessionSpan)
|
|
114
|
-
|
|
125
|
+
|
|
126
|
+
this.tracer._exporter.flush(() => {
|
|
127
|
+
if (onDone) {
|
|
128
|
+
onDone()
|
|
129
|
+
}
|
|
130
|
+
})
|
|
115
131
|
})
|
|
116
132
|
|
|
117
133
|
// Test suites can be run in a different process from jest's main one.
|
|
118
134
|
// This subscriber changes the configuration objects from jest to inject the trace id
|
|
119
|
-
// of the test session to the processes that run the test suites.
|
|
135
|
+
// of the test session to the processes that run the test suites, and other data.
|
|
120
136
|
this.addSub('ci:jest:session:configuration', configs => {
|
|
121
137
|
configs.forEach(config => {
|
|
122
138
|
config._ddTestSessionId = this.testSessionSpan.context().toTraceId()
|
|
123
139
|
config._ddTestModuleId = this.testModuleSpan.context().toSpanId()
|
|
124
140
|
config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
|
|
125
141
|
config._ddItrCorrelationId = this.itrCorrelationId
|
|
142
|
+
config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled
|
|
143
|
+
config._ddEarlyFlakeDetectionNumRetries = this.libraryConfig?.earlyFlakeDetectionNumRetries ?? 0
|
|
126
144
|
})
|
|
127
145
|
})
|
|
128
146
|
|
|
@@ -223,7 +241,7 @@ class JestPlugin extends CiPlugin {
|
|
|
223
241
|
})
|
|
224
242
|
|
|
225
243
|
/**
|
|
226
|
-
* This can't use `this.
|
|
244
|
+
* This can't use `this.libraryConfig` like `ci:mocha:test-suite:code-coverage`
|
|
227
245
|
* because this subscription happens in a different process from the one
|
|
228
246
|
* fetching the ITR config.
|
|
229
247
|
*/
|
|
@@ -286,7 +304,17 @@ class JestPlugin extends CiPlugin {
|
|
|
286
304
|
}
|
|
287
305
|
|
|
288
306
|
startTestSpan (test) {
|
|
289
|
-
const {
|
|
307
|
+
const {
|
|
308
|
+
suite,
|
|
309
|
+
name,
|
|
310
|
+
runner,
|
|
311
|
+
testParameters,
|
|
312
|
+
frameworkVersion,
|
|
313
|
+
testStartLine,
|
|
314
|
+
testFileAbsolutePath,
|
|
315
|
+
isNew,
|
|
316
|
+
isEfdRetry
|
|
317
|
+
} = test
|
|
290
318
|
|
|
291
319
|
const extraTags = {
|
|
292
320
|
[JEST_TEST_RUNNER]: runner,
|
|
@@ -296,6 +324,19 @@ class JestPlugin extends CiPlugin {
|
|
|
296
324
|
if (testStartLine) {
|
|
297
325
|
extraTags[TEST_SOURCE_START] = testStartLine
|
|
298
326
|
}
|
|
327
|
+
if (testFileAbsolutePath) {
|
|
328
|
+
extraTags[TEST_SOURCE_FILE] = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
|
|
329
|
+
} else {
|
|
330
|
+
// If for whatever we don't have the full path, we'll set the source file to the suite name
|
|
331
|
+
extraTags[TEST_SOURCE_FILE] = suite
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (isNew) {
|
|
335
|
+
extraTags[TEST_IS_NEW] = 'true'
|
|
336
|
+
if (isEfdRetry) {
|
|
337
|
+
extraTags[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
|
|
338
|
+
}
|
|
339
|
+
}
|
|
299
340
|
|
|
300
341
|
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)
|
|
301
342
|
}
|
|
@@ -15,7 +15,8 @@ const {
|
|
|
15
15
|
TEST_ITR_UNSKIPPABLE,
|
|
16
16
|
TEST_ITR_FORCED_RUN,
|
|
17
17
|
TEST_CODE_OWNERS,
|
|
18
|
-
ITR_CORRELATION_ID
|
|
18
|
+
ITR_CORRELATION_ID,
|
|
19
|
+
TEST_SOURCE_FILE
|
|
19
20
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
20
21
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
21
22
|
const {
|
|
@@ -42,7 +43,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
42
43
|
this.sourceRoot = process.cwd()
|
|
43
44
|
|
|
44
45
|
this.addSub('ci:mocha:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
|
|
45
|
-
if (!this.
|
|
46
|
+
if (!this.libraryConfig?.isCodeCoverageEnabled) {
|
|
46
47
|
return
|
|
47
48
|
}
|
|
48
49
|
const testSuiteSpan = this._testSuites.get(suiteFile)
|
|
@@ -98,7 +99,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
98
99
|
}
|
|
99
100
|
})
|
|
100
101
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
101
|
-
if (this.
|
|
102
|
+
if (this.libraryConfig?.isCodeCoverageEnabled) {
|
|
102
103
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
|
|
103
104
|
}
|
|
104
105
|
if (itrCorrelationId) {
|
|
@@ -192,7 +193,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
192
193
|
error
|
|
193
194
|
}) => {
|
|
194
195
|
if (this.testSessionSpan) {
|
|
195
|
-
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.
|
|
196
|
+
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
|
|
196
197
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
197
198
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
198
199
|
|
|
@@ -222,7 +223,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
222
223
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
|
|
223
224
|
finishAllTraceSpans(this.testSessionSpan)
|
|
224
225
|
}
|
|
225
|
-
this.
|
|
226
|
+
this.libraryConfig = null
|
|
226
227
|
this.tracer._exporter.flush()
|
|
227
228
|
})
|
|
228
229
|
}
|
|
@@ -244,6 +245,14 @@ class MochaPlugin extends CiPlugin {
|
|
|
244
245
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
245
246
|
const testSuiteSpan = this._testSuites.get(testSuiteAbsolutePath)
|
|
246
247
|
|
|
248
|
+
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
249
|
+
|
|
250
|
+
if (testSourceFile) {
|
|
251
|
+
extraTags[TEST_SOURCE_FILE] = testSourceFile
|
|
252
|
+
} else {
|
|
253
|
+
extraTags[TEST_SOURCE_FILE] = testSuite
|
|
254
|
+
}
|
|
255
|
+
|
|
247
256
|
return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
|
|
248
257
|
}
|
|
249
258
|
}
|
|
@@ -9,7 +9,9 @@ const {
|
|
|
9
9
|
getTestSuitePath,
|
|
10
10
|
getTestSuiteCommonTags,
|
|
11
11
|
TEST_SOURCE_START,
|
|
12
|
-
TEST_CODE_OWNERS
|
|
12
|
+
TEST_CODE_OWNERS,
|
|
13
|
+
TEST_SOURCE_FILE,
|
|
14
|
+
TEST_CONFIGURATION_BROWSER_NAME
|
|
13
15
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
14
16
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
15
17
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -76,10 +78,11 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
76
78
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
77
79
|
})
|
|
78
80
|
|
|
79
|
-
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine }) => {
|
|
81
|
+
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine, browserName }) => {
|
|
80
82
|
const store = storage.getStore()
|
|
81
83
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
82
|
-
const
|
|
84
|
+
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
85
|
+
const span = this.startTestSpan(testName, testSuite, testSourceFile, testSourceLine, browserName)
|
|
83
86
|
|
|
84
87
|
this.enter(span, store)
|
|
85
88
|
})
|
|
@@ -126,9 +129,20 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
126
129
|
})
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
startTestSpan (testName, testSuite, testSourceLine) {
|
|
132
|
+
startTestSpan (testName, testSuite, testSourceFile, testSourceLine, browserName) {
|
|
130
133
|
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
131
|
-
|
|
134
|
+
|
|
135
|
+
const extraTags = {
|
|
136
|
+
[TEST_SOURCE_START]: testSourceLine
|
|
137
|
+
}
|
|
138
|
+
if (testSourceFile) {
|
|
139
|
+
extraTags[TEST_SOURCE_FILE] = testSourceFile || testSuite
|
|
140
|
+
}
|
|
141
|
+
if (browserName) {
|
|
142
|
+
extraTags[TEST_CONFIGURATION_BROWSER_NAME] = browserName
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
|
|
132
146
|
}
|
|
133
147
|
}
|
|
134
148
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
|
+
const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
|
|
5
6
|
|
|
6
7
|
class RheaConsumerPlugin extends ConsumerPlugin {
|
|
7
8
|
static get id () { return 'rhea' }
|
|
@@ -19,7 +20,7 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
19
20
|
const name = getResourceNameFromMessage(msgObj)
|
|
20
21
|
const childOf = extractTextMap(msgObj, this.tracer)
|
|
21
22
|
|
|
22
|
-
this.startSpan({
|
|
23
|
+
const span = this.startSpan({
|
|
23
24
|
childOf,
|
|
24
25
|
resource: name,
|
|
25
26
|
type: 'worker',
|
|
@@ -29,6 +30,15 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
29
30
|
'amqp.link.role': 'receiver'
|
|
30
31
|
}
|
|
31
32
|
})
|
|
33
|
+
|
|
34
|
+
if (this.config.dsmEnabled && msgObj.message) {
|
|
35
|
+
const payloadSize = getAmqpMessageSize(
|
|
36
|
+
{ headers: msgObj.message.delivery_annotations, content: msgObj.message.body }
|
|
37
|
+
)
|
|
38
|
+
this.tracer.decodeDataStreamsContext(msgObj.message.delivery_annotations[CONTEXT_PROPAGATION_KEY])
|
|
39
|
+
this.tracer
|
|
40
|
+
.setCheckpoint(['direction:in', `topic:${name}`, 'type:rabbitmq'], span, payloadSize)
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
4
4
|
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
5
|
+
const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
|
|
6
|
+
const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
|
|
5
7
|
|
|
6
8
|
class RheaProducerPlugin extends ProducerPlugin {
|
|
7
9
|
static get id () { return 'rhea' }
|
|
@@ -36,6 +38,15 @@ function addDeliveryAnnotations (msg, tracer, span) {
|
|
|
36
38
|
msg.delivery_annotations = msg.delivery_annotations || {}
|
|
37
39
|
|
|
38
40
|
tracer.inject(span, 'text_map', msg.delivery_annotations)
|
|
41
|
+
|
|
42
|
+
if (tracer._config.dsmEnabled) {
|
|
43
|
+
const targetName = span.context()._tags['amqp.link.target.address']
|
|
44
|
+
const payloadSize = getAmqpMessageSize({ content: msg.body, headers: msg.delivery_annotations })
|
|
45
|
+
const dataStreamsContext = tracer
|
|
46
|
+
.setCheckpoint(['direction:out', `exchange:${targetName}`, 'type:rabbitmq'], span, payloadSize)
|
|
47
|
+
const pathwayCtx = encodePathwayContext(dataStreamsContext)
|
|
48
|
+
msg.delivery_annotations[CONTEXT_PROPAGATION_KEY] = pathwayCtx
|
|
49
|
+
}
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
|
|
@@ -15,6 +15,8 @@ module.exports = {
|
|
|
15
15
|
HTTP_INCOMING_GRAPHQL_RESOLVERS: 'graphql.server.all_resolvers',
|
|
16
16
|
HTTP_INCOMING_GRAPHQL_RESOLVER: 'graphql.server.resolver',
|
|
17
17
|
|
|
18
|
+
HTTP_OUTGOING_BODY: 'server.response.body',
|
|
19
|
+
|
|
18
20
|
HTTP_CLIENT_IP: 'http.client_ip',
|
|
19
21
|
|
|
20
22
|
USER_ID: 'usr.id',
|
|
@@ -5,6 +5,8 @@ const log = require('../log')
|
|
|
5
5
|
let enabled
|
|
6
6
|
let requestSampling
|
|
7
7
|
|
|
8
|
+
const sampledRequests = new WeakSet()
|
|
9
|
+
|
|
8
10
|
function configure ({ apiSecurity }) {
|
|
9
11
|
enabled = apiSecurity.enabled
|
|
10
12
|
setRequestSampling(apiSecurity.requestSampling)
|
|
@@ -32,17 +34,28 @@ function parseRequestSampling (requestSampling) {
|
|
|
32
34
|
return parsed
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
function sampleRequest () {
|
|
37
|
+
function sampleRequest (req) {
|
|
36
38
|
if (!enabled || !requestSampling) {
|
|
37
39
|
return false
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
const shouldSample = Math.random() <= requestSampling
|
|
43
|
+
|
|
44
|
+
if (shouldSample) {
|
|
45
|
+
sampledRequests.add(req)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return shouldSample
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isSampled (req) {
|
|
52
|
+
return sampledRequests.has(req)
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
module.exports = {
|
|
44
56
|
configure,
|
|
45
57
|
disable,
|
|
46
58
|
setRequestSampling,
|
|
47
|
-
sampleRequest
|
|
59
|
+
sampleRequest,
|
|
60
|
+
isSampled
|
|
48
61
|
}
|
|
@@ -16,5 +16,6 @@ module.exports = {
|
|
|
16
16
|
queryParser: dc.channel('datadog:query:read:finish'),
|
|
17
17
|
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
18
18
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
19
|
-
nextQueryParsed: dc.channel('apm:next:query-parsed')
|
|
19
|
+
nextQueryParsed: dc.channel('apm:next:query-parsed'),
|
|
20
|
+
responseBody: dc.channel('datadog:express:response:json:start')
|
|
20
21
|
}
|
|
@@ -8,7 +8,7 @@ class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
onConfigure () {
|
|
11
|
-
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
11
|
+
this.addSub('tracing:datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -9,7 +9,7 @@ const { storage } = require('../../../../../datadog-core')
|
|
|
9
9
|
const { getIastContext } = require('../iast-context')
|
|
10
10
|
const { HTTP_REQUEST_PARAMETER, HTTP_REQUEST_BODY } = require('../taint-tracking/source-types')
|
|
11
11
|
|
|
12
|
-
const EXCLUDED_PATHS_FROM_STACK = getNodeModulesPaths('mongodb', 'mongoose')
|
|
12
|
+
const EXCLUDED_PATHS_FROM_STACK = getNodeModulesPaths('mongodb', 'mongoose', 'mquery')
|
|
13
13
|
const MONGODB_NOSQL_SECURE_MARK = getNextSecureMark()
|
|
14
14
|
|
|
15
15
|
function iterateObjectStrings (target, fn, levelKeys = [], depth = 50, visited = new Set()) {
|
|
@@ -37,34 +37,39 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
37
37
|
onConfigure () {
|
|
38
38
|
this.configureSanitizers()
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const onStart = ({ filters }) => {
|
|
41
41
|
const store = storage.getStore()
|
|
42
42
|
if (store && !store.nosqlAnalyzed && filters?.length) {
|
|
43
43
|
filters.forEach(filter => {
|
|
44
44
|
this.analyze({ filter }, store)
|
|
45
45
|
})
|
|
46
46
|
}
|
|
47
|
-
})
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!store) return
|
|
48
|
+
return store
|
|
49
|
+
}
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
51
|
+
const onStartAndEnterWithStore = (message) => {
|
|
52
|
+
const store = onStart(message || {})
|
|
53
|
+
if (store) {
|
|
54
|
+
storage.enterWith({ ...store, nosqlAnalyzed: true, nosqlParentStore: store })
|
|
57
55
|
}
|
|
56
|
+
}
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
this.addSub('datadog:mongoose:model:filter:finish', () => {
|
|
58
|
+
const onFinish = () => {
|
|
63
59
|
const store = storage.getStore()
|
|
64
|
-
if (store?.
|
|
65
|
-
storage.enterWith(store.
|
|
60
|
+
if (store?.nosqlParentStore) {
|
|
61
|
+
storage.enterWith(store.nosqlParentStore)
|
|
66
62
|
}
|
|
67
|
-
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.addSub('datadog:mongodb:collection:filter:start', onStart)
|
|
66
|
+
|
|
67
|
+
this.addSub('datadog:mongoose:model:filter:start', onStartAndEnterWithStore)
|
|
68
|
+
this.addSub('datadog:mongoose:model:filter:finish', onFinish)
|
|
69
|
+
|
|
70
|
+
this.addSub('datadog:mquery:filter:prepare', onStart)
|
|
71
|
+
this.addSub('tracing:datadog:mquery:filter:start', onStartAndEnterWithStore)
|
|
72
|
+
this.addSub('tracing:datadog:mquery:filter:asyncEnd', onFinish)
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
configureSanitizers () {
|
|
@@ -4,8 +4,6 @@ const InjectionAnalyzer = require('./injection-analyzer')
|
|
|
4
4
|
const { SQL_INJECTION } = require('../vulnerabilities')
|
|
5
5
|
const { getRanges } = require('../taint-tracking/operations')
|
|
6
6
|
const { storage } = require('../../../../../datadog-core')
|
|
7
|
-
const { getIastContext } = require('../iast-context')
|
|
8
|
-
const { addVulnerability } = require('../vulnerability-reporter')
|
|
9
7
|
const { getNodeModulesPaths } = require('../path-line')
|
|
10
8
|
|
|
11
9
|
const EXCLUDED_PATHS = getNodeModulesPaths('mysql', 'mysql2', 'sequelize', 'pg-pool', 'knex')
|
|
@@ -16,9 +14,9 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
onConfigure () {
|
|
19
|
-
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
20
|
-
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
21
|
-
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, 'POSTGRES'))
|
|
17
|
+
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
|
|
18
|
+
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
|
|
19
|
+
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, undefined, 'POSTGRES'))
|
|
22
20
|
|
|
23
21
|
this.addSub(
|
|
24
22
|
'datadog:sequelize:query:start',
|
|
@@ -42,7 +40,7 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
42
40
|
getStoreAndAnalyze (query, dialect) {
|
|
43
41
|
const parentStore = storage.getStore()
|
|
44
42
|
if (parentStore) {
|
|
45
|
-
this.analyze(query,
|
|
43
|
+
this.analyze(query, parentStore, dialect)
|
|
46
44
|
|
|
47
45
|
storage.enterWith({ ...parentStore, sqlAnalyzed: true, sqlParentStore: parentStore })
|
|
48
46
|
}
|
|
@@ -60,29 +58,10 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
60
58
|
return { value, ranges, dialect }
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
analyze (value,
|
|
61
|
+
analyze (value, store, dialect) {
|
|
62
|
+
store = store || storage.getStore()
|
|
64
63
|
if (!(store && store.sqlAnalyzed)) {
|
|
65
|
-
|
|
66
|
-
if (this._isInvalidContext(store, iastContext)) return
|
|
67
|
-
this._reportIfVulnerable(value, iastContext, dialect)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
_reportIfVulnerable (value, context, dialect) {
|
|
72
|
-
if (this._isVulnerable(value, context) && this._checkOCE(context)) {
|
|
73
|
-
this._report(value, context, dialect)
|
|
74
|
-
return true
|
|
75
|
-
}
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
_report (value, context, dialect) {
|
|
80
|
-
const evidence = this._getEvidence(value, context, dialect)
|
|
81
|
-
const location = this._getLocation()
|
|
82
|
-
if (!this._isExcluded(location)) {
|
|
83
|
-
const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
|
|
84
|
-
const vulnerability = this._createVulnerability(this._type, evidence, spanId, location)
|
|
85
|
-
addVulnerability(context, vulnerability)
|
|
64
|
+
super.analyze(value, store, dialect)
|
|
86
65
|
}
|
|
87
66
|
}
|
|
88
67
|
|