dd-trace 5.110.0 → 5.112.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 -2
- package/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +34 -1
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +37 -34
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +13 -2
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +15 -24
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +639 -12
- package/packages/datadog-plugin-graphql/src/index.js +37 -9
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +17 -6
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +31 -15
- package/packages/datadog-plugin-mocha/src/index.js +40 -15
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +4 -3
- package/packages/datadog-plugin-vitest/src/index.js +120 -72
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +14 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +127 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +23 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +15 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -11,45 +11,59 @@ const doGenerateAfterChannel = channel('dd-trace:vercel-ai:doGenerate:after')
|
|
|
11
11
|
const doStreamBeforeChannel = channel('dd-trace:vercel-ai:doStream:before')
|
|
12
12
|
const doStreamAfterChannel = channel('dd-trace:vercel-ai:doStream:after')
|
|
13
13
|
|
|
14
|
+
let isEnabled = false
|
|
15
|
+
let aiguard
|
|
16
|
+
let opts
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Subscribes AI Guard to Vercel AI lifecycle channels.
|
|
16
20
|
*
|
|
17
|
-
* @param {object}
|
|
21
|
+
* @param {object} aiguardInstance
|
|
18
22
|
* @param {boolean} block
|
|
19
|
-
* @returns {() => void}
|
|
20
23
|
*/
|
|
21
|
-
function enable (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
function onBefore (ctx) {
|
|
25
|
-
pushEvaluation(ctx, aiguard, convertVercelPromptToMessages(ctx.prompt), opts)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function onGenerateAfter (ctx) {
|
|
29
|
-
const inputMessages = convertVercelPromptToMessages(ctx.prompt)
|
|
30
|
-
if (!inputMessages.length || !ctx.result?.content?.length) return
|
|
31
|
-
|
|
32
|
-
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, ctx.result.content), opts)
|
|
33
|
-
}
|
|
24
|
+
function enable (aiguardInstance, block) {
|
|
25
|
+
if (isEnabled) return
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!inputMessages.length || !ctx.chunks?.length) return
|
|
38
|
-
|
|
39
|
-
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, getStreamContent(ctx.chunks)), opts)
|
|
40
|
-
}
|
|
27
|
+
aiguard = aiguardInstance
|
|
28
|
+
opts = { block, source: SOURCE_AUTO, integration: 'ai' }
|
|
41
29
|
|
|
42
30
|
doGenerateBeforeChannel.subscribe(onBefore)
|
|
43
31
|
doGenerateAfterChannel.subscribe(onGenerateAfter)
|
|
44
32
|
doStreamBeforeChannel.subscribe(onBefore)
|
|
45
33
|
doStreamAfterChannel.subscribe(onStreamAfter)
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
isEnabled = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function disable () {
|
|
39
|
+
if (!isEnabled) return
|
|
40
|
+
|
|
41
|
+
doGenerateBeforeChannel.unsubscribe(onBefore)
|
|
42
|
+
doGenerateAfterChannel.unsubscribe(onGenerateAfter)
|
|
43
|
+
doStreamBeforeChannel.unsubscribe(onBefore)
|
|
44
|
+
doStreamAfterChannel.unsubscribe(onStreamAfter)
|
|
45
|
+
|
|
46
|
+
aiguard = undefined
|
|
47
|
+
opts = undefined
|
|
48
|
+
isEnabled = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function onBefore (ctx) {
|
|
52
|
+
pushEvaluation(ctx, aiguard, convertVercelPromptToMessages(ctx.prompt), opts)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function onGenerateAfter (ctx) {
|
|
56
|
+
const inputMessages = convertVercelPromptToMessages(ctx.prompt)
|
|
57
|
+
if (!inputMessages.length || !ctx.result?.content?.length) return
|
|
58
|
+
|
|
59
|
+
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, ctx.result.content), opts)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function onStreamAfter (ctx) {
|
|
63
|
+
const inputMessages = convertVercelPromptToMessages(ctx.prompt)
|
|
64
|
+
if (!inputMessages.length || !ctx.chunks?.length) return
|
|
65
|
+
|
|
66
|
+
pushEvaluation(ctx, aiguard, buildOutputMessages(inputMessages, getStreamContent(ctx.chunks)), opts)
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/**
|
|
@@ -75,4 +89,4 @@ function getStreamContent (chunks) {
|
|
|
75
89
|
return text ? [{ type: 'text', text }] : []
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
module.exports = { enable }
|
|
92
|
+
module.exports = { enable, disable }
|
|
@@ -93,14 +93,14 @@ class AIGuard extends NoopAIGuard {
|
|
|
93
93
|
constructor (tracer, config) {
|
|
94
94
|
super()
|
|
95
95
|
|
|
96
|
-
if (!config.
|
|
96
|
+
if (!config.DD_API_KEY || !config.DD_APP_KEY) {
|
|
97
97
|
log.error('AIGuard: missing api and/or app keys, use env DD_API_KEY and DD_APP_KEY')
|
|
98
98
|
this.#initialized = false
|
|
99
99
|
return
|
|
100
100
|
}
|
|
101
101
|
this.#tracer = tracer
|
|
102
102
|
this.#headers = {
|
|
103
|
-
'DD-API-KEY': config.
|
|
103
|
+
'DD-API-KEY': config.DD_API_KEY,
|
|
104
104
|
'DD-APPLICATION-KEY': config.DD_APP_KEY,
|
|
105
105
|
'DD-AI-GUARD-VERSION': tracerVersion,
|
|
106
106
|
'DD-AI-GUARD-SOURCE': 'SDK',
|
|
@@ -31,11 +31,11 @@ class NoopTTLCache {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function configure ({ appsec, apmTracingEnabled }) {
|
|
34
|
-
enabled = appsec.
|
|
34
|
+
enabled = appsec.DD_API_SECURITY_ENABLED
|
|
35
35
|
asmStandaloneEnabled = apmTracingEnabled === false
|
|
36
|
-
sampledRequests = appsec.
|
|
36
|
+
sampledRequests = appsec.DD_API_SECURITY_SAMPLE_DELAY === 0
|
|
37
37
|
? new NoopTTLCache()
|
|
38
|
-
: new TTLCache({ max: MAX_SIZE, ttl: appsec.
|
|
38
|
+
: new TTLCache({ max: MAX_SIZE, ttl: appsec.DD_API_SECURITY_SAMPLE_DELAY * 1000 })
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function disable () {
|
|
@@ -27,6 +27,8 @@ module.exports = {
|
|
|
27
27
|
httpClientResponseFinish: dc.channel('apm:http:client:response:finish'),
|
|
28
28
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
29
29
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
30
|
+
lambdaStartInvocation: dc.channel('datadog:lambda:start-invocation'),
|
|
31
|
+
lambdaEndInvocation: dc.channel('datadog:lambda:end-invocation'),
|
|
30
32
|
multerParser: dc.channel('datadog:multer:read:finish'),
|
|
31
33
|
mysql2OuterQueryStart: dc.channel('datadog:mysql2:outerquery:start'),
|
|
32
34
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
@@ -43,7 +45,7 @@ module.exports = {
|
|
|
43
45
|
routerParam: dc.channel('datadog:router:param:start'),
|
|
44
46
|
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
45
47
|
setUncaughtExceptionCaptureCallbackStart: dc.channel('datadog:process:setUncaughtExceptionCaptureCallback:start'),
|
|
46
|
-
|
|
48
|
+
startGraphqlResolver: dc.channel('datadog:graphql:resolver:start'),
|
|
47
49
|
stripeCheckoutSessionCreate: dc.channel('datadog:stripe:checkoutSession:create:finish'),
|
|
48
50
|
stripeConstructEvent: dc.channel('datadog:stripe:constructEvent:finish'),
|
|
49
51
|
stripePaymentIntentCreate: dc.channel('datadog:stripe:paymentIntent:create:finish'),
|
|
@@ -41,7 +41,7 @@ function enable (_config) {
|
|
|
41
41
|
downstreamAnalysisCount = new WeakMap()
|
|
42
42
|
responseBodyIgnoredCount = new WeakMap()
|
|
43
43
|
|
|
44
|
-
const bodyAnalysisSampleRate = config.appsec.
|
|
44
|
+
const bodyAnalysisSampleRate = config.appsec.DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE
|
|
45
45
|
samplingRate = Math.min(Math.max(bodyAnalysisSampleRate, 0), 1)
|
|
46
46
|
|
|
47
47
|
if (samplingRate !== bodyAnalysisSampleRate) {
|
|
@@ -105,7 +105,7 @@ function recordResponseBodyIgnored (req, tag) {
|
|
|
105
105
|
* @returns {boolean} whether downstream response body should be collected for AppSec.
|
|
106
106
|
*/
|
|
107
107
|
function evaluateResponseBodyCollection (originatingReq, res) {
|
|
108
|
-
const maxBytes = config.appsec.
|
|
108
|
+
const maxBytes = config.appsec.DD_API_SECURITY_MAX_DOWNSTREAM_BODY_BYTES
|
|
109
109
|
|
|
110
110
|
const mime = extractMimeType(res.headers?.['content-type'])
|
|
111
111
|
if (!mime || !SUPPORTED_RESPONSE_BODY_MIME_TYPES.has(mime)) {
|
|
@@ -137,7 +137,7 @@ function shouldSampleBody (req) {
|
|
|
137
137
|
globalRequestCounter = (globalRequestCounter + 1n) & UINT64_MAX
|
|
138
138
|
|
|
139
139
|
const currentCount = bodyAnalysisCount.get(req) || 0
|
|
140
|
-
if (currentCount >= config.appsec.
|
|
140
|
+
if (currentCount >= config.appsec.DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS) {
|
|
141
141
|
return false
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -166,7 +166,7 @@ function isRedirectResponse (res) {
|
|
|
166
166
|
* @param {object} ctx http client instrumentation context (mutated).
|
|
167
167
|
*/
|
|
168
168
|
function planResponseBodyCollection (originatingReq, res, ctx) {
|
|
169
|
-
if (!config
|
|
169
|
+
if (!config) {
|
|
170
170
|
return
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
const waf = require('./waf')
|
|
13
13
|
const addresses = require('./addresses')
|
|
14
14
|
const {
|
|
15
|
-
|
|
15
|
+
startGraphqlResolver,
|
|
16
16
|
graphqlMiddlewareChannel,
|
|
17
17
|
apolloHttpServerChannel,
|
|
18
18
|
apolloChannel,
|
|
@@ -32,7 +32,7 @@ function disable () {
|
|
|
32
32
|
disableGraphql()
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function
|
|
35
|
+
function onGraphqlStartResolver ({ abortController, resolverInfo }) {
|
|
36
36
|
const req = getActiveRequest()
|
|
37
37
|
|
|
38
38
|
if (!req) return
|
|
@@ -46,7 +46,7 @@ function onGraphqlStartResolve ({ context, resolverInfo }) {
|
|
|
46
46
|
if (requestData?.isInGraphqlRequest) {
|
|
47
47
|
requestData.blocked = true
|
|
48
48
|
requestData.wafAction = blockingAction
|
|
49
|
-
|
|
49
|
+
abortController?.abort()
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -72,14 +72,17 @@ function enterInApolloServerCoreRequest () {
|
|
|
72
72
|
|
|
73
73
|
function enterInApolloRequest () {
|
|
74
74
|
const req = getActiveRequest()
|
|
75
|
+
if (!req) return
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
if (requestData) {
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
requestData
|
|
81
|
-
|
|
77
|
+
let requestData = graphqlRequestData.get(req)
|
|
78
|
+
if (!requestData) {
|
|
79
|
+
// executeHTTPGraphQLRequest is the GraphQL request boundary, so seed here
|
|
80
|
+
// when no upstream hook (express4 middleware, drainHttpServer) has run.
|
|
81
|
+
requestData = { blocked: false }
|
|
82
|
+
graphqlRequestData.set(req, requestData)
|
|
82
83
|
}
|
|
84
|
+
requestData.isInGraphqlRequest = true
|
|
85
|
+
addSpecificEndpoint(req, specificBlockingTypes.GRAPHQL)
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
function beforeWriteApolloGraphqlResponse ({ abortController, abortData }) {
|
|
@@ -153,11 +156,11 @@ function disableApollo () {
|
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
function enableGraphql () {
|
|
156
|
-
|
|
159
|
+
startGraphqlResolver.subscribe(onGraphqlStartResolver)
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
function disableGraphql () {
|
|
160
|
-
if (
|
|
163
|
+
if (startGraphqlResolver.hasSubscribers) startGraphqlResolver.unsubscribe(onGraphqlStartResolver)
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
module.exports = {
|
|
@@ -15,6 +15,20 @@ const EXCLUDED_PATHS_FROM_STACK = getNodeModulesPaths('mongodb', 'mongoose', 'mq
|
|
|
15
15
|
const SAFE_OPERATORS = new Set(['$eq', '$gt', '$gte', '$in', '$lt', '$lte', '$ne', '$nin',
|
|
16
16
|
'$exists', '$type', '$mod', '$bitsAllClear', '$bitsAllSet', '$bitsAnyClear', '$bitsAnySet'])
|
|
17
17
|
|
|
18
|
+
// Scope a marked child store for a query's deferred execution so the nested mongodb
|
|
19
|
+
// driver query skips re-analysis. `bindStore` enters the child only for that async
|
|
20
|
+
// scope and restores the parent on its own, so no marker can be stranded for a later
|
|
21
|
+
// request to inherit, and two concurrent queries of the same request each branch from
|
|
22
|
+
// the shared parent rather than from each other.
|
|
23
|
+
function markExecStore () {
|
|
24
|
+
const store = storage('legacy').getStore()
|
|
25
|
+
if (store && !store.nosqlAnalyzed && getIastContext(store)) {
|
|
26
|
+
return { ...store, nosqlAnalyzed: true }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return store
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
19
33
|
constructor () {
|
|
20
34
|
super(NOSQL_MONGODB_INJECTION)
|
|
@@ -27,39 +41,25 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
27
41
|
// Anything that accesses the storage is context dependent
|
|
28
42
|
const onStart = ({ filters }) => {
|
|
29
43
|
const store = storage('legacy').getStore()
|
|
30
|
-
if (store && !store.nosqlAnalyzed && filters?.length) {
|
|
44
|
+
if (store && !store.nosqlAnalyzed && filters?.length && getIastContext(store)) {
|
|
31
45
|
for (const filter of filters) {
|
|
32
46
|
this.analyze({ filter }, store)
|
|
33
47
|
}
|
|
34
48
|
}
|
|
35
|
-
|
|
36
|
-
return store
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const onStartAndEnterWithStore = (message) => {
|
|
40
|
-
const store = onStart(message || {})
|
|
41
|
-
if (store) {
|
|
42
|
-
storage('legacy').enterWith({ ...store, nosqlAnalyzed: true, nosqlParentStore: store })
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Anything that accesses the storage is context dependent
|
|
47
|
-
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
48
|
-
const onFinish = () => {
|
|
49
|
-
const store = storage('legacy').getStore()
|
|
50
|
-
if (store?.nosqlParentStore) {
|
|
51
|
-
storage('legacy').enterWith(store.nosqlParentStore)
|
|
52
|
-
}
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
this.addSub('datadog:mongodb:collection:filter:start', onStart)
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
// A mongoose query builds, executes, and reaches the driver in separate async
|
|
54
|
+
// steps. The filters are analyzed at the build, but the marker is bound around
|
|
55
|
+
// the execution channel, where the driver call still runs under the parent store.
|
|
56
|
+
this.addSub('datadog:mongoose:model:filter:start', onStart)
|
|
57
|
+
this.addBind('datadog:mongoose:model:filter:exec', markExecStore)
|
|
59
58
|
|
|
59
|
+
// mquery analyzes filters at the prepare step and marks the execution scope at
|
|
60
|
+
// tracing start, where the deferred driver call runs under the marked store.
|
|
60
61
|
this.addSub('datadog:mquery:filter:prepare', onStart)
|
|
61
|
-
this.
|
|
62
|
-
this.addSub('tracing:datadog:mquery:filter:asyncEnd', onFinish)
|
|
62
|
+
this.addBind('tracing:datadog:mquery:filter:start', markExecStore)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
configureSanitizers () {
|
|
@@ -21,10 +21,10 @@ let controlsKeys
|
|
|
21
21
|
let hooks
|
|
22
22
|
|
|
23
23
|
function configure (iastConfig) {
|
|
24
|
-
if (!iastConfig
|
|
24
|
+
if (!iastConfig.DD_IAST_SECURITY_CONTROLS_CONFIGURATION) return
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
controls = parse(iastConfig.
|
|
27
|
+
controls = parse(iastConfig.DD_IAST_SECURITY_CONTROLS_CONFIGURATION)
|
|
28
28
|
if (controls?.size > 0) {
|
|
29
29
|
hooks = new WeakSet()
|
|
30
30
|
controlsKeys = [...controls.keys()]
|
|
@@ -6,7 +6,8 @@ const { initRequestNamespace, finalizeRequestNamespace, globalNamespace } = requ
|
|
|
6
6
|
|
|
7
7
|
class Telemetry {
|
|
8
8
|
configure (config, verbosity) {
|
|
9
|
-
const telemetryAndMetricsEnabled = config
|
|
9
|
+
const telemetryAndMetricsEnabled = config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED &&
|
|
10
|
+
config.telemetry.DD_TELEMETRY_METRICS_ENABLED
|
|
10
11
|
|
|
11
12
|
this.verbosity = telemetryAndMetricsEnabled ? getVerbosity(verbosity) : Verbosity.OFF
|
|
12
13
|
this.enabled = this.verbosity !== Verbosity.OFF
|
|
@@ -16,6 +16,8 @@ const {
|
|
|
16
16
|
fastifyCookieParser,
|
|
17
17
|
incomingHttpRequestStart,
|
|
18
18
|
incomingHttpRequestEnd,
|
|
19
|
+
lambdaStartInvocation,
|
|
20
|
+
lambdaEndInvocation,
|
|
19
21
|
passportVerify,
|
|
20
22
|
passportUser,
|
|
21
23
|
expressSession,
|
|
@@ -43,6 +45,7 @@ const { isBlocked, block, callBlockDelegation, setTemplates, getBlockingAction }
|
|
|
43
45
|
const { getActiveRequest } = require('./store')
|
|
44
46
|
const UserTracking = require('./user_tracking')
|
|
45
47
|
const graphql = require('./graphql')
|
|
48
|
+
const lambda = require('./lambda')
|
|
46
49
|
const rasp = require('./rasp')
|
|
47
50
|
|
|
48
51
|
const responseAnalyzedSet = new WeakSet()
|
|
@@ -99,11 +102,15 @@ function enable (_config) {
|
|
|
99
102
|
stripeCheckoutSessionCreate.subscribe(onStripeCheckoutSessionCreate)
|
|
100
103
|
stripePaymentIntentCreate.subscribe(onStripePaymentIntentCreate)
|
|
101
104
|
stripeConstructEvent.subscribe(onStripeConstructEvent)
|
|
105
|
+
lambdaStartInvocation.subscribe(lambda.onLambdaStartInvocation)
|
|
106
|
+
lambdaEndInvocation.subscribe(lambda.onLambdaEndInvocation)
|
|
102
107
|
|
|
103
108
|
isEnabled = true
|
|
104
109
|
config = _config
|
|
105
110
|
} catch (err) {
|
|
106
|
-
if (
|
|
111
|
+
if (IS_SERVERLESS) {
|
|
112
|
+
log.debug('[ASM] Serverless mode: suppressing error log, calling disable()')
|
|
113
|
+
} else {
|
|
107
114
|
log.error('[ASM] Unable to start AppSec', err)
|
|
108
115
|
}
|
|
109
116
|
|
|
@@ -544,6 +551,8 @@ function disable () {
|
|
|
544
551
|
if (stripeCheckoutSessionCreate.hasSubscribers) stripeCheckoutSessionCreate.unsubscribe(onStripeCheckoutSessionCreate)
|
|
545
552
|
if (stripePaymentIntentCreate.hasSubscribers) stripePaymentIntentCreate.unsubscribe(onStripePaymentIntentCreate)
|
|
546
553
|
if (stripeConstructEvent.hasSubscribers) stripeConstructEvent.unsubscribe(onStripeConstructEvent)
|
|
554
|
+
if (lambdaStartInvocation.hasSubscribers) lambdaStartInvocation.unsubscribe(lambda.onLambdaStartInvocation)
|
|
555
|
+
if (lambdaEndInvocation.hasSubscribers) lambdaEndInvocation.unsubscribe(lambda.onLambdaEndInvocation)
|
|
547
556
|
}
|
|
548
557
|
|
|
549
558
|
/**
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
|
|
4
|
+
|
|
5
|
+
const log = require('../log')
|
|
6
|
+
const addresses = require('./addresses')
|
|
7
|
+
const Reporter = require('./reporter')
|
|
8
|
+
const waf = require('./waf')
|
|
9
|
+
|
|
10
|
+
// Tracks spans for which start-invocation has been processed, so that
|
|
11
|
+
// end-invocation can gate correctly
|
|
12
|
+
const activeInvocations = new WeakSet()
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Maps pre-extracted HTTP data from the Lambda event to WAF addresses,
|
|
16
|
+
* runs the WAF, and reports results on the span.
|
|
17
|
+
*
|
|
18
|
+
* @param {{ span: object, headers: Record<string, string>, method: string, path: string,
|
|
19
|
+
* query: Record<string, string | string[]> | undefined, body: string | object | undefined,
|
|
20
|
+
* isBase64Encoded: boolean, clientIp: string | undefined,
|
|
21
|
+
* pathParams: Record<string, string> | undefined,
|
|
22
|
+
* cookies: Record<string, string> | undefined,
|
|
23
|
+
* route: string | undefined }} data
|
|
24
|
+
*/
|
|
25
|
+
function onLambdaStartInvocation (data) {
|
|
26
|
+
try {
|
|
27
|
+
const { span, headers, method, path, query, body, clientIp, pathParams, cookies } = data
|
|
28
|
+
|
|
29
|
+
if (!span) {
|
|
30
|
+
log.warn('[ASM] No span provided in Lambda start invocation')
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
activeInvocations.add(span)
|
|
35
|
+
|
|
36
|
+
span.addTags({
|
|
37
|
+
'_dd.appsec.enabled': 1,
|
|
38
|
+
'_dd.runtime_family': 'nodejs',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const persistent = {}
|
|
42
|
+
|
|
43
|
+
if (path) {
|
|
44
|
+
persistent[addresses.HTTP_INCOMING_URL] = path
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (method) {
|
|
48
|
+
persistent[addresses.HTTP_INCOMING_METHOD] = method
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (headers) {
|
|
52
|
+
// Cookie header is already stripped by the Lambda layer's event-data-extractor
|
|
53
|
+
persistent[addresses.HTTP_INCOMING_HEADERS] = headers
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (clientIp) {
|
|
57
|
+
span.setTag(HTTP_CLIENT_IP, clientIp)
|
|
58
|
+
persistent[addresses.HTTP_CLIENT_IP] = clientIp
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (query) {
|
|
62
|
+
persistent[addresses.HTTP_INCOMING_QUERY] = query
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (body !== undefined && body !== null) {
|
|
66
|
+
persistent[addresses.HTTP_INCOMING_BODY] = body
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (pathParams) {
|
|
70
|
+
persistent[addresses.HTTP_INCOMING_PARAMS] = pathParams
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (cookies) {
|
|
74
|
+
persistent[addresses.HTTP_INCOMING_COOKIES] = cookies
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
waf.run({ persistent }, span, undefined, span)
|
|
78
|
+
} catch (err) {
|
|
79
|
+
log.error('[ASM] Error in Lambda start-invocation handler', err)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Maps response data to WAF addresses, runs a final WAF pass,
|
|
85
|
+
* disposes the WAF context, and finishes the request report.
|
|
86
|
+
*
|
|
87
|
+
* @param {{ span: object, statusCode: string | undefined,
|
|
88
|
+
* responseHeaders: Record<string, string> | undefined }} data
|
|
89
|
+
*/
|
|
90
|
+
function onLambdaEndInvocation (data) {
|
|
91
|
+
try {
|
|
92
|
+
const { span, statusCode, responseHeaders } = data
|
|
93
|
+
|
|
94
|
+
if (!span) {
|
|
95
|
+
log.warn('[ASM] No span provided in Lambda end invocation')
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!activeInvocations.has(span)) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
activeInvocations.delete(span)
|
|
104
|
+
|
|
105
|
+
let hasPersistentData = false
|
|
106
|
+
const persistent = {}
|
|
107
|
+
|
|
108
|
+
if (statusCode) {
|
|
109
|
+
persistent[addresses.HTTP_INCOMING_RESPONSE_CODE] = String(statusCode)
|
|
110
|
+
hasPersistentData = true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (responseHeaders) {
|
|
114
|
+
const filteredHeaders = { ...responseHeaders }
|
|
115
|
+
delete filteredHeaders['set-cookie']
|
|
116
|
+
persistent[addresses.HTTP_INCOMING_RESPONSE_HEADERS] = filteredHeaders
|
|
117
|
+
hasPersistentData = true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (hasPersistentData) {
|
|
121
|
+
waf.run({ persistent }, span, undefined, span)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
waf.disposeContext(span)
|
|
125
|
+
|
|
126
|
+
Reporter.finishRequest(span, null, {}, undefined, span)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
log.error('[ASM] Error in Lambda end-invocation handler', err)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
onLambdaStartInvocation,
|
|
134
|
+
onLambdaEndInvocation,
|
|
135
|
+
}
|
|
@@ -318,13 +318,21 @@ function reportWafConfigUpdate (product, rcConfigId, diagnostics, wafVersion) {
|
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* @param {object} metrics - WAF run metrics
|
|
323
|
+
* @param {string} [raspRule] - RASP rule identifier
|
|
324
|
+
* @param {object} [req] - Request key (plain object for lambda)
|
|
325
|
+
* @param {object} [rootSpan] - Root span (required for lambda)
|
|
326
|
+
*/
|
|
327
|
+
function reportMetrics (metrics, raspRule, req, rootSpan) {
|
|
322
328
|
if (!req) {
|
|
323
329
|
req = getActiveRequest()
|
|
324
330
|
}
|
|
325
|
-
|
|
331
|
+
if (!rootSpan) {
|
|
332
|
+
rootSpan = req && web.root(req)
|
|
333
|
+
}
|
|
326
334
|
|
|
327
|
-
if (!rootSpan) return
|
|
335
|
+
if (!req || !rootSpan) return
|
|
328
336
|
|
|
329
337
|
if (metrics.rulesVersion) {
|
|
330
338
|
rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
|
|
@@ -353,13 +361,26 @@ function reportTruncationMetrics (rootSpan, metrics) {
|
|
|
353
361
|
}
|
|
354
362
|
}
|
|
355
363
|
|
|
356
|
-
|
|
364
|
+
// NOTE: `req` in the WAF execution path may be any object, not necessarily
|
|
365
|
+
// an HTTP IncomingMessage. In Lambda, it is a plain context key ({}) with no
|
|
366
|
+
// HTTP properties. Always guard HTTP-specific property access
|
|
367
|
+
// See tests in lambda.spec.js for enforcement.
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @param {{ events: Array, actions: object }} result - WAF result with attack data
|
|
371
|
+
* @param {object} [req] - Request key. May be an HTTP IncomingMessage or a plain object (Lambda)
|
|
372
|
+
* @param {object} [rootSpan] - Root span (required for lambda)
|
|
373
|
+
*/
|
|
374
|
+
function reportAttack ({ events: attackData, actions }, req, rootSpan) {
|
|
357
375
|
if (!req) {
|
|
358
376
|
req = getActiveRequest()
|
|
359
377
|
}
|
|
360
378
|
|
|
361
|
-
|
|
362
|
-
|
|
379
|
+
if (!rootSpan) {
|
|
380
|
+
rootSpan = web.root(req)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (!req || !rootSpan) return
|
|
363
384
|
|
|
364
385
|
const spanContext = rootSpan.context()
|
|
365
386
|
|
|
@@ -494,14 +515,21 @@ function isSchemaAttribute (attribute) {
|
|
|
494
515
|
return attribute.startsWith('_dd.appsec.s.')
|
|
495
516
|
}
|
|
496
517
|
|
|
497
|
-
|
|
518
|
+
/**
|
|
519
|
+
* @param {object} [attributes] - WAF result attributes
|
|
520
|
+
* @param {object} [req] - Request key (plain object for lambda)
|
|
521
|
+
* @param {object} [rootSpan] - Root span (required for lambda)
|
|
522
|
+
*/
|
|
523
|
+
function reportAttributes (attributes, req, rootSpan) {
|
|
498
524
|
if (!attributes) return
|
|
499
525
|
|
|
500
526
|
if (!req) {
|
|
501
527
|
req = getActiveRequest()
|
|
502
528
|
}
|
|
503
529
|
|
|
504
|
-
|
|
530
|
+
if (!rootSpan) {
|
|
531
|
+
rootSpan = web.root(req)
|
|
532
|
+
}
|
|
505
533
|
|
|
506
534
|
if (!rootSpan) return
|
|
507
535
|
|
|
@@ -517,8 +545,17 @@ function reportAttributes (attributes, req) {
|
|
|
517
545
|
rootSpan.addTags(tags)
|
|
518
546
|
}
|
|
519
547
|
|
|
520
|
-
|
|
521
|
-
|
|
548
|
+
/**
|
|
549
|
+
* @param {object} [req] - Request key (null for Lambda)
|
|
550
|
+
* @param {object} [res] - Response object (null for lambda)
|
|
551
|
+
* @param {object} storedResponseHeaders
|
|
552
|
+
* @param {object} [requestBody]
|
|
553
|
+
* @param {object} [rootSpan] - Root span (required for lambda)
|
|
554
|
+
*/
|
|
555
|
+
function finishRequest (req, res, storedResponseHeaders, requestBody, rootSpan) {
|
|
556
|
+
if (!rootSpan) {
|
|
557
|
+
rootSpan = web.root(req)
|
|
558
|
+
}
|
|
522
559
|
if (!rootSpan) return
|
|
523
560
|
|
|
524
561
|
if (metricsQueue.size) {
|
|
@@ -569,6 +606,8 @@ function finishRequest (req, res, storedResponseHeaders, requestBody) {
|
|
|
569
606
|
rootSpan.setTag('_dd.appsec.rasp.rule.eval', metrics.raspEvalCount)
|
|
570
607
|
}
|
|
571
608
|
|
|
609
|
+
if (!req) return
|
|
610
|
+
|
|
572
611
|
incrementWafRequestsMetric(req)
|
|
573
612
|
|
|
574
613
|
const tags = rootSpan.context().getTags()
|
|
@@ -27,7 +27,7 @@ const metricsStoreMap = new WeakMap()
|
|
|
27
27
|
let enabled = false
|
|
28
28
|
|
|
29
29
|
function enable (config) {
|
|
30
|
-
enabled = config.telemetry
|
|
30
|
+
enabled = config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED && config.telemetry.DD_TELEMETRY_METRICS_ENABLED
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function disable () {
|
|
@@ -110,7 +110,19 @@ function removeConfig (configPath) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
// `req` in the WAF execution path may be any object, not necessarily
|
|
114
|
+
// an HTTP IncomingMessage (it is a plain object for lambda).
|
|
115
|
+
// Always guard HTTP-specific property access.
|
|
116
|
+
// @see packages/dd-trace/test/appsec/lambda.spec.js
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {object} data - WAF address data ({ persistent, ephemeral })
|
|
120
|
+
* @param {object} req - Request key for WAF context lookup. May be an HTTP
|
|
121
|
+
* IncomingMessage or a plain object (Lambda invocation key).
|
|
122
|
+
* @param {string} [raspRule] - RASP rule identifier
|
|
123
|
+
* @param {object} [rootSpan] - Root span to tag (required for Lambda)
|
|
124
|
+
*/
|
|
125
|
+
function run (data, req, raspRule, rootSpan) {
|
|
114
126
|
if (!req) {
|
|
115
127
|
req = getActiveRequest()
|
|
116
128
|
if (!req) {
|
|
@@ -120,12 +132,12 @@ function run (data, req, raspRule) {
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
const wafContext = waf.wafManager.getWAFContext(req)
|
|
123
|
-
const result = wafContext.run(data, raspRule, req)
|
|
135
|
+
const result = wafContext.run(data, raspRule, req, rootSpan)
|
|
124
136
|
|
|
125
137
|
if (result?.keep) {
|
|
126
138
|
if (limiter.isAllowed()) {
|
|
127
|
-
const
|
|
128
|
-
keepTrace(
|
|
139
|
+
const span = rootSpan || web.root(req)
|
|
140
|
+
keepTrace(span, ASM)
|
|
129
141
|
} else {
|
|
130
142
|
updateRateLimitedMetric(req)
|
|
131
143
|
}
|