dd-trace 5.89.0 → 5.91.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 +0 -3
- package/index.d.ts +38 -0
- package/package.json +12 -11
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
- package/packages/datadog-instrumentations/src/cucumber.js +58 -4
- package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +30 -0
- package/packages/datadog-instrumentations/src/jest.js +31 -2
- package/packages/datadog-instrumentations/src/langgraph.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -12
- package/packages/datadog-instrumentations/src/mocha/utils.js +6 -1
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -4
- package/packages/datadog-instrumentations/src/playwright.js +20 -2
- package/packages/datadog-instrumentations/src/prisma.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +69 -24
- package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
- package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
- package/packages/datadog-plugin-apollo/src/index.js +28 -0
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +12 -2
- package/packages/datadog-plugin-cypress/src/support.js +5 -7
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-langgraph/src/index.js +24 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +41 -0
- package/packages/datadog-plugin-playwright/src/index.js +35 -8
- package/packages/dd-trace/src/aiguard/noop.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +14 -5
- package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
- package/packages/dd-trace/src/appsec/index.js +11 -1
- package/packages/dd-trace/src/appsec/reporter.js +28 -11
- package/packages/dd-trace/src/appsec/waf/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/config/defaults.js +1 -0
- package/packages/dd-trace/src/config/index.js +9 -1
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -0
- package/packages/dd-trace/src/dogstatsd.js +1 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
- package/packages/dd-trace/src/encode/agentless-json.js +67 -22
- package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
- package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +11 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +114 -0
- package/packages/dd-trace/src/llmobs/tagger.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
- package/packages/dd-trace/src/plugins/apollo.js +7 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -2
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/ci.js +95 -3
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
- package/packages/dd-trace/src/plugins/util/test.js +7 -10
- package/packages/dd-trace/src/plugins/util/web.js +31 -11
- package/packages/dd-trace/src/priority_sampler.js +20 -2
- package/packages/dd-trace/src/process-tags/index.js +41 -34
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -1
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
- package/packages/dd-trace/src/standalone/product.js +2 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
const { spanHasError } = require('../../dd-trace/src/llmobs/util')
|
|
5
|
+
|
|
6
|
+
// We are only tracing Pregel.stream because Pregel.invoke calls stream internally resulting in
|
|
7
|
+
// a graph with spans that look redundant.
|
|
8
|
+
class PregelStreamPlugin extends TracingPlugin {
|
|
9
|
+
static id = 'langgraph_pregel_stream'
|
|
10
|
+
static prefix = 'tracing:orchestrion:@langchain/langgraph:Pregel_stream'
|
|
11
|
+
|
|
12
|
+
bindStart (ctx) {
|
|
13
|
+
this.startSpan('LangGraph', {
|
|
14
|
+
service: this.config.service,
|
|
15
|
+
kind: 'internal',
|
|
16
|
+
component: 'langgraph',
|
|
17
|
+
}, ctx)
|
|
18
|
+
return ctx.currentStore
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class NextStreamPlugin extends TracingPlugin {
|
|
22
|
+
static id = 'langgraph_stream_next'
|
|
23
|
+
static prefix = 'tracing:orchestrion:@langchain/langgraph:Pregel_stream_next'
|
|
24
|
+
|
|
25
|
+
bindStart (ctx) {
|
|
26
|
+
return ctx.currentStore
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
asyncEnd (ctx) {
|
|
30
|
+
const span = ctx.currentStore?.span
|
|
31
|
+
if (!span) return
|
|
32
|
+
if (ctx.result.done === true || spanHasError(span)) {
|
|
33
|
+
span.finish()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = [
|
|
39
|
+
PregelStreamPlugin,
|
|
40
|
+
NextStreamPlugin,
|
|
41
|
+
]
|
|
@@ -114,11 +114,14 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
114
114
|
})
|
|
115
115
|
|
|
116
116
|
this.addBind('ci:playwright:test-suite:start', (ctx) => {
|
|
117
|
-
const { testSuiteAbsolutePath } = ctx
|
|
117
|
+
const { testSuiteAbsolutePath, testSourceFileAbsolutePath } = ctx
|
|
118
118
|
|
|
119
119
|
const store = storage('legacy').getStore()
|
|
120
120
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
121
|
-
const testSourceFile = getTestSuitePath(
|
|
121
|
+
const testSourceFile = getTestSuitePath(
|
|
122
|
+
testSourceFileAbsolutePath || testSuiteAbsolutePath,
|
|
123
|
+
this.repositoryRoot
|
|
124
|
+
)
|
|
122
125
|
|
|
123
126
|
const testSuiteMetadata = {
|
|
124
127
|
...getTestSuiteCommonTags(
|
|
@@ -238,12 +241,15 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
238
241
|
// test_suite_absolute_path is just a hack because in the worker we don't have rootDir and repositoryRoot
|
|
239
242
|
// but if we pass those the same way we pass `DD_PLAYWRIGHT_WORKER` this is not necessary
|
|
240
243
|
const testSuitePath = getTestSuitePath(formattedSpan.meta.test_suite_absolute_path, this.rootDir)
|
|
241
|
-
const
|
|
244
|
+
const testSourceAbsolutePath = formattedSpan.meta.test_source_absolute_path ||
|
|
245
|
+
formattedSpan.meta.test_suite_absolute_path
|
|
246
|
+
const testSourceFile = getTestSuitePath(testSourceAbsolutePath, this.repositoryRoot)
|
|
242
247
|
// we need to rewrite this because this.rootDir and this.repositoryRoot are not available in the worker
|
|
243
248
|
formattedSpan.meta[TEST_SUITE] = testSuitePath
|
|
244
249
|
formattedSpan.meta[TEST_SOURCE_FILE] = testSourceFile
|
|
245
250
|
formattedSpan.resource = `${testSuitePath}.${formattedSpan.meta[TEST_NAME]}`
|
|
246
251
|
delete formattedSpan.meta.test_suite_absolute_path
|
|
252
|
+
delete formattedSpan.meta.test_source_absolute_path
|
|
247
253
|
}
|
|
248
254
|
formattedTrace.push(formattedSpan)
|
|
249
255
|
}
|
|
@@ -259,20 +265,25 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
259
265
|
const {
|
|
260
266
|
testName,
|
|
261
267
|
testSuiteAbsolutePath,
|
|
268
|
+
testSourceFileAbsolutePath,
|
|
262
269
|
testSourceLine,
|
|
263
270
|
browserName,
|
|
264
271
|
isDisabled,
|
|
265
272
|
} = ctx
|
|
266
273
|
const store = storage('legacy').getStore()
|
|
267
274
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
268
|
-
const testSourceFile = getTestSuitePath(
|
|
275
|
+
const testSourceFile = getTestSuitePath(
|
|
276
|
+
testSourceFileAbsolutePath || testSuiteAbsolutePath,
|
|
277
|
+
this.repositoryRoot
|
|
278
|
+
)
|
|
269
279
|
const span = this.startTestSpan(
|
|
270
280
|
testName,
|
|
271
281
|
testSuiteAbsolutePath,
|
|
272
282
|
testSuite,
|
|
273
283
|
testSourceFile,
|
|
274
284
|
testSourceLine,
|
|
275
|
-
browserName
|
|
285
|
+
browserName,
|
|
286
|
+
testSourceFileAbsolutePath
|
|
276
287
|
)
|
|
277
288
|
|
|
278
289
|
if (isDisabled) {
|
|
@@ -408,6 +419,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
408
419
|
this.addSub('ci:playwright:test:skip', ({
|
|
409
420
|
testName,
|
|
410
421
|
testSuiteAbsolutePath,
|
|
422
|
+
testSourceFileAbsolutePath,
|
|
411
423
|
testSourceLine,
|
|
412
424
|
browserName,
|
|
413
425
|
isNew,
|
|
@@ -416,14 +428,18 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
416
428
|
isQuarantined,
|
|
417
429
|
}) => {
|
|
418
430
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
419
|
-
const testSourceFile = getTestSuitePath(
|
|
431
|
+
const testSourceFile = getTestSuitePath(
|
|
432
|
+
testSourceFileAbsolutePath || testSuiteAbsolutePath,
|
|
433
|
+
this.repositoryRoot
|
|
434
|
+
)
|
|
420
435
|
const span = this.startTestSpan(
|
|
421
436
|
testName,
|
|
422
437
|
testSuiteAbsolutePath,
|
|
423
438
|
testSuite,
|
|
424
439
|
testSourceFile,
|
|
425
440
|
testSourceLine,
|
|
426
|
-
browserName
|
|
441
|
+
browserName,
|
|
442
|
+
testSourceFileAbsolutePath
|
|
427
443
|
)
|
|
428
444
|
|
|
429
445
|
span.setTag(TEST_STATUS, 'skip')
|
|
@@ -446,7 +462,15 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
446
462
|
}
|
|
447
463
|
|
|
448
464
|
// TODO: this runs both in worker and main process (main process: skipped tests that do not go through _runTest)
|
|
449
|
-
startTestSpan (
|
|
465
|
+
startTestSpan (
|
|
466
|
+
testName,
|
|
467
|
+
testSuiteAbsolutePath,
|
|
468
|
+
testSuite,
|
|
469
|
+
testSourceFile,
|
|
470
|
+
testSourceLine,
|
|
471
|
+
browserName,
|
|
472
|
+
testSourceFileAbsolutePath
|
|
473
|
+
) {
|
|
450
474
|
const testSuiteSpan = this._testSuiteSpansByTestSuiteAbsolutePath.get(testSuiteAbsolutePath)
|
|
451
475
|
|
|
452
476
|
const extraTags = {
|
|
@@ -462,6 +486,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
462
486
|
}
|
|
463
487
|
|
|
464
488
|
extraTags.test_suite_absolute_path = testSuiteAbsolutePath
|
|
489
|
+
if (testSourceFileAbsolutePath) {
|
|
490
|
+
extraTags.test_source_absolute_path = testSourceFileAbsolutePath
|
|
491
|
+
}
|
|
465
492
|
|
|
466
493
|
return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
|
|
467
494
|
}
|
|
@@ -4,6 +4,8 @@ const rfdc = require('../../../../vendor/dist/rfdc')({ proto: false, circles: fa
|
|
|
4
4
|
const log = require('../log')
|
|
5
5
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
6
6
|
const tracerVersion = require('../../../../package.json').version
|
|
7
|
+
const { keepTrace } = require('../priority_sampler')
|
|
8
|
+
const { AI_GUARD } = require('../standalone/product')
|
|
7
9
|
const NoopAIGuard = require('./noop')
|
|
8
10
|
const executeRequest = require('./client')
|
|
9
11
|
const {
|
|
@@ -23,11 +25,12 @@ const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
|
23
25
|
const ALLOW = 'ALLOW'
|
|
24
26
|
|
|
25
27
|
class AIGuardAbortError extends Error {
|
|
26
|
-
constructor (reason, tags) {
|
|
28
|
+
constructor (reason, tags, sds) {
|
|
27
29
|
super(reason)
|
|
28
30
|
this.name = 'AIGuardAbortError'
|
|
29
31
|
this.reason = reason
|
|
30
32
|
this.tags = tags
|
|
33
|
+
this.sds = sds || []
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -135,7 +138,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
135
138
|
if (!this.#initialized) {
|
|
136
139
|
return super.evaluate(messages, opts)
|
|
137
140
|
}
|
|
138
|
-
const { block =
|
|
141
|
+
const { block = true } = opts ?? {}
|
|
139
142
|
return this.#tracer.trace(AI_GUARD_RESOURCE, {}, async (span) => {
|
|
140
143
|
const last = messages[messages.length - 1]
|
|
141
144
|
const target = this.#isToolCall(last) ? 'tool' : 'prompt'
|
|
@@ -152,6 +155,12 @@ class AIGuard extends NoopAIGuard {
|
|
|
152
155
|
span.meta_struct = {
|
|
153
156
|
[AI_GUARD_META_STRUCT_KEY]: metaStruct,
|
|
154
157
|
}
|
|
158
|
+
const rootSpan = span.context()?._trace?.started?.[0]
|
|
159
|
+
if (rootSpan) {
|
|
160
|
+
// keepTrace must be called before executeRequest so the sampling decision
|
|
161
|
+
// is propagated correctly to outgoing HTTP client calls.
|
|
162
|
+
keepTrace(rootSpan, AI_GUARD)
|
|
163
|
+
}
|
|
155
164
|
let response
|
|
156
165
|
try {
|
|
157
166
|
const payload = {
|
|
@@ -184,7 +193,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
184
193
|
action = attr.action
|
|
185
194
|
reason = attr.reason
|
|
186
195
|
tags = attr.tags
|
|
187
|
-
sdsFindings = attr.sds_findings
|
|
196
|
+
sdsFindings = attr.sds_findings || []
|
|
188
197
|
blockingEnabled = attr.is_blocking_enabled ?? false
|
|
189
198
|
} catch (e) {
|
|
190
199
|
appsecMetrics.count(AI_GUARD_TELEMETRY_REQUESTS, { error: true }).inc(1)
|
|
@@ -204,9 +213,9 @@ class AIGuard extends NoopAIGuard {
|
|
|
204
213
|
}
|
|
205
214
|
if (shouldBlock) {
|
|
206
215
|
span.setTag(AI_GUARD_BLOCKED_TAG_KEY, 'true')
|
|
207
|
-
throw new AIGuardAbortError(reason, tags)
|
|
216
|
+
throw new AIGuardAbortError(reason, tags, sdsFindings)
|
|
208
217
|
}
|
|
209
|
-
return { action, reason, tags }
|
|
218
|
+
return { action, reason, tags, sds: sdsFindings }
|
|
210
219
|
})
|
|
211
220
|
}
|
|
212
221
|
}
|
|
@@ -70,8 +70,25 @@ function isSampled (key) {
|
|
|
70
70
|
return sampledRequests.has(key)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function getRouteOrEndpoint (context, statusCode) {
|
|
74
|
+
// First try to get the route from the context paths
|
|
75
|
+
const route = context?.paths?.join('') || ''
|
|
76
|
+
if (route) {
|
|
77
|
+
return route
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If route is not available, fallback to http.endpoint
|
|
81
|
+
if (statusCode !== 404) {
|
|
82
|
+
const endpoint = context?.span?.context()?._tags?.['http.endpoint']
|
|
83
|
+
if (endpoint) {
|
|
84
|
+
return endpoint
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return ''
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
function computeKey (req, res) {
|
|
74
|
-
const route = web.getContext(req)?.paths?.join('') || ''
|
|
75
92
|
const method = req.method
|
|
76
93
|
const status = res.statusCode
|
|
77
94
|
|
|
@@ -79,6 +96,10 @@ function computeKey (req, res) {
|
|
|
79
96
|
log.warn('[ASM] Unsupported groupkey for API security')
|
|
80
97
|
return null
|
|
81
98
|
}
|
|
99
|
+
|
|
100
|
+
const context = web.getContext(req)
|
|
101
|
+
const route = getRouteOrEndpoint(context, status)
|
|
102
|
+
|
|
82
103
|
return method + route + status
|
|
83
104
|
}
|
|
84
105
|
|
|
@@ -68,7 +68,7 @@ function enable (_config) {
|
|
|
68
68
|
|
|
69
69
|
appsecRemoteConfig.enableWafUpdate(_config.appsec)
|
|
70
70
|
|
|
71
|
-
Reporter.init(_config.appsec)
|
|
71
|
+
Reporter.init(_config.appsec, _config.inferredProxyServicesEnabled)
|
|
72
72
|
|
|
73
73
|
apiSecuritySampler.configure(_config)
|
|
74
74
|
|
|
@@ -174,6 +174,13 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
|
174
174
|
[HTTP_CLIENT_IP]: clientIp,
|
|
175
175
|
})
|
|
176
176
|
|
|
177
|
+
if (config.inferredProxyServicesEnabled) {
|
|
178
|
+
const context = web.getContext(req)
|
|
179
|
+
if (context?.inferredProxySpan) {
|
|
180
|
+
context.inferredProxySpan.setTag('_dd.appsec.enabled', 1)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
177
184
|
const requestHeaders = { ...req.headers }
|
|
178
185
|
delete requestHeaders.cookie
|
|
179
186
|
|
|
@@ -226,6 +233,9 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
226
233
|
persistent[addresses.HTTP_INCOMING_QUERY] = query
|
|
227
234
|
}
|
|
228
235
|
|
|
236
|
+
// This hook runs before span finish, so ensure route/endpoint tags are available before API Security sampling runs.
|
|
237
|
+
web.setRouteOrEndpointTag(req)
|
|
238
|
+
|
|
229
239
|
if (apiSecuritySampler.sampleRequest(req, res, true)) {
|
|
230
240
|
persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
|
|
231
241
|
}
|
|
@@ -34,6 +34,7 @@ const config = {
|
|
|
34
34
|
maxHeadersCollected: 0,
|
|
35
35
|
headersRedaction: false,
|
|
36
36
|
raspBodyCollection: false,
|
|
37
|
+
inferredProxyServicesEnabled: false,
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const metricsQueue = new Map()
|
|
@@ -103,11 +104,12 @@ const NON_EXTENDED_REQUEST_HEADERS = new Set([...requestHeadersList, ...eventHea
|
|
|
103
104
|
const NON_EXTENDED_RESPONSE_HEADERS = new Set(responseHeaderList)
|
|
104
105
|
const REDACTED_HEADERS = new Set(redactedHeadersList)
|
|
105
106
|
|
|
106
|
-
function init (_config) {
|
|
107
|
+
function init (_config, inferredProxyServicesEnabled) {
|
|
107
108
|
config.headersExtendedCollectionEnabled = _config.extendedHeadersCollection.enabled
|
|
108
109
|
config.maxHeadersCollected = _config.extendedHeadersCollection.maxHeaders
|
|
109
110
|
config.headersRedaction = _config.extendedHeadersCollection.redaction
|
|
110
111
|
config.raspBodyCollection = _config.rasp.bodyCollection
|
|
112
|
+
config.inferredProxyServicesEnabled = inferredProxyServicesEnabled
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
function formatHeaderName (name) {
|
|
@@ -298,9 +300,11 @@ function reportWafConfigUpdate (product, rcConfigId, diagnostics, wafVersion) {
|
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
302
|
|
|
301
|
-
function reportMetrics (metrics, raspRule) {
|
|
302
|
-
|
|
303
|
-
|
|
303
|
+
function reportMetrics (metrics, raspRule, req) {
|
|
304
|
+
if (!req) {
|
|
305
|
+
req = storage('legacy').getStore()?.req
|
|
306
|
+
}
|
|
307
|
+
const rootSpan = req && web.root(req)
|
|
304
308
|
|
|
305
309
|
if (!rootSpan) return
|
|
306
310
|
|
|
@@ -309,9 +313,9 @@ function reportMetrics (metrics, raspRule) {
|
|
|
309
313
|
}
|
|
310
314
|
|
|
311
315
|
if (raspRule) {
|
|
312
|
-
updateRaspRequestsMetricTags(metrics,
|
|
316
|
+
updateRaspRequestsMetricTags(metrics, req, raspRule)
|
|
313
317
|
} else {
|
|
314
|
-
updateWafRequestsMetricTags(metrics,
|
|
318
|
+
updateWafRequestsMetricTags(metrics, req)
|
|
315
319
|
}
|
|
316
320
|
|
|
317
321
|
reportTruncationMetrics(rootSpan, metrics)
|
|
@@ -331,9 +335,11 @@ function reportTruncationMetrics (rootSpan, metrics) {
|
|
|
331
335
|
}
|
|
332
336
|
}
|
|
333
337
|
|
|
334
|
-
function reportAttack ({ events: attackData, actions }) {
|
|
335
|
-
|
|
336
|
-
|
|
338
|
+
function reportAttack ({ events: attackData, actions }, req) {
|
|
339
|
+
if (!req) {
|
|
340
|
+
req = storage('legacy').getStore()?.req
|
|
341
|
+
}
|
|
342
|
+
|
|
337
343
|
const rootSpan = web.root(req)
|
|
338
344
|
if (!rootSpan) return
|
|
339
345
|
|
|
@@ -362,6 +368,14 @@ function reportAttack ({ events: attackData, actions }) {
|
|
|
362
368
|
|
|
363
369
|
rootSpan.addTags(newTags)
|
|
364
370
|
|
|
371
|
+
// Add _dd.appsec.json tag to inferred proxy span
|
|
372
|
+
if (config.inferredProxyServicesEnabled) {
|
|
373
|
+
const context = web.getContext(req)
|
|
374
|
+
if (context?.inferredProxySpan) {
|
|
375
|
+
context.inferredProxySpan.setTag('_dd.appsec.json', newTags['_dd.appsec.json'])
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
365
379
|
// TODO this should be deleted in a major
|
|
366
380
|
if (config.raspBodyCollection && isRaspAttack(attackData)) {
|
|
367
381
|
reportRequestBody(rootSpan, req.body, true)
|
|
@@ -463,10 +477,13 @@ function isSchemaAttribute (attribute) {
|
|
|
463
477
|
return attribute.startsWith('_dd.appsec.s.')
|
|
464
478
|
}
|
|
465
479
|
|
|
466
|
-
function reportAttributes (attributes) {
|
|
480
|
+
function reportAttributes (attributes, req) {
|
|
467
481
|
if (!attributes) return
|
|
468
482
|
|
|
469
|
-
|
|
483
|
+
if (!req) {
|
|
484
|
+
req = storage('legacy').getStore()?.req
|
|
485
|
+
}
|
|
486
|
+
|
|
470
487
|
const rootSpan = web.root(req)
|
|
471
488
|
|
|
472
489
|
if (!rootSpan) return
|
|
@@ -122,7 +122,7 @@ function run (data, req, raspRule) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const wafContext = waf.wafManager.getWAFContext(req)
|
|
125
|
-
const result = wafContext.run(data, raspRule)
|
|
125
|
+
const result = wafContext.run(data, raspRule, req)
|
|
126
126
|
|
|
127
127
|
if (result?.keep) {
|
|
128
128
|
if (limiter.isAllowed()) {
|
|
@@ -22,7 +22,7 @@ class WAFContextWrapper {
|
|
|
22
22
|
this.cachedUserIdResults = new Map()
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
run ({ persistent, ephemeral }, raspRule) {
|
|
25
|
+
run ({ persistent, ephemeral }, raspRule, req) {
|
|
26
26
|
if (this.ddwafContext.disposed) {
|
|
27
27
|
log.warn('[ASM] Calling run on a disposed context')
|
|
28
28
|
if (raspRule) {
|
|
@@ -141,10 +141,10 @@ class WAFContextWrapper {
|
|
|
141
141
|
metrics.wafTimeout = result.timeout
|
|
142
142
|
|
|
143
143
|
if (ruleTriggered) {
|
|
144
|
-
Reporter.reportAttack(result)
|
|
144
|
+
Reporter.reportAttack(result, req)
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
Reporter.reportAttributes(result.attributes)
|
|
147
|
+
Reporter.reportAttributes(result.attributes, req)
|
|
148
148
|
|
|
149
149
|
return result
|
|
150
150
|
} catch (err) {
|
|
@@ -156,7 +156,7 @@ class WAFContextWrapper {
|
|
|
156
156
|
wafRunFinished.publish({ payload })
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
Reporter.reportMetrics(metrics, raspRule)
|
|
159
|
+
Reporter.reportMetrics(metrics, raspRule, req)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -722,8 +722,10 @@ class Config {
|
|
|
722
722
|
// Priority:
|
|
723
723
|
// DD_SERVICE > tags.service > OTEL_SERVICE_NAME > NX_TASK_TARGET_PROJECT (if DD_ENABLE_NX_SERVICE_NAME) > default
|
|
724
724
|
let serviceName = DD_SERVICE || tags.service || OTEL_SERVICE_NAME
|
|
725
|
+
let isServiceNameInferred
|
|
725
726
|
if (!serviceName && NX_TASK_TARGET_PROJECT) {
|
|
726
727
|
if (isTrue(DD_ENABLE_NX_SERVICE_NAME)) {
|
|
728
|
+
isServiceNameInferred = true
|
|
727
729
|
serviceName = NX_TASK_TARGET_PROJECT
|
|
728
730
|
} else if (DD_MAJOR < 6) {
|
|
729
731
|
// Warn about v6 behavior change for Nx projects
|
|
@@ -734,6 +736,7 @@ class Config {
|
|
|
734
736
|
}
|
|
735
737
|
}
|
|
736
738
|
setString(target, 'service', serviceName)
|
|
739
|
+
if (serviceName) setBoolean(target, 'isServiceNameInferred', isServiceNameInferred ?? false)
|
|
737
740
|
if (DD_SERVICE_MAPPING) {
|
|
738
741
|
target.serviceMapping = Object.fromEntries(
|
|
739
742
|
DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
|
|
@@ -1004,7 +1007,11 @@ class Config {
|
|
|
1004
1007
|
setUnit(opts, 'sampleRate', options.sampleRate ?? options.ingestion.sampleRate)
|
|
1005
1008
|
opts['sampler.rateLimit'] = maybeInt(options.rateLimit ?? options.ingestion.rateLimit)
|
|
1006
1009
|
setSamplingRule(opts, 'sampler.rules', options.samplingRules)
|
|
1007
|
-
|
|
1010
|
+
const optService = options.service || tags.service
|
|
1011
|
+
setString(opts, 'service', optService)
|
|
1012
|
+
if (optService) {
|
|
1013
|
+
setBoolean(opts, 'isServiceNameInferred', false)
|
|
1014
|
+
}
|
|
1008
1015
|
opts.serviceMapping = options.serviceMapping
|
|
1009
1016
|
setString(opts, 'site', options.site)
|
|
1010
1017
|
if (options.spanAttributeSchema) {
|
|
@@ -1661,6 +1668,7 @@ function getAgentUrl (url, options) {
|
|
|
1661
1668
|
!options.port &&
|
|
1662
1669
|
!getEnv('DD_AGENT_HOST') &&
|
|
1663
1670
|
!getEnv('DD_TRACE_AGENT_PORT') &&
|
|
1671
|
+
!isTrue(getEnv('DD_CIVISIBILITY_AGENTLESS_ENABLED')) &&
|
|
1664
1672
|
fs.existsSync('/var/run/datadog/apm.socket')
|
|
1665
1673
|
) {
|
|
1666
1674
|
return new URL('unix:///var/run/datadog/apm.socket')
|
|
@@ -2182,6 +2182,13 @@
|
|
|
2182
2182
|
"default": "true"
|
|
2183
2183
|
}
|
|
2184
2184
|
],
|
|
2185
|
+
"DD_TRACE_AZURE_DURABLE_FUNCTIONS_ENABLED": [
|
|
2186
|
+
{
|
|
2187
|
+
"implementation": "B",
|
|
2188
|
+
"type": "boolean",
|
|
2189
|
+
"default": "true"
|
|
2190
|
+
}
|
|
2191
|
+
],
|
|
2185
2192
|
"DD_TRACE_AZURE_EVENTHUBS_BATCH_LINKS_ENABLED": [
|
|
2186
2193
|
{
|
|
2187
2194
|
"implementation": "A",
|
|
@@ -2988,6 +2995,13 @@
|
|
|
2988
2995
|
"default": "true"
|
|
2989
2996
|
}
|
|
2990
2997
|
],
|
|
2998
|
+
"DD_TRACE_LANGGRAPH_ENABLED": [
|
|
2999
|
+
{
|
|
3000
|
+
"implementation": "C",
|
|
3001
|
+
"type": "boolean",
|
|
3002
|
+
"default": "true"
|
|
3003
|
+
}
|
|
3004
|
+
],
|
|
2991
3005
|
"DD_TRACE_LDAPJS_ENABLED": [
|
|
2992
3006
|
{
|
|
2993
3007
|
"implementation": "A",
|
|
@@ -17,11 +17,13 @@ module.exports = {
|
|
|
17
17
|
SAMPLING_MECHANISM_SPAN: 8,
|
|
18
18
|
SAMPLING_MECHANISM_REMOTE_USER: 11,
|
|
19
19
|
SAMPLING_MECHANISM_REMOTE_DYNAMIC: 12,
|
|
20
|
+
SAMPLING_MECHANISM_AI_GUARD: 13,
|
|
20
21
|
SPAN_SAMPLING_MECHANISM: '_dd.span_sampling.mechanism',
|
|
21
22
|
SPAN_SAMPLING_RULE_RATE: '_dd.span_sampling.rule_rate',
|
|
22
23
|
SPAN_SAMPLING_MAX_PER_SECOND: '_dd.span_sampling.max_per_second',
|
|
23
24
|
DATADOG_LAMBDA_EXTENSION_PATH: '/opt/extensions/datadog-agent',
|
|
24
25
|
DECISION_MAKER_KEY: '_dd.p.dm',
|
|
26
|
+
SAMPLING_KNUTH_RATE: '_dd.p.ksr',
|
|
25
27
|
PROCESS_ID: 'process_id',
|
|
26
28
|
ERROR_TYPE: 'error.type',
|
|
27
29
|
ERROR_MESSAGE: 'error.message',
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const { workerData: { config: parentConfig, parentThreadId, configPort } } = require('node:worker_threads')
|
|
4
4
|
const { getAgentUrl } = require('../../agent/url')
|
|
5
|
+
const processTags = require('../../process-tags')
|
|
5
6
|
const log = require('./log')
|
|
6
7
|
|
|
8
|
+
processTags.initialize()
|
|
9
|
+
|
|
7
10
|
const config = module.exports = {
|
|
8
11
|
...parentConfig,
|
|
9
12
|
parentThreadId,
|
|
@@ -265,14 +265,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
265
265
|
}
|
|
266
266
|
const startTime = Date.now()
|
|
267
267
|
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
const testSessionEvents = rawEvents.filter(
|
|
271
|
-
event => event.type === 'test_session_end' || event.type === 'test_suite_end' || event.type === 'test_module_end'
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
const isTestSessionTrace = !!testSessionEvents.length
|
|
275
|
-
const events = isTestSessionTrace ? testSessionEvents : rawEvents
|
|
268
|
+
const events = trace.map(formatSpan)
|
|
276
269
|
|
|
277
270
|
this._eventCount += events.length
|
|
278
271
|
|