dd-trace 5.109.0 → 5.111.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/README.md +13 -8
- package/ci/init.js +25 -5
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +79 -24
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +154 -47
- package/package.json +20 -16
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -484
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.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 +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1598
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- 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 +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
- 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 +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +76 -64
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +15 -3
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- 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 +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- 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 +20 -7
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +50 -10
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +27 -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/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -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 +3 -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 +55 -13
- 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/request.js +1 -17
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- 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 +25 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
- package/packages/dd-trace/src/config/helper.js +134 -33
- package/packages/dd-trace/src/config/index.js +39 -23
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +109 -57
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- 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/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +17 -6
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +8 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
- package/packages/dd-trace/src/llmobs/tagger.js +58 -3
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- 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/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +14 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +91 -9
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +11 -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 +20 -18
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/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
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -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
|
}
|
|
@@ -153,11 +153,11 @@ function disableApollo () {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
function enableGraphql () {
|
|
156
|
-
|
|
156
|
+
startGraphqlResolver.subscribe(onGraphqlStartResolver)
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
function disableGraphql () {
|
|
160
|
-
if (
|
|
160
|
+
if (startGraphqlResolver.hasSubscribers) startGraphqlResolver.unsubscribe(onGraphqlStartResolver)
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
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,7 +41,7 @@ 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
|
}
|
|
@@ -36,30 +50,23 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
36
50
|
return store
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
// mquery analyzes and marks on the same channel: its tracing start both reaches
|
|
54
|
+
// the driver scope and is the first place the filters are available.
|
|
55
|
+
const onStartAndBind = (message) => {
|
|
56
|
+
onStart(message)
|
|
57
|
+
return markExecStore()
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
this.addSub('datadog:mongodb:collection:filter:start', onStart)
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
// A mongoose query builds, executes, and reaches the driver in separate async
|
|
63
|
+
// steps. The filters are analyzed at the build, but the marker is bound around
|
|
64
|
+
// the execution channel, where the driver call still runs under the parent store.
|
|
65
|
+
this.addSub('datadog:mongoose:model:filter:start', onStart)
|
|
66
|
+
this.addBind('datadog:mongoose:model:filter:exec', markExecStore)
|
|
59
67
|
|
|
60
68
|
this.addSub('datadog:mquery:filter:prepare', onStart)
|
|
61
|
-
this.
|
|
62
|
-
this.addSub('tracing:datadog:mquery:filter:asyncEnd', onFinish)
|
|
69
|
+
this.addBind('tracing:datadog:mquery:filter:start', onStartAndBind)
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
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,
|
|
@@ -38,11 +40,12 @@ const waf = require('./waf')
|
|
|
38
40
|
const addresses = require('./addresses')
|
|
39
41
|
const Reporter = require('./reporter')
|
|
40
42
|
const appsecTelemetry = require('./telemetry')
|
|
41
|
-
const
|
|
43
|
+
const apiSecurity = require('./api_security')
|
|
42
44
|
const { isBlocked, block, callBlockDelegation, setTemplates, getBlockingAction } = require('./blocking')
|
|
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()
|
|
@@ -71,7 +74,7 @@ function enable (_config) {
|
|
|
71
74
|
|
|
72
75
|
Reporter.init(_config.appsec, _config.inferredProxyServicesEnabled)
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
apiSecurity.configure(_config)
|
|
75
78
|
|
|
76
79
|
UserTracking.setCollectionMode(_config.appsec.eventTracking.mode, false)
|
|
77
80
|
|
|
@@ -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
|
|
|
@@ -233,14 +240,18 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
233
240
|
// This hook runs before span finish, so ensure route/endpoint tags are available before API Security sampling runs.
|
|
234
241
|
web.setRouteOrEndpointTag(req)
|
|
235
242
|
|
|
236
|
-
|
|
243
|
+
const apiSecSamplingDecision = apiSecurity.sampleRequest(req, res, true)
|
|
244
|
+
if (apiSecSamplingDecision === apiSecurity.SamplingDecision.SAMPLE) {
|
|
237
245
|
persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
|
|
238
246
|
}
|
|
239
247
|
|
|
248
|
+
let wafResult
|
|
240
249
|
if (!isEmpty(persistent)) {
|
|
241
|
-
waf.run({ persistent }, req)
|
|
250
|
+
wafResult = waf.run({ persistent }, req)
|
|
242
251
|
}
|
|
243
252
|
|
|
253
|
+
apiSecurity.reportRequest(req, apiSecSamplingDecision, wafResult)
|
|
254
|
+
|
|
244
255
|
waf.disposeContext(req)
|
|
245
256
|
|
|
246
257
|
const storedHeaders = storedResponseHeaders.get(req) || {}
|
|
@@ -339,7 +350,7 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
|
|
|
339
350
|
|
|
340
351
|
function onResponseBody ({ req, res, body }) {
|
|
341
352
|
if (!body || typeof body !== 'object') return
|
|
342
|
-
if (
|
|
353
|
+
if (apiSecurity.sampleRequest(req, res) !== apiSecurity.SamplingDecision.SAMPLE) return
|
|
343
354
|
|
|
344
355
|
// we don't support blocking at this point, so no results needed
|
|
345
356
|
waf.run({
|
|
@@ -513,7 +524,7 @@ function disable () {
|
|
|
513
524
|
|
|
514
525
|
appsecRemoteConfig.disableWafUpdate()
|
|
515
526
|
|
|
516
|
-
|
|
527
|
+
apiSecurity.disable()
|
|
517
528
|
|
|
518
529
|
// Channel#unsubscribe() is undefined for non active channels
|
|
519
530
|
if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed)
|
|
@@ -540,6 +551,8 @@ function disable () {
|
|
|
540
551
|
if (stripeCheckoutSessionCreate.hasSubscribers) stripeCheckoutSessionCreate.unsubscribe(onStripeCheckoutSessionCreate)
|
|
541
552
|
if (stripePaymentIntentCreate.hasSubscribers) stripePaymentIntentCreate.unsubscribe(onStripePaymentIntentCreate)
|
|
542
553
|
if (stripeConstructEvent.hasSubscribers) stripeConstructEvent.unsubscribe(onStripeConstructEvent)
|
|
554
|
+
if (lambdaStartInvocation.hasSubscribers) lambdaStartInvocation.unsubscribe(lambda.onLambdaStartInvocation)
|
|
555
|
+
if (lambdaEndInvocation.hasSubscribers) lambdaEndInvocation.unsubscribe(lambda.onLambdaEndInvocation)
|
|
543
556
|
}
|
|
544
557
|
|
|
545
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()
|
|
@@ -615,4 +654,5 @@ module.exports = {
|
|
|
615
654
|
finishRequest,
|
|
616
655
|
mapHeaderAndTags,
|
|
617
656
|
truncateRequestBody,
|
|
657
|
+
isSchemaAttribute,
|
|
618
658
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const telemetryMetrics = require('../../telemetry/metrics')
|
|
4
|
+
|
|
5
|
+
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
6
|
+
|
|
7
|
+
const normalizedFrameworkCache = new Map()
|
|
8
|
+
|
|
9
|
+
function normalizeFramework (framework) {
|
|
10
|
+
let normalized = normalizedFrameworkCache.get(framework)
|
|
11
|
+
if (normalized === undefined) {
|
|
12
|
+
normalized = framework ? framework.toLowerCase().replaceAll(' ', '_') : 'unknown'
|
|
13
|
+
normalizedFrameworkCache.set(framework, normalized)
|
|
14
|
+
}
|
|
15
|
+
return normalized
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function incrementApiSecRequestSchema (framework) {
|
|
19
|
+
appsecMetrics.count('api_security.request.schema', { framework: normalizeFramework(framework) }).inc()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function incrementApiSecRequestNoSchema (framework) {
|
|
23
|
+
appsecMetrics.count('api_security.request.no_schema', { framework: normalizeFramework(framework) }).inc()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function incrementApiSecMissingRoute (framework) {
|
|
27
|
+
appsecMetrics.count('api_security.missing_route', { framework: normalizeFramework(framework) }).inc()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
incrementApiSecRequestSchema,
|
|
32
|
+
incrementApiSecRequestNoSchema,
|
|
33
|
+
incrementApiSecMissingRoute,
|
|
34
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const {
|
|
4
|
+
incrementApiSecMissingRoute,
|
|
5
|
+
incrementApiSecRequestNoSchema,
|
|
6
|
+
incrementApiSecRequestSchema,
|
|
7
|
+
} = require('./api_security')
|
|
3
8
|
const { DD_TELEMETRY_REQUEST_METRICS } = require('./common')
|
|
4
9
|
const { incrementMissingUserId, incrementMissingUserLogin, incrementSdkEvent } = require('./user')
|
|
5
10
|
const {
|
|
@@ -22,7 +27,7 @@ const metricsStoreMap = new WeakMap()
|
|
|
22
27
|
let enabled = false
|
|
23
28
|
|
|
24
29
|
function enable (config) {
|
|
25
|
-
enabled = config.telemetry
|
|
30
|
+
enabled = config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED && config.telemetry.DD_TELEMETRY_METRICS_ENABLED
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
function disable () {
|
|
@@ -155,6 +160,24 @@ function incrementSdkEventMetric (eventType, sdkVersion) {
|
|
|
155
160
|
incrementSdkEvent(eventType, sdkVersion)
|
|
156
161
|
}
|
|
157
162
|
|
|
163
|
+
function incrementApiSecRequestSchemaMetric (framework) {
|
|
164
|
+
if (!enabled) return
|
|
165
|
+
|
|
166
|
+
incrementApiSecRequestSchema(framework)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function incrementApiSecRequestNoSchemaMetric (framework) {
|
|
170
|
+
if (!enabled) return
|
|
171
|
+
|
|
172
|
+
incrementApiSecRequestNoSchema(framework)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function incrementApiSecMissingRouteMetric (framework) {
|
|
176
|
+
if (!enabled) return
|
|
177
|
+
|
|
178
|
+
incrementApiSecMissingRoute(framework)
|
|
179
|
+
}
|
|
180
|
+
|
|
158
181
|
function getRequestMetrics (req) {
|
|
159
182
|
if (req) {
|
|
160
183
|
const store = getStore(req)
|
|
@@ -179,6 +202,9 @@ module.exports = {
|
|
|
179
202
|
incrementMissingUserLoginMetric,
|
|
180
203
|
incrementMissingUserIdMetric,
|
|
181
204
|
incrementSdkEventMetric,
|
|
205
|
+
incrementApiSecRequestSchemaMetric,
|
|
206
|
+
incrementApiSecRequestNoSchemaMetric,
|
|
207
|
+
incrementApiSecMissingRouteMetric,
|
|
182
208
|
|
|
183
209
|
getRequestMetrics,
|
|
184
210
|
}
|
|
@@ -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
|
}
|