dd-trace 3.46.0 → 3.48.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
|
@@ -1,10 +1,69 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
const {
|
|
3
|
+
CONTEXT_PROPAGATION_KEY,
|
|
4
|
+
getSizeOrZero
|
|
5
|
+
} = require('../../../dd-trace/src/datastreams/processor')
|
|
6
|
+
const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
|
|
2
7
|
const log = require('../../../dd-trace/src/log')
|
|
3
8
|
const BaseAwsSdkPlugin = require('../base')
|
|
9
|
+
const { storage } = require('../../../datadog-core')
|
|
10
|
+
|
|
4
11
|
class Kinesis extends BaseAwsSdkPlugin {
|
|
5
12
|
static get id () { return 'kinesis' }
|
|
6
13
|
static get peerServicePrecursors () { return ['streamname'] }
|
|
7
14
|
|
|
15
|
+
constructor (...args) {
|
|
16
|
+
super(...args)
|
|
17
|
+
|
|
18
|
+
// TODO(bengl) Find a way to create the response span tags without this WeakMap being populated
|
|
19
|
+
// in the base class
|
|
20
|
+
this.requestTags = new WeakMap()
|
|
21
|
+
|
|
22
|
+
this.addSub('apm:aws:response:start:kinesis', obj => {
|
|
23
|
+
const { request, response } = obj
|
|
24
|
+
const store = storage.getStore()
|
|
25
|
+
const plugin = this
|
|
26
|
+
|
|
27
|
+
// if we have either of these operations, we want to store the streamName param
|
|
28
|
+
// since it is not typically available during get/put records requests
|
|
29
|
+
if (request.operation === 'getShardIterator' || request.operation === 'listShards') {
|
|
30
|
+
this.storeStreamName(request.params, request.operation, store)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (request.operation === 'getRecords') {
|
|
35
|
+
let span
|
|
36
|
+
const responseExtraction = this.responseExtract(request.params, request.operation, response)
|
|
37
|
+
if (responseExtraction && responseExtraction.maybeChildOf) {
|
|
38
|
+
obj.needsFinish = true
|
|
39
|
+
const options = {
|
|
40
|
+
childOf: responseExtraction.maybeChildOf,
|
|
41
|
+
tags: Object.assign(
|
|
42
|
+
{},
|
|
43
|
+
this.requestTags.get(request) || {},
|
|
44
|
+
{ 'span.kind': 'server' }
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
span = plugin.tracer.startSpan('aws.response', options)
|
|
48
|
+
this.enter(span, store)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// get the stream name that should have been stored previously
|
|
52
|
+
const { streamName } = storage.getStore()
|
|
53
|
+
|
|
54
|
+
// extract DSM context after as we might not have a parent-child but may have a DSM context
|
|
55
|
+
this.responseExtractDSMContext(
|
|
56
|
+
request.operation, response, span ?? null, streamName
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
this.addSub('apm:aws:response:finish:kinesis', err => {
|
|
62
|
+
const { span } = storage.getStore()
|
|
63
|
+
this.finish(span, null, err)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
8
67
|
generateTags (params, operation, response) {
|
|
9
68
|
if (!params || !params.StreamName) return {}
|
|
10
69
|
|
|
@@ -15,6 +74,58 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
15
74
|
}
|
|
16
75
|
}
|
|
17
76
|
|
|
77
|
+
storeStreamName (params, operation, store) {
|
|
78
|
+
if (!operation || (operation !== 'getShardIterator' && operation !== 'listShards')) return
|
|
79
|
+
if (!params || !params.StreamName) return
|
|
80
|
+
|
|
81
|
+
const streamName = params.StreamName
|
|
82
|
+
storage.enterWith({ ...store, streamName })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
responseExtract (params, operation, response) {
|
|
86
|
+
if (operation !== 'getRecords') return
|
|
87
|
+
if (params.Limit && params.Limit !== 1) return
|
|
88
|
+
if (!response || !response.Records || !response.Records[0]) return
|
|
89
|
+
|
|
90
|
+
const record = response.Records[0]
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const decodedData = JSON.parse(Buffer.from(record.Data).toString())
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
maybeChildOf: this.tracer.extract('text_map', decodedData._datadog),
|
|
97
|
+
parsedAttributes: decodedData._datadog
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
log.error(e)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
responseExtractDSMContext (operation, response, span, streamName) {
|
|
105
|
+
if (!this.config.dsmEnabled) return
|
|
106
|
+
if (operation !== 'getRecords') return
|
|
107
|
+
if (!response || !response.Records || !response.Records[0]) return
|
|
108
|
+
|
|
109
|
+
// we only want to set the payloadSize on the span if we have one message, not repeatedly
|
|
110
|
+
span = response.Records.length > 1 ? null : span
|
|
111
|
+
|
|
112
|
+
response.Records.forEach(record => {
|
|
113
|
+
const parsedAttributes = JSON.parse(Buffer.from(record.Data).toString())
|
|
114
|
+
|
|
115
|
+
if (
|
|
116
|
+
parsedAttributes &&
|
|
117
|
+
parsedAttributes._datadog &&
|
|
118
|
+
parsedAttributes._datadog[CONTEXT_PROPAGATION_KEY] &&
|
|
119
|
+
streamName
|
|
120
|
+
) {
|
|
121
|
+
const payloadSize = getSizeOrZero(record.Data)
|
|
122
|
+
this.tracer.decodeDataStreamsContext(Buffer.from(parsedAttributes._datadog[CONTEXT_PROPAGATION_KEY]))
|
|
123
|
+
this.tracer
|
|
124
|
+
.setCheckpoint(['direction:in', `topic:${streamName}`, 'type:kinesis'], span, payloadSize)
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
18
129
|
// AWS-SDK will b64 kinesis payloads
|
|
19
130
|
// or will accept an already b64 encoded payload
|
|
20
131
|
// This method handles both
|
|
@@ -37,8 +148,9 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
37
148
|
if (!request.params) {
|
|
38
149
|
return
|
|
39
150
|
}
|
|
40
|
-
|
|
41
151
|
const traceData = {}
|
|
152
|
+
|
|
153
|
+
// inject data with DD context
|
|
42
154
|
this.tracer.inject(span, 'text_map', traceData)
|
|
43
155
|
let injectPath
|
|
44
156
|
if (request.params.Records && request.params.Records.length > 0) {
|
|
@@ -49,9 +161,30 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
49
161
|
log.error('No valid payload passed, unable to pass trace context')
|
|
50
162
|
return
|
|
51
163
|
}
|
|
164
|
+
|
|
52
165
|
const parsedData = this._tryParse(injectPath.Data)
|
|
53
166
|
if (parsedData) {
|
|
54
167
|
parsedData._datadog = traceData
|
|
168
|
+
|
|
169
|
+
// set DSM hash if enabled
|
|
170
|
+
if (this.config.dsmEnabled) {
|
|
171
|
+
// get payload size of request data
|
|
172
|
+
const payloadSize = Buffer.from(JSON.stringify(parsedData)).byteLength
|
|
173
|
+
let stream
|
|
174
|
+
// users can optionally use either stream name or stream arn
|
|
175
|
+
if (request.params && request.params.StreamArn) {
|
|
176
|
+
stream = request.params.StreamArn
|
|
177
|
+
} else if (request.params && request.params.StreamName) {
|
|
178
|
+
stream = request.params.StreamName
|
|
179
|
+
}
|
|
180
|
+
const dataStreamsContext = this.tracer
|
|
181
|
+
.setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
|
|
182
|
+
if (dataStreamsContext) {
|
|
183
|
+
const pathwayCtx = encodePathwayContext(dataStreamsContext)
|
|
184
|
+
parsedData._datadog[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
55
188
|
const finalData = Buffer.from(JSON.stringify(parsedData))
|
|
56
189
|
const byteSize = finalData.length
|
|
57
190
|
// Kinesis max payload size is 1MB
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
const { CONTEXT_PROPAGATION_KEY, getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
|
|
3
|
+
const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
|
|
2
4
|
const log = require('../../../dd-trace/src/log')
|
|
3
5
|
const BaseAwsSdkPlugin = require('../base')
|
|
4
6
|
|
|
@@ -11,6 +13,7 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
11
13
|
|
|
12
14
|
if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
|
|
13
15
|
const TopicArn = params.TopicArn || response.data.TopicArn
|
|
16
|
+
|
|
14
17
|
// Split the ARN into its parts
|
|
15
18
|
// ex.'arn:aws:sns:us-east-1:123456789012:my-topic'
|
|
16
19
|
const arnParts = TopicArn.split(':')
|
|
@@ -72,10 +75,25 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
72
75
|
}
|
|
73
76
|
const ddInfo = {}
|
|
74
77
|
this.tracer.inject(span, 'text_map', ddInfo)
|
|
78
|
+
// add ddInfo before checking DSM so we can include DD attributes in payload size
|
|
75
79
|
params.MessageAttributes._datadog = {
|
|
76
80
|
DataType: 'Binary',
|
|
77
|
-
BinaryValue:
|
|
81
|
+
BinaryValue: ddInfo
|
|
82
|
+
}
|
|
83
|
+
if (this.config.dsmEnabled) {
|
|
84
|
+
const payloadSize = getHeadersSize({
|
|
85
|
+
Message: params.Message,
|
|
86
|
+
MessageAttributes: params.MessageAttributes
|
|
87
|
+
})
|
|
88
|
+
const dataStreamsContext = this.tracer
|
|
89
|
+
.setCheckpoint(['direction:out', `topic:${params.TopicArn}`, 'type:sns'], span, payloadSize)
|
|
90
|
+
if (dataStreamsContext) {
|
|
91
|
+
const pathwayCtx = encodePathwayContext(dataStreamsContext)
|
|
92
|
+
ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
|
|
93
|
+
}
|
|
78
94
|
}
|
|
95
|
+
// BINARY types are automatically base64 encoded
|
|
96
|
+
params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
|
|
79
97
|
}
|
|
80
98
|
}
|
|
81
99
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
5
|
const { storage } = require('../../../datadog-core')
|
|
6
|
+
const { CONTEXT_PROPAGATION_KEY, getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
|
|
7
|
+
const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
|
|
6
8
|
|
|
7
9
|
class Sqs extends BaseAwsSdkPlugin {
|
|
8
10
|
static get id () { return 'sqs' }
|
|
@@ -19,20 +21,27 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
19
21
|
const { request, response } = obj
|
|
20
22
|
const store = storage.getStore()
|
|
21
23
|
const plugin = this
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
+
const contextExtraction = this.responseExtract(request.params, request.operation, response)
|
|
25
|
+
let span
|
|
26
|
+
let parsedMessageAttributes
|
|
27
|
+
if (contextExtraction && contextExtraction.datadogContext) {
|
|
24
28
|
obj.needsFinish = true
|
|
25
29
|
const options = {
|
|
26
|
-
childOf:
|
|
30
|
+
childOf: contextExtraction.datadogContext,
|
|
27
31
|
tags: Object.assign(
|
|
28
32
|
{},
|
|
29
33
|
this.requestTags.get(request) || {},
|
|
30
34
|
{ 'span.kind': 'server' }
|
|
31
35
|
)
|
|
32
36
|
}
|
|
33
|
-
|
|
37
|
+
parsedMessageAttributes = contextExtraction.parsedAttributes
|
|
38
|
+
span = plugin.tracer.startSpan('aws.response', options)
|
|
34
39
|
this.enter(span, store)
|
|
35
40
|
}
|
|
41
|
+
// extract DSM context after as we might not have a parent-child but may have a DSM context
|
|
42
|
+
this.responseExtractDSMContext(
|
|
43
|
+
request.operation, request.params, response, span ?? null, parsedMessageAttributes ?? null
|
|
44
|
+
)
|
|
36
45
|
})
|
|
37
46
|
|
|
38
47
|
this.addSub('apm:aws:response:finish:sqs', err => {
|
|
@@ -133,19 +142,69 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
133
142
|
|
|
134
143
|
const datadogAttribute = message.MessageAttributes._datadog
|
|
135
144
|
|
|
145
|
+
const parsedAttributes = this.parseDatadogAttributes(datadogAttribute)
|
|
146
|
+
if (parsedAttributes) {
|
|
147
|
+
return {
|
|
148
|
+
datadogContext: this.tracer.extract('text_map', parsedAttributes),
|
|
149
|
+
parsedAttributes: parsedAttributes
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
parseDatadogAttributes (attributes) {
|
|
136
155
|
try {
|
|
137
|
-
if (
|
|
138
|
-
const textMap =
|
|
139
|
-
return
|
|
140
|
-
} else if (
|
|
141
|
-
const buffer = Buffer.from(
|
|
142
|
-
return
|
|
156
|
+
if (attributes.StringValue) {
|
|
157
|
+
const textMap = attributes.StringValue
|
|
158
|
+
return JSON.parse(textMap)
|
|
159
|
+
} else if (attributes.Type === 'Binary') {
|
|
160
|
+
const buffer = Buffer.from(attributes.Value, 'base64')
|
|
161
|
+
return JSON.parse(buffer)
|
|
143
162
|
}
|
|
144
163
|
} catch (e) {
|
|
145
164
|
log.error(e)
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
167
|
|
|
168
|
+
responseExtractDSMContext (operation, params, response, span, parsedAttributes) {
|
|
169
|
+
if (!this.config.dsmEnabled) return
|
|
170
|
+
if (operation !== 'receiveMessage') return
|
|
171
|
+
if (!response || !response.Messages || !response.Messages[0]) return
|
|
172
|
+
|
|
173
|
+
// we only want to set the payloadSize on the span if we have one message
|
|
174
|
+
span = response.Messages.length > 1 ? null : span
|
|
175
|
+
|
|
176
|
+
response.Messages.forEach(message => {
|
|
177
|
+
// we may have already parsed the message attributes when extracting trace context
|
|
178
|
+
if (!parsedAttributes) {
|
|
179
|
+
if (message.Body) {
|
|
180
|
+
try {
|
|
181
|
+
const body = JSON.parse(message.Body)
|
|
182
|
+
|
|
183
|
+
// SNS to SQS
|
|
184
|
+
if (body.Type === 'Notification') {
|
|
185
|
+
message = body
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {
|
|
188
|
+
// SQS to SQS
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (message.MessageAttributes && message.MessageAttributes._datadog) {
|
|
192
|
+
parsedAttributes = this.parseDatadogAttributes(message.MessageAttributes._datadog)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (parsedAttributes && parsedAttributes[CONTEXT_PROPAGATION_KEY]) {
|
|
196
|
+
const payloadSize = getHeadersSize({
|
|
197
|
+
Body: message.Body,
|
|
198
|
+
MessageAttributes: message.MessageAttributes
|
|
199
|
+
})
|
|
200
|
+
const queue = params.QueueUrl.split('/').pop()
|
|
201
|
+
this.tracer.decodeDataStreamsContext(Buffer.from(parsedAttributes[CONTEXT_PROPAGATION_KEY]))
|
|
202
|
+
this.tracer
|
|
203
|
+
.setCheckpoint(['direction:in', `topic:${queue}`, 'type:sqs'], span, payloadSize)
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
149
208
|
requestInject (span, request) {
|
|
150
209
|
const operation = request.operation
|
|
151
210
|
if (operation === 'sendMessage') {
|
|
@@ -164,6 +223,20 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
164
223
|
DataType: 'String',
|
|
165
224
|
StringValue: JSON.stringify(ddInfo)
|
|
166
225
|
}
|
|
226
|
+
if (this.config.dsmEnabled) {
|
|
227
|
+
const payloadSize = getHeadersSize({
|
|
228
|
+
Body: request.params.MessageBody,
|
|
229
|
+
MessageAttributes: request.params.MessageAttributes
|
|
230
|
+
})
|
|
231
|
+
const queue = request.params.QueueUrl.split('/').pop()
|
|
232
|
+
const dataStreamsContext = this.tracer
|
|
233
|
+
.setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
|
|
234
|
+
if (dataStreamsContext) {
|
|
235
|
+
const pathwayCtx = encodePathwayContext(dataStreamsContext)
|
|
236
|
+
ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
request.params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
|
|
167
240
|
}
|
|
168
241
|
}
|
|
169
242
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
const scrubChildProcessCmd = require('./scrub-cmd-params')
|
|
5
|
+
|
|
6
|
+
const MAX_ARG_SIZE = 4096 // 4kB
|
|
7
|
+
|
|
8
|
+
function truncateCommand (cmdFields) {
|
|
9
|
+
let size = cmdFields[0].length
|
|
10
|
+
let truncated = false
|
|
11
|
+
for (let i = 1; i < cmdFields.length; i++) {
|
|
12
|
+
if (size >= MAX_ARG_SIZE) {
|
|
13
|
+
truncated = true
|
|
14
|
+
cmdFields[i] = ''
|
|
15
|
+
continue
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const argLen = cmdFields[i].length
|
|
19
|
+
if (size < MAX_ARG_SIZE && size + argLen > MAX_ARG_SIZE) {
|
|
20
|
+
cmdFields[i] = cmdFields[i].substring(0, 2)
|
|
21
|
+
truncated = true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
size += argLen
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return truncated
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class ChildProcessPlugin extends TracingPlugin {
|
|
31
|
+
static get id () { return 'child_process' }
|
|
32
|
+
static get prefix () { return 'tracing:datadog:child_process:execution' }
|
|
33
|
+
|
|
34
|
+
get tracer () {
|
|
35
|
+
return this._tracer
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
start ({ command, shell }) {
|
|
39
|
+
if (typeof command !== 'string') {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cmdFields = scrubChildProcessCmd(command)
|
|
44
|
+
const truncated = truncateCommand(cmdFields)
|
|
45
|
+
const property = (shell === true) ? 'cmd.shell' : 'cmd.exec'
|
|
46
|
+
|
|
47
|
+
const meta = {
|
|
48
|
+
'component': 'subprocess',
|
|
49
|
+
[property]: (shell === true) ? cmdFields.join(' ') : JSON.stringify(cmdFields)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (truncated) {
|
|
53
|
+
meta['cmd.truncated'] = `${truncated}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.startSpan('command_execution', {
|
|
57
|
+
service: this.config.service,
|
|
58
|
+
resource: (shell === true) ? 'sh' : cmdFields[0],
|
|
59
|
+
type: 'system',
|
|
60
|
+
meta
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
end ({ result, error }) {
|
|
65
|
+
let exitCode
|
|
66
|
+
|
|
67
|
+
if (result !== undefined) {
|
|
68
|
+
exitCode = result?.status || 0
|
|
69
|
+
} else if (error !== undefined) {
|
|
70
|
+
exitCode = error?.status || error?.code || 0
|
|
71
|
+
} else {
|
|
72
|
+
// TracingChannels call start, end synchronously. Later when the promise is resolved then asyncStart asyncEnd.
|
|
73
|
+
// Therefore in the case of calling end with neither result nor error means that they will come in the asyncEnd.
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.activeSpan?.setTag('cmd.exit_code', `${exitCode}`)
|
|
78
|
+
this.activeSpan?.finish()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
error (error) {
|
|
82
|
+
this.addError(error)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
asyncEnd ({ result }) {
|
|
86
|
+
this.activeSpan?.setTag('cmd.exit_code', `${result}`)
|
|
87
|
+
this.activeSpan?.finish()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = ChildProcessPlugin
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const shellParser = require('shell-quote/parse')
|
|
4
|
+
|
|
5
|
+
const ALLOWED_ENV_VARIABLES = ['LD_PRELOAD', 'LD_LIBRARY_PATH', 'PATH']
|
|
6
|
+
const PROCESS_DENYLIST = ['md5']
|
|
7
|
+
|
|
8
|
+
const VARNAMES_REGEX = /\$([\w\d_]*)(?:[^\w\d_]|$)/gmi
|
|
9
|
+
// eslint-disable-next-line max-len
|
|
10
|
+
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|api_?key|secret|a(?:ccess|uth)_token|mysql_pwd|credentials|(?:stripe)?token)$'
|
|
11
|
+
const regexParam = new RegExp(PARAM_PATTERN, 'i')
|
|
12
|
+
const ENV_PATTERN = '^(\\w+=\\w+;)*\\w+=\\w+;?$'
|
|
13
|
+
const envvarRegex = new RegExp(ENV_PATTERN)
|
|
14
|
+
const REDACTED = '?'
|
|
15
|
+
|
|
16
|
+
function extractVarNames (expression) {
|
|
17
|
+
const varNames = new Set()
|
|
18
|
+
let match
|
|
19
|
+
|
|
20
|
+
while ((match = VARNAMES_REGEX.exec(expression))) {
|
|
21
|
+
varNames.add(match[1])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const varNamesObject = {}
|
|
25
|
+
for (const varName of varNames.keys()) {
|
|
26
|
+
varNamesObject[varName] = `$${varName}`
|
|
27
|
+
}
|
|
28
|
+
return varNamesObject
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getTokensByExpression (expressionTokens) {
|
|
32
|
+
const expressionListTokens = []
|
|
33
|
+
let wipExpressionTokens = []
|
|
34
|
+
let isNewExpression = true
|
|
35
|
+
|
|
36
|
+
expressionTokens.forEach(token => {
|
|
37
|
+
if (isNewExpression) {
|
|
38
|
+
expressionListTokens.push(wipExpressionTokens)
|
|
39
|
+
isNewExpression = false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
wipExpressionTokens.push(token)
|
|
43
|
+
|
|
44
|
+
if (token.op) {
|
|
45
|
+
wipExpressionTokens = []
|
|
46
|
+
isNewExpression = true
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
return expressionListTokens
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function scrubChildProcessCmd (expression) {
|
|
53
|
+
const varNames = extractVarNames(expression)
|
|
54
|
+
const expressionTokens = shellParser(expression, varNames)
|
|
55
|
+
|
|
56
|
+
const expressionListTokens = getTokensByExpression(expressionTokens)
|
|
57
|
+
|
|
58
|
+
const result = []
|
|
59
|
+
expressionListTokens.forEach((expressionTokens) => {
|
|
60
|
+
let foundBinary = false
|
|
61
|
+
for (let index = 0; index < expressionTokens.length; index++) {
|
|
62
|
+
const token = expressionTokens[index]
|
|
63
|
+
|
|
64
|
+
if (typeof token === 'object') {
|
|
65
|
+
if (token.pattern) {
|
|
66
|
+
result.push(token.pattern)
|
|
67
|
+
} else if (token.op) {
|
|
68
|
+
result.push(token.op)
|
|
69
|
+
} else if (token.comment) {
|
|
70
|
+
result.push(`#${token.comment}`)
|
|
71
|
+
}
|
|
72
|
+
} else if (!foundBinary) {
|
|
73
|
+
if (envvarRegex.test(token)) {
|
|
74
|
+
const envSplit = token.split('=')
|
|
75
|
+
|
|
76
|
+
if (!ALLOWED_ENV_VARIABLES.includes(envSplit[0])) {
|
|
77
|
+
envSplit[1] = REDACTED
|
|
78
|
+
|
|
79
|
+
const newToken = envSplit.join('=')
|
|
80
|
+
expressionTokens[index] = newToken
|
|
81
|
+
|
|
82
|
+
result.push(newToken)
|
|
83
|
+
} else {
|
|
84
|
+
result.push(token)
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
foundBinary = true
|
|
88
|
+
result.push(token)
|
|
89
|
+
|
|
90
|
+
if (PROCESS_DENYLIST.includes(token)) {
|
|
91
|
+
for (index++; index < expressionTokens.length; index++) {
|
|
92
|
+
const token = expressionTokens[index]
|
|
93
|
+
|
|
94
|
+
if (token.op) {
|
|
95
|
+
result.push(token.op)
|
|
96
|
+
} else {
|
|
97
|
+
expressionTokens[index] = REDACTED
|
|
98
|
+
result.push(REDACTED)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
const paramKeyValue = token.split('=')
|
|
106
|
+
const paramKey = paramKeyValue[0]
|
|
107
|
+
|
|
108
|
+
if (regexParam.test(paramKey)) {
|
|
109
|
+
if (paramKeyValue.length === 1) {
|
|
110
|
+
expressionTokens[index + 1] = REDACTED
|
|
111
|
+
result.push(token)
|
|
112
|
+
} else {
|
|
113
|
+
result.push(`${paramKey}=${REDACTED}`)
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
result.push(token)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return result
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = scrubChildProcessCmd
|
|
@@ -14,7 +14,8 @@ 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
|
|
18
19
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
20
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
20
21
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -47,7 +48,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
47
48
|
hasUnskippableSuites,
|
|
48
49
|
hasForcedToRunSuites
|
|
49
50
|
}) => {
|
|
50
|
-
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.
|
|
51
|
+
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
|
|
51
52
|
addIntelligentTestRunnerSpanTags(
|
|
52
53
|
this.testSessionSpan,
|
|
53
54
|
this.testModuleSpan,
|
|
@@ -71,7 +72,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
71
72
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
|
|
72
73
|
finishAllTraceSpans(this.testSessionSpan)
|
|
73
74
|
|
|
74
|
-
this.
|
|
75
|
+
this.libraryConfig = null
|
|
75
76
|
this.tracer._exporter.flush()
|
|
76
77
|
})
|
|
77
78
|
|
|
@@ -102,7 +103,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
102
103
|
}
|
|
103
104
|
})
|
|
104
105
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
105
|
-
if (this.
|
|
106
|
+
if (this.libraryConfig?.isCodeCoverageEnabled) {
|
|
106
107
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
|
|
107
108
|
}
|
|
108
109
|
})
|
|
@@ -114,7 +115,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
114
115
|
})
|
|
115
116
|
|
|
116
117
|
this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
|
|
117
|
-
if (!this.
|
|
118
|
+
if (!this.libraryConfig?.isCodeCoverageEnabled) {
|
|
118
119
|
return
|
|
119
120
|
}
|
|
120
121
|
if (!coverageFiles.length) {
|
|
@@ -122,7 +123,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
const relativeCoverageFiles = [...coverageFiles, suiteFile]
|
|
125
|
-
.map(filename => getTestSuitePath(filename, this.
|
|
126
|
+
.map(filename => getTestSuitePath(filename, this.repositoryRoot))
|
|
126
127
|
|
|
127
128
|
this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
|
|
128
129
|
|
|
@@ -136,10 +137,11 @@ class CucumberPlugin extends CiPlugin {
|
|
|
136
137
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
|
|
137
138
|
})
|
|
138
139
|
|
|
139
|
-
this.addSub('ci:cucumber:test:start', ({ testName,
|
|
140
|
+
this.addSub('ci:cucumber:test:start', ({ testName, testFileAbsolutePath, testSourceLine }) => {
|
|
140
141
|
const store = storage.getStore()
|
|
141
|
-
const testSuite = getTestSuitePath(
|
|
142
|
-
const
|
|
142
|
+
const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
|
|
143
|
+
const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
|
|
144
|
+
const testSpan = this.startTestSpan(testName, testSuite, testSourceFile, testSourceLine)
|
|
143
145
|
|
|
144
146
|
this.enter(testSpan, store)
|
|
145
147
|
})
|
|
@@ -191,12 +193,15 @@ class CucumberPlugin extends CiPlugin {
|
|
|
191
193
|
})
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
startTestSpan (testName, testSuite, testSourceLine) {
|
|
196
|
+
startTestSpan (testName, testSuite, testSourceFile, testSourceLine) {
|
|
195
197
|
return super.startTestSpan(
|
|
196
198
|
testName,
|
|
197
199
|
testSuite,
|
|
198
200
|
this.testSuiteSpan,
|
|
199
|
-
{
|
|
201
|
+
{
|
|
202
|
+
[TEST_SOURCE_START]: testSourceLine,
|
|
203
|
+
[TEST_SOURCE_FILE]: testSourceFile
|
|
204
|
+
}
|
|
200
205
|
)
|
|
201
206
|
}
|
|
202
207
|
}
|