dd-trace 5.97.0 → 5.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +26 -2
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +65 -3
- package/packages/datadog-instrumentations/src/cypress-config.js +31 -37
- package/packages/datadog-instrumentations/src/jest.js +104 -12
- package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
- package/packages/datadog-instrumentations/src/redis.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +6 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +109 -1
- package/packages/datadog-plugin-cypress/src/index.js +59 -2
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +10 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +10 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +8 -2
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-tedious/src/index.js +1 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/receiver.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +22 -22
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
- package/packages/dd-trace/src/appsec/blocking.js +62 -34
- package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
- package/packages/dd-trace/src/config/defaults.js +0 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
- package/packages/dd-trace/src/config/index.js +55 -28
- package/packages/dd-trace/src/config/supported-configurations.json +61 -4
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
- package/packages/dd-trace/src/encode/0.4.js +7 -6
- package/packages/dd-trace/src/encode/span-stats.js +4 -1
- package/packages/dd-trace/src/log/index.js +0 -10
- package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
- package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
- package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
- package/packages/dd-trace/src/opentracing/tracer.js +9 -4
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
- package/packages/dd-trace/src/plugins/plugin.js +6 -11
- package/packages/dd-trace/src/plugins/storage.js +2 -2
- package/packages/dd-trace/src/plugins/tracing.js +22 -5
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +6 -88
- package/packages/dd-trace/src/profiling/profiler.js +34 -77
- package/packages/dd-trace/src/proxy.js +8 -3
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +15 -11
- package/packages/dd-trace/src/service-naming/index.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
- package/packages/dd-trace/src/span_stats.js +5 -1
- package/packages/dd-trace/src/tracer.js +2 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
- package/vendor/dist/protobufjs/index.js +1 -1
- package/packages/dd-trace/src/log/utils.js +0 -16
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// Capture real timers at module load, before any test can install fake timers.
|
|
4
|
+
const { performance } = require('perf_hooks')
|
|
5
|
+
const dateNow = Date.now
|
|
6
|
+
|
|
3
7
|
const {
|
|
4
8
|
TEST_STATUS,
|
|
5
9
|
TEST_IS_RUM_ACTIVE,
|
|
@@ -53,6 +57,7 @@ const {
|
|
|
53
57
|
DYNAMIC_NAME_RE,
|
|
54
58
|
logDynamicNamesWarning,
|
|
55
59
|
getPullRequestBaseBranch,
|
|
60
|
+
TEST_FINAL_STATUS,
|
|
56
61
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
57
62
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
58
63
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -240,6 +245,58 @@ function getSuiteStatus (suiteStats) {
|
|
|
240
245
|
return 'pass'
|
|
241
246
|
}
|
|
242
247
|
|
|
248
|
+
const FINAL_STATUS_RETRY_KIND = {
|
|
249
|
+
none: 'none',
|
|
250
|
+
atr: 'atr',
|
|
251
|
+
efd: 'efd',
|
|
252
|
+
atf: 'atf',
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTestRetriesCount }) {
|
|
256
|
+
// Infer retry kind from the executions we actually saw so ATR enabled with
|
|
257
|
+
// a retry count of 0 is still treated as a single final execution.
|
|
258
|
+
if (finishedTest.isAttemptToFix) {
|
|
259
|
+
return FINAL_STATUS_RETRY_KIND.atf
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (finishedTestAttempts.some(testAttempt => testAttempt.isEfdRetry)) {
|
|
263
|
+
return FINAL_STATUS_RETRY_KIND.efd
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (finishedTestAttempts.length > 1 && flakyTestRetriesCount > 0) {
|
|
267
|
+
return FINAL_STATUS_RETRY_KIND.atr
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return FINAL_STATUS_RETRY_KIND.none
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function getFinalStatus ({
|
|
274
|
+
status,
|
|
275
|
+
retryKind,
|
|
276
|
+
hasFailedAllRetries,
|
|
277
|
+
hasPassedAllAtfRetries,
|
|
278
|
+
isQuarantined,
|
|
279
|
+
isDisabled,
|
|
280
|
+
}) {
|
|
281
|
+
// If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
|
|
282
|
+
// the final status of its last execution should be reported as 'skip'.
|
|
283
|
+
if (isQuarantined || isDisabled || status === 'skip') {
|
|
284
|
+
return 'skip'
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
switch (retryKind) {
|
|
288
|
+
case FINAL_STATUS_RETRY_KIND.atr:
|
|
289
|
+
case FINAL_STATUS_RETRY_KIND.efd:
|
|
290
|
+
// These modes report the aggregate result across attempts.
|
|
291
|
+
return hasFailedAllRetries ? 'fail' : 'pass'
|
|
292
|
+
case FINAL_STATUS_RETRY_KIND.atf:
|
|
293
|
+
// Attempt-to-fix only passes if every execution passed.
|
|
294
|
+
return hasPassedAllAtfRetries ? 'pass' : 'fail'
|
|
295
|
+
default:
|
|
296
|
+
return status
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
243
300
|
class CypressPlugin {
|
|
244
301
|
_isInit = false
|
|
245
302
|
testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
|
|
@@ -349,6 +406,19 @@ class CypressPlugin {
|
|
|
349
406
|
this.rumFlushWaitMillis = undefined
|
|
350
407
|
this._pendingRequestErrorTags = []
|
|
351
408
|
this.libraryConfigurationPromise = undefined
|
|
409
|
+
this._timeOrigin = 0
|
|
410
|
+
this._perfOrigin = 0
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Returns the current time in the same coordinate system used by span
|
|
415
|
+
* start/finish. Captured at session span creation so it shares the same
|
|
416
|
+
* epoch as the trace without reaching into span internals.
|
|
417
|
+
*
|
|
418
|
+
* @returns {number}
|
|
419
|
+
*/
|
|
420
|
+
_now () {
|
|
421
|
+
return this._timeOrigin + performance.now() - this._perfOrigin
|
|
352
422
|
}
|
|
353
423
|
|
|
354
424
|
// Init function returns a promise that resolves with the Cypress configuration
|
|
@@ -665,6 +735,13 @@ class CypressPlugin {
|
|
|
665
735
|
this.tracer._tracer._exporter.addMetadataTags(metadataTags)
|
|
666
736
|
}
|
|
667
737
|
|
|
738
|
+
// Capture time references that match what startSpan records internally
|
|
739
|
+
// (trace.startTime = Date.now(), trace.ticks = performance.now()).
|
|
740
|
+
// This lets _now() produce values in the same coordinate system as
|
|
741
|
+
// span._startTime without accessing span internals.
|
|
742
|
+
this._timeOrigin = dateNow()
|
|
743
|
+
this._perfOrigin = performance.now()
|
|
744
|
+
|
|
668
745
|
this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
|
|
669
746
|
childOf,
|
|
670
747
|
tags: {
|
|
@@ -797,6 +874,7 @@ class CypressPlugin {
|
|
|
797
874
|
: spec.relative
|
|
798
875
|
|
|
799
876
|
const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
|
|
877
|
+
skippedTestSpan.setTag(TEST_FINAL_STATUS, 'skip')
|
|
800
878
|
|
|
801
879
|
skippedTestSpan.setTag(TEST_STATUS, 'skip')
|
|
802
880
|
if (isSkippedByItr) {
|
|
@@ -886,6 +964,36 @@ class CypressPlugin {
|
|
|
886
964
|
finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
|
|
887
965
|
}
|
|
888
966
|
|
|
967
|
+
// We can check if this is the last attempt regardless of the retry mechanism
|
|
968
|
+
const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
|
|
969
|
+
if (isLastAttempt) {
|
|
970
|
+
const testSpanTags = finishedTest.testSpan.context()._tags
|
|
971
|
+
const retryKind = getFinalStatusRetryKind({
|
|
972
|
+
finishedTest,
|
|
973
|
+
finishedTestAttempts,
|
|
974
|
+
flakyTestRetriesCount: this.flakyTestRetriesCount,
|
|
975
|
+
})
|
|
976
|
+
|
|
977
|
+
const hasFailedAllRetries = testSpanTags[TEST_HAS_FAILED_ALL_RETRIES] === 'true'
|
|
978
|
+
const hasPassedAllAtfRetries =
|
|
979
|
+
testSpanTags[TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED] === 'true'
|
|
980
|
+
const isQuarantined = testSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true'
|
|
981
|
+
const isDisabled = testSpanTags[TEST_MANAGEMENT_IS_DISABLED] === 'true'
|
|
982
|
+
|
|
983
|
+
const finalStatus = getFinalStatus({
|
|
984
|
+
status: cypressTestStatus,
|
|
985
|
+
retryKind,
|
|
986
|
+
hasFailedAllRetries,
|
|
987
|
+
hasPassedAllAtfRetries,
|
|
988
|
+
isQuarantined,
|
|
989
|
+
isDisabled,
|
|
990
|
+
})
|
|
991
|
+
|
|
992
|
+
if (finalStatus) {
|
|
993
|
+
finishedTest.testSpan.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
889
997
|
finishedTest.testSpan.finish(finishedTest.finishTime)
|
|
890
998
|
}
|
|
891
999
|
}
|
|
@@ -1103,7 +1211,7 @@ class CypressPlugin {
|
|
|
1103
1211
|
const finishedTest = {
|
|
1104
1212
|
testName,
|
|
1105
1213
|
testStatus,
|
|
1106
|
-
finishTime: this.
|
|
1214
|
+
finishTime: this._now(),
|
|
1107
1215
|
testSpan: this.activeTestSpan,
|
|
1108
1216
|
isEfdRetry,
|
|
1109
1217
|
isAttemptToFix,
|
|
@@ -1,11 +1,68 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const NoopTracer = require('../../dd-trace/src/noop/tracer')
|
|
3
4
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Cypress plugin handles setup-node-events from the instrumentation layer
|
|
8
|
+
* via a diagnostic channel, keeping the instrumentation free of tracer references.
|
|
9
|
+
*/
|
|
7
10
|
class CypressPlugin extends Plugin {
|
|
8
11
|
static id = 'cypress'
|
|
12
|
+
|
|
13
|
+
constructor (...args) {
|
|
14
|
+
super(...args)
|
|
15
|
+
|
|
16
|
+
this.addSub('ci:cypress:setup-node-events', (payload) => {
|
|
17
|
+
// Bail out if the tracer failed to init (e.g. invalid DD_SITE).
|
|
18
|
+
// Mirrors the guard in the manual plugin entrypoint (plugin.js).
|
|
19
|
+
if (this._tracer._tracer instanceof NoopTracer) return
|
|
20
|
+
|
|
21
|
+
const { on, config, userAfterSpecHandlers, userAfterRunHandlers, cleanupWrapper } = payload
|
|
22
|
+
|
|
23
|
+
const registerAfterRunWithCleanup = (afterRunHandler) => {
|
|
24
|
+
on('after:run', (results) => {
|
|
25
|
+
const chain = userAfterRunHandlers.reduce(
|
|
26
|
+
(p, h) => p.then(() => h(results)),
|
|
27
|
+
Promise.resolve()
|
|
28
|
+
)
|
|
29
|
+
if (afterRunHandler) {
|
|
30
|
+
return chain.then(() => afterRunHandler(results)).finally(cleanupWrapper)
|
|
31
|
+
}
|
|
32
|
+
return chain.finally(cleanupWrapper)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const cypressPlugin = require('./cypress-plugin')
|
|
37
|
+
|
|
38
|
+
if (cypressPlugin._isInit) {
|
|
39
|
+
// Already initialized by manual plugin call — just chain user handlers
|
|
40
|
+
for (const h of userAfterSpecHandlers) on('after:spec', h)
|
|
41
|
+
registerAfterRunWithCleanup()
|
|
42
|
+
payload.registered = true
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))
|
|
47
|
+
|
|
48
|
+
on('after:spec', (spec, results) => {
|
|
49
|
+
const chain = userAfterSpecHandlers.reduce(
|
|
50
|
+
(p, h) => p.then(() => h(spec, results)),
|
|
51
|
+
Promise.resolve()
|
|
52
|
+
)
|
|
53
|
+
return chain.then(() => cypressPlugin.afterSpec(spec, results))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
registerAfterRunWithCleanup((results) => cypressPlugin.afterRun(results))
|
|
57
|
+
|
|
58
|
+
on('task', cypressPlugin.getTasks())
|
|
59
|
+
|
|
60
|
+
payload.registered = true
|
|
61
|
+
// cypressPlugin.init expects the proxy tracer (with ._tracer._exporter),
|
|
62
|
+
// not the unwrapped internal tracer that this.tracer returns.
|
|
63
|
+
payload.configPromise = Promise.resolve(cypressPlugin.init(this._tracer, config)).then(() => config)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
9
66
|
}
|
|
10
67
|
|
|
11
68
|
module.exports = CypressPlugin
|
|
@@ -123,7 +123,8 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
123
123
|
const topicName = topic?.slice(topic.lastIndexOf('/') + 1) ??
|
|
124
124
|
subscription.name.slice(subscription.name.lastIndexOf('/') + 1)
|
|
125
125
|
const baseService = this.tracer._service || 'unknown'
|
|
126
|
-
const serviceName = this.config.service || `${baseService}-pubsub
|
|
126
|
+
const serviceName = this.config.service || { name: `${baseService}-pubsub`, source: baseService }
|
|
127
|
+
|
|
127
128
|
const meta = {
|
|
128
129
|
'gcloud.project_id': subscription.pubsub.projectId,
|
|
129
130
|
'pubsub.topic': topic,
|
|
@@ -145,17 +145,12 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
145
145
|
const subscriptionName = subscription?.slice(subscription.lastIndexOf('/') + 1) ?? subscription
|
|
146
146
|
const publishStartTime = attrs['x-dd-publish-start-time']
|
|
147
147
|
const startTime = publishStartTime ? Number.parseInt(publishStartTime, 10) : undefined
|
|
148
|
-
|
|
149
|
-
// Get the base service name and construct the pubsub service override
|
|
150
|
-
const baseService = this.tracer._service
|
|
151
|
-
const serviceOverride = this.config.service ?? `${baseService}-pubsub`
|
|
152
|
-
|
|
153
148
|
// Use this.startSpan() which automatically activates the span
|
|
154
149
|
const span = this.startSpan('pubsub.push.receive', {
|
|
155
150
|
childOf: parentContext,
|
|
156
151
|
startTime,
|
|
157
152
|
kind: 'consumer',
|
|
158
|
-
service:
|
|
153
|
+
service: this.config.service || { name: `${this.tracer._service}-pubsub`, source: this.tracer._service },
|
|
159
154
|
meta: {
|
|
160
155
|
component: 'google-cloud-pubsub',
|
|
161
156
|
'pubsub.method': 'receive',
|
|
@@ -163,7 +158,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
163
158
|
'pubsub.message_id': message.messageId,
|
|
164
159
|
'pubsub.subscription_type': 'push',
|
|
165
160
|
'pubsub.topic': topicName,
|
|
166
|
-
'_dd.base_service':
|
|
161
|
+
'_dd.base_service': this.tracer._service,
|
|
167
162
|
'_dd.serviceoverride.type': 'integration',
|
|
168
163
|
'resource.name': `Push Subscription ${subscriptionName}`,
|
|
169
164
|
},
|
|
@@ -40,10 +40,10 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
40
40
|
const span = this.startSpan(this.operationName(), {
|
|
41
41
|
childOf,
|
|
42
42
|
integrationName: this.constructor.id,
|
|
43
|
+
service: this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
|
|
43
44
|
meta: {
|
|
44
45
|
[COMPONENT]: this.constructor.id,
|
|
45
46
|
'span.kind': 'client',
|
|
46
|
-
'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
|
|
47
47
|
'resource.name': method,
|
|
48
48
|
'span.type': 'http',
|
|
49
49
|
'http.method': method,
|
|
@@ -4,7 +4,7 @@ const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
6
6
|
const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/channels')
|
|
7
|
-
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
7
|
+
const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
|
|
8
8
|
|
|
9
9
|
class HttpServerPlugin extends ServerPlugin {
|
|
10
10
|
static id = 'http'
|
|
@@ -18,16 +18,24 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
18
18
|
|
|
19
19
|
start ({ req, res, abortController }) {
|
|
20
20
|
let store = storage('legacy').getStore()
|
|
21
|
+
const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
|
|
22
|
+
const service = this.config.service || schemaServiceName
|
|
23
|
+
const serviceSource = (this.config.service && service !== this.tracer._service)
|
|
24
|
+
? 'opt.plugin'
|
|
25
|
+
: (service === this.tracer._service ? undefined : schemaServiceSource)
|
|
21
26
|
const span = web.startSpan(
|
|
22
27
|
this.tracer,
|
|
23
28
|
{
|
|
24
29
|
...this.config,
|
|
25
|
-
service
|
|
30
|
+
service,
|
|
26
31
|
},
|
|
27
32
|
req,
|
|
28
33
|
res,
|
|
29
34
|
this.operationName()
|
|
30
35
|
)
|
|
36
|
+
if (serviceSource !== undefined) {
|
|
37
|
+
span.setTag(SVC_SRC_KEY, serviceSource)
|
|
38
|
+
}
|
|
31
39
|
span.setTag(COMPONENT, this.constructor.id)
|
|
32
40
|
span._integrationName = this.constructor.id
|
|
33
41
|
|
|
@@ -41,10 +41,10 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
41
41
|
const span = this.startSpan(this.operationName(), {
|
|
42
42
|
childOf,
|
|
43
43
|
integrationName: this.constructor.id,
|
|
44
|
+
service: this.serviceName({ pluginConfig: this.config, sessionDetails }),
|
|
44
45
|
meta: {
|
|
45
46
|
[COMPONENT]: this.constructor.id,
|
|
46
47
|
[SPAN_KIND]: CLIENT,
|
|
47
|
-
'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails }),
|
|
48
48
|
'resource.name': method,
|
|
49
49
|
'span.type': 'http',
|
|
50
50
|
'http.method': method,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
6
6
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
7
|
-
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
7
|
+
const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
|
|
8
8
|
|
|
9
9
|
class Http2ServerPlugin extends ServerPlugin {
|
|
10
10
|
constructor (tracer, config) {
|
|
@@ -19,17 +19,25 @@ class Http2ServerPlugin extends ServerPlugin {
|
|
|
19
19
|
bindStart (ctx) {
|
|
20
20
|
const { req, res } = ctx
|
|
21
21
|
|
|
22
|
+
const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
|
|
23
|
+
const service = this.config.service || schemaServiceName
|
|
24
|
+
const serviceSource = (this.config.service && service !== this.tracer._service)
|
|
25
|
+
? 'opt.plugin'
|
|
26
|
+
: (service === this.tracer._service ? undefined : schemaServiceSource)
|
|
22
27
|
const span = web.startSpan(
|
|
23
28
|
this.tracer,
|
|
24
29
|
{
|
|
25
30
|
...this.config,
|
|
26
|
-
service
|
|
31
|
+
service,
|
|
27
32
|
},
|
|
28
33
|
req,
|
|
29
34
|
res,
|
|
30
35
|
this.operationName(),
|
|
31
36
|
ctx
|
|
32
37
|
)
|
|
38
|
+
if (serviceSource !== undefined) {
|
|
39
|
+
span.setTag(SVC_SRC_KEY, serviceSource)
|
|
40
|
+
}
|
|
33
41
|
|
|
34
42
|
span.setTag(COMPONENT, this.constructor.id)
|
|
35
43
|
span._integrationName = this.constructor.id
|
|
@@ -35,9 +35,9 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
35
35
|
}
|
|
36
36
|
const query = getQuery(ops)
|
|
37
37
|
const resource = truncate(getResource(this, ns, query, name))
|
|
38
|
-
const
|
|
38
|
+
const serviceResult = this.serviceName({ pluginConfig: this.config })
|
|
39
39
|
const span = this.startSpan(this.operationName(), {
|
|
40
|
-
service,
|
|
40
|
+
service: serviceResult,
|
|
41
41
|
resource,
|
|
42
42
|
type: 'mongodb',
|
|
43
43
|
kind: 'client',
|
|
@@ -49,7 +49,7 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
49
49
|
'out.port': options.port,
|
|
50
50
|
},
|
|
51
51
|
}, ctx)
|
|
52
|
-
const comment = this.injectDbmComment(span, ops.comment,
|
|
52
|
+
const comment = this.injectDbmComment(span, ops.comment, serviceResult.name)
|
|
53
53
|
if (comment) {
|
|
54
54
|
ops.comment = comment
|
|
55
55
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
|
-
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
6
|
+
const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
|
|
7
7
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
8
8
|
|
|
9
9
|
const errorPages = new Set(['/404', '/500', '/_error', '/_not-found', '/_not-found/page'])
|
|
@@ -19,15 +19,21 @@ class NextPlugin extends ServerPlugin {
|
|
|
19
19
|
bindStart ({ req, res }) {
|
|
20
20
|
const store = storage('legacy').getStore()
|
|
21
21
|
const childOf = store ? store.span : store
|
|
22
|
+
const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
|
|
23
|
+
const serviceName = this.config.service || schemaServiceName
|
|
24
|
+
let serviceSource = this.config.service ? 'opt.plugin' : schemaServiceSource
|
|
25
|
+
if (!serviceName || serviceName === this.tracer._service) serviceSource = undefined
|
|
26
|
+
|
|
22
27
|
const span = this.tracer.startSpan(this.operationName(), {
|
|
23
28
|
childOf,
|
|
24
29
|
tags: {
|
|
25
30
|
[COMPONENT]: this.constructor.id,
|
|
26
|
-
'service.name':
|
|
31
|
+
'service.name': serviceName,
|
|
27
32
|
'resource.name': req.method,
|
|
28
33
|
'span.type': 'web',
|
|
29
34
|
'span.kind': 'server',
|
|
30
35
|
'http.method': req.method,
|
|
36
|
+
...(serviceSource === undefined ? {} : { [SVC_SRC_KEY]: serviceSource }),
|
|
31
37
|
},
|
|
32
38
|
integrationName: this.constructor.id,
|
|
33
39
|
})
|
|
@@ -32,7 +32,7 @@ class PGPlugin extends DatabasePlugin {
|
|
|
32
32
|
span.setTag('db.stream', 1)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
query.__ddInjectableQuery = this.injectDbmQuery(span, query.text, service, !!query.name)
|
|
35
|
+
query.__ddInjectableQuery = this.injectDbmQuery(span, query.text, service.name, !!query.name)
|
|
36
36
|
|
|
37
37
|
return ctx.currentStore
|
|
38
38
|
}
|
|
@@ -28,7 +28,7 @@ class TediousPlugin extends DatabasePlugin {
|
|
|
28
28
|
|
|
29
29
|
// SQL Server includes comments when caching queries
|
|
30
30
|
// For that reason we allow service mode but not full mode
|
|
31
|
-
ctx.sql = this.injectDbmQuery(span, ctx.queryOrProcedure, service, true)
|
|
31
|
+
ctx.sql = this.injectDbmQuery(span, ctx.queryOrProcedure, service.name, true)
|
|
32
32
|
return ctx.currentStore
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -47,7 +47,7 @@ class WSClosePlugin extends TracingPlugin {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
if (isPeerClose && traceWebsocketMessagesInheritSampling && traceWebsocketMessagesSeparateTraces) {
|
|
50
|
-
span.setTag('_dd.dm.service', spanTags['service.name'] || service)
|
|
50
|
+
span.setTag('_dd.dm.service', spanTags['service.name'] || service.name)
|
|
51
51
|
span.setTag('_dd.dm.resource', spanTags['resource.name'] || `websocket ${path}`)
|
|
52
52
|
span.setTag('_dd.dm.inherited', 1)
|
|
53
53
|
}
|
|
@@ -48,7 +48,7 @@ class WSReceiverPlugin extends TracingPlugin {
|
|
|
48
48
|
}, ctx)
|
|
49
49
|
|
|
50
50
|
if (traceWebsocketMessagesInheritSampling && traceWebsocketMessagesSeparateTraces) {
|
|
51
|
-
span.setTag('_dd.dm.service', spanTags['service.name'] || service)
|
|
51
|
+
span.setTag('_dd.dm.service', spanTags['service.name'] || service.name)
|
|
52
52
|
span.setTag('_dd.dm.resource', spanTags['resource.name'] || `websocket ${path}`)
|
|
53
53
|
span.setTag('_dd.dm.inherited', 1)
|
|
54
54
|
}
|
|
@@ -25,11 +25,12 @@ const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
|
25
25
|
const ALLOW = 'ALLOW'
|
|
26
26
|
|
|
27
27
|
class AIGuardAbortError extends Error {
|
|
28
|
-
constructor (reason, tags, sds) {
|
|
28
|
+
constructor (reason, tags, tagProbs, sds) {
|
|
29
29
|
super(reason)
|
|
30
30
|
this.name = 'AIGuardAbortError'
|
|
31
31
|
this.reason = reason
|
|
32
32
|
this.tags = tags
|
|
33
|
+
this.tagProbabilities = tagProbs
|
|
33
34
|
this.sds = sds || []
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -188,38 +189,37 @@ class AIGuard extends NoopAIGuard {
|
|
|
188
189
|
`AI Guard service call failed, status ${response.status}`,
|
|
189
190
|
{ errors: response.body?.errors })
|
|
190
191
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const attr = response.body.data.attributes
|
|
194
|
-
if (!attr.action) {
|
|
195
|
-
throw new Error('Action missing from response')
|
|
196
|
-
}
|
|
197
|
-
action = attr.action
|
|
198
|
-
reason = attr.reason
|
|
199
|
-
tags = attr.tags
|
|
200
|
-
sdsFindings = attr.sds_findings || []
|
|
201
|
-
blockingEnabled = attr.is_blocking_enabled ?? false
|
|
202
|
-
} catch (e) {
|
|
192
|
+
const attr = response.body?.data?.attributes
|
|
193
|
+
if (!attr?.action) {
|
|
203
194
|
appsecMetrics.count(AI_GUARD_TELEMETRY_REQUESTS, { error: true }).inc(1)
|
|
204
|
-
throw new AIGuardClientError(`AI Guard service returned unexpected response : ${response.body}
|
|
195
|
+
throw new AIGuardClientError(`AI Guard service returned unexpected response : ${response.body}`)
|
|
196
|
+
}
|
|
197
|
+
const action = attr.action
|
|
198
|
+
const reason = attr.reason
|
|
199
|
+
const tags = attr.tags ?? []
|
|
200
|
+
if (tags.length > 0) {
|
|
201
|
+
metaStruct.attack_categories = tags
|
|
202
|
+
}
|
|
203
|
+
const sdsFindings = attr.sds_findings ?? []
|
|
204
|
+
if (sdsFindings.length > 0) {
|
|
205
|
+
metaStruct.sds = sdsFindings
|
|
206
|
+
}
|
|
207
|
+
const tagProbabilities = attr.tag_probs ?? {}
|
|
208
|
+
if (attr.tag_probs) {
|
|
209
|
+
metaStruct.tag_probs = tagProbabilities
|
|
205
210
|
}
|
|
211
|
+
const blockingEnabled = attr.is_blocking_enabled ?? false
|
|
206
212
|
const shouldBlock = block && blockingEnabled && action !== ALLOW
|
|
207
213
|
appsecMetrics.count(AI_GUARD_TELEMETRY_REQUESTS, { action, error: false, block: shouldBlock }).inc(1)
|
|
208
214
|
span.setTag(AI_GUARD_ACTION_TAG_KEY, action)
|
|
209
215
|
if (reason) {
|
|
210
216
|
span.setTag(AI_GUARD_REASON_TAG_KEY, reason)
|
|
211
217
|
}
|
|
212
|
-
if (tags?.length > 0) {
|
|
213
|
-
metaStruct.attack_categories = tags
|
|
214
|
-
}
|
|
215
|
-
if (sdsFindings?.length > 0) {
|
|
216
|
-
metaStruct.sds = sdsFindings
|
|
217
|
-
}
|
|
218
218
|
if (shouldBlock) {
|
|
219
219
|
span.setTag(AI_GUARD_BLOCKED_TAG_KEY, 'true')
|
|
220
|
-
throw new AIGuardAbortError(reason, tags, sdsFindings)
|
|
220
|
+
throw new AIGuardAbortError(reason, tags, tagProbabilities, sdsFindings)
|
|
221
221
|
}
|
|
222
|
-
return { action, reason, tags, sds: sdsFindings }
|
|
222
|
+
return { action, reason, tags, tagProbabilities, sds: sdsFindings }
|
|
223
223
|
})
|
|
224
224
|
}
|
|
225
225
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @stylistic/max-len */
|
|
2
|
+
/* eslint-disable @stylistic/quotes */
|
|
2
3
|
'use strict'
|
|
3
4
|
|
|
4
|
-
const html =
|
|
5
|
+
const html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You've been blocked</title><style>a,body,div,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}p{display:block}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}.security-response-id{font-size:14px;color:#999;margin-top:20px;font-family:monospace}</style></head><body><main><p>Sorry, you cannot access this page. Please contact the customer service team.</p><p class="security-response-id">Security Response ID: [security_response_id]</p></main><footer><p>Security provided by <a href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/" target="_blank" rel="noopener noreferrer">Datadog</a></p></footer></body></html>`
|
|
5
6
|
|
|
6
|
-
const json =
|
|
7
|
+
const json = `{"errors":[{"title":"You've been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}],"security_response_id":"[security_response_id]"}`
|
|
7
8
|
|
|
8
|
-
const graphqlJson =
|
|
9
|
+
const graphqlJson = `{"errors":[{"message":"You've been blocked","extensions":{"detail":"Sorry, you cannot perform this operation. Please contact the customer service team. Security provided by Datadog."}}],"security_response_id":"[security_response_id]"}`
|
|
9
10
|
|
|
10
11
|
module.exports = {
|
|
11
12
|
html,
|