dd-trace 5.55.0 → 5.57.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 -0
- package/index.d.ts +44 -2
- package/init.js +4 -1
- package/package.json +24 -23
- package/packages/datadog-core/src/utils/src/set.js +8 -10
- package/packages/datadog-esbuild/index.js +22 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +43 -60
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +12 -12
- package/packages/datadog-instrumentations/src/cucumber.js +4 -6
- package/packages/datadog-instrumentations/src/elasticsearch.js +16 -19
- package/packages/datadog-instrumentations/src/fastify.js +91 -9
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +20 -5
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +17 -5
- package/packages/datadog-instrumentations/src/ioredis.js +8 -13
- package/packages/datadog-instrumentations/src/iovalkey.js +10 -14
- package/packages/datadog-instrumentations/src/jest.js +423 -325
- package/packages/datadog-instrumentations/src/memcached.js +17 -24
- package/packages/datadog-instrumentations/src/mocha/main.js +7 -6
- package/packages/datadog-instrumentations/src/moleculer/client.js +9 -10
- package/packages/datadog-instrumentations/src/moleculer/server.js +12 -13
- package/packages/datadog-instrumentations/src/openai.js +30 -2
- package/packages/datadog-instrumentations/src/playwright.js +4 -1
- package/packages/datadog-instrumentations/src/prisma.js +116 -0
- package/packages/datadog-instrumentations/src/redis.js +32 -43
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/sharedb.js +10 -16
- package/packages/datadog-instrumentations/src/vitest.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +9 -4
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +3 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
- package/packages/datadog-plugin-bunyan/src/index.js +2 -2
- package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -2
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +1 -1
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +1 -1
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +4 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -5
- package/packages/datadog-plugin-elasticsearch/src/index.js +12 -4
- package/packages/datadog-plugin-http/src/client.js +1 -0
- package/packages/datadog-plugin-http/src/server.js +2 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -0
- package/packages/datadog-plugin-http2/src/server.js +1 -0
- package/packages/datadog-plugin-jest/src/index.js +4 -3
- package/packages/datadog-plugin-memcached/src/index.js +6 -2
- package/packages/datadog-plugin-mocha/src/index.js +3 -2
- package/packages/datadog-plugin-moleculer/src/client.js +15 -9
- package/packages/datadog-plugin-moleculer/src/server.js +9 -5
- package/packages/datadog-plugin-next/src/index.js +2 -1
- package/packages/datadog-plugin-openai/src/tracing.js +127 -80
- package/packages/datadog-plugin-oracledb/src/index.js +2 -1
- package/packages/datadog-plugin-pino/src/index.js +2 -2
- package/packages/datadog-plugin-prisma/src/client.js +62 -0
- package/packages/datadog-plugin-prisma/src/engine.js +81 -0
- package/packages/datadog-plugin-prisma/src/index.js +22 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-redis/src/index.js +9 -3
- package/packages/datadog-plugin-router/src/index.js +1 -0
- package/packages/datadog-plugin-sharedb/src/index.js +13 -5
- package/packages/datadog-plugin-winston/src/index.js +2 -2
- package/packages/dd-trace/src/appsec/channels.js +26 -21
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +13 -20
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +0 -1
- package/packages/dd-trace/src/appsec/index.js +28 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +0 -5
- package/packages/dd-trace/src/appsec/reporter.js +6 -4
- package/packages/dd-trace/src/baggage.js +2 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -3
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- 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/writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -6
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -3
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -3
- package/packages/dd-trace/src/config.js +334 -312
- package/packages/dd-trace/src/constants.js +2 -1
- package/packages/dd-trace/src/crashtracking/crashtracker.js +12 -14
- package/packages/dd-trace/src/datastreams/context.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/datastreams/writer.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -3
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -3
- package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -4
- package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
- package/packages/dd-trace/src/dogstatsd.js +3 -3
- package/packages/dd-trace/src/exporters/agent/index.js +10 -5
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -2
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/log/index.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/writer.js +2 -2
- package/packages/dd-trace/src/guardrails/index.js +3 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/index.js +11 -5
- package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +13 -13
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
- package/packages/dd-trace/src/llmobs/writers/spans.js +2 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -28
- package/packages/dd-trace/src/opentracing/span.js +1 -0
- package/packages/dd-trace/src/plugin_manager.js +3 -3
- package/packages/dd-trace/src/plugins/cache.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +11 -7
- package/packages/dd-trace/src/plugins/database.js +3 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +5 -1
- package/packages/dd-trace/src/plugins/outbound.js +8 -6
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +9 -0
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +83 -30
- package/packages/dd-trace/src/plugins/util/git.js +1 -0
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +3 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -0
- package/packages/dd-trace/src/plugins/util/tags.js +4 -1
- package/packages/dd-trace/src/plugins/util/test.js +80 -10
- package/packages/dd-trace/src/plugins/util/web.js +1 -0
- package/packages/dd-trace/src/profiler.js +0 -2
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +18 -126
- package/packages/dd-trace/src/proxy.js +12 -27
- package/packages/dd-trace/src/runtime_metrics/index.js +1 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +14 -45
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/supported-configurations.json +13 -3
- package/packages/dd-trace/src/telemetry/telemetry.js +11 -4
- package/packages/dd-trace/src/tracer.js +11 -0
- package/packages/dd-trace/src/tracer_metadata.js +25 -0
- package/packages/dd-trace/src/util.js +11 -4
- package/version.js +3 -1
- package/packages/datadog-core/src/utils/src/get.js +0 -11
- package/packages/datadog-core/src/utils/src/has.js +0 -14
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +0 -30
|
@@ -5,7 +5,9 @@ const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
|
5
5
|
class SharedbPlugin extends ServerPlugin {
|
|
6
6
|
static get id () { return 'sharedb' }
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
bindStart (ctx) {
|
|
9
|
+
const { actionName, request } = ctx
|
|
10
|
+
|
|
9
11
|
const span = this.startSpan('sharedb.request', {
|
|
10
12
|
service: this.config.service,
|
|
11
13
|
resource: getReadableResourceName(actionName, request.c, request.q),
|
|
@@ -13,19 +15,25 @@ class SharedbPlugin extends ServerPlugin {
|
|
|
13
15
|
meta: {
|
|
14
16
|
'sharedb.action': actionName
|
|
15
17
|
}
|
|
16
|
-
})
|
|
18
|
+
}, ctx)
|
|
17
19
|
|
|
18
20
|
if (this.config.hooks && this.config.hooks.receive) {
|
|
19
21
|
this.config.hooks.receive(span, request)
|
|
20
22
|
}
|
|
23
|
+
|
|
24
|
+
return ctx.currentStore
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
const
|
|
27
|
+
bindFinish (ctx) {
|
|
28
|
+
const { request, res } = ctx
|
|
29
|
+
|
|
30
|
+
const span = ctx.currentStore.span
|
|
25
31
|
if (this.config.hooks && this.config.hooks.reply) {
|
|
26
32
|
this.config.hooks.reply(span, request, res)
|
|
27
33
|
}
|
|
28
|
-
super.finish()
|
|
34
|
+
super.finish(ctx)
|
|
35
|
+
|
|
36
|
+
return ctx.parentStore
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const StructuredLogPlugin = require('../../dd-trace/src/plugins/structured_log_plugin')
|
|
4
4
|
|
|
5
|
-
class WinstonPlugin extends
|
|
5
|
+
class WinstonPlugin extends StructuredLogPlugin {
|
|
6
6
|
static get id () {
|
|
7
7
|
return 'winston'
|
|
8
8
|
}
|
|
@@ -4,34 +4,39 @@ const dc = require('dc-polyfill')
|
|
|
4
4
|
|
|
5
5
|
// TODO: use TBD naming convention
|
|
6
6
|
module.exports = {
|
|
7
|
+
apolloChannel: dc.tracingChannel('datadog:apollo:request'),
|
|
8
|
+
apolloServerCoreChannel: dc.tracingChannel('datadog:apollo-server-core:request'),
|
|
7
9
|
bodyParser: dc.channel('datadog:body-parser:read:finish'),
|
|
10
|
+
childProcessExecutionTracingChannel: dc.tracingChannel('datadog:child_process:execution'),
|
|
8
11
|
cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
expressMiddlewareError: dc.channel('apm:express:middleware:error'),
|
|
13
|
+
expressProcessParams: dc.channel('datadog:express:process_params:start'),
|
|
14
|
+
expressSession: dc.channel('datadog:express-session:middleware:finish'),
|
|
15
|
+
fastifyBodyParser: dc.channel('datadog:fastify:body-parser:finish'),
|
|
16
|
+
fastifyResponseChannel: dc.channel('datadog:fastify:response:finish'),
|
|
17
|
+
fastifyQueryParams: dc.channel('datadog:fastify:query-params:finish'),
|
|
18
|
+
fastifyCookieParser: dc.channel('datadog:fastify-cookie:read:finish'),
|
|
19
|
+
fastifyPathParams: dc.channel('datadog:fastify:path-params:finish'),
|
|
20
|
+
fsOperationStart: dc.channel('apm:fs:operation:start'),
|
|
11
21
|
graphqlMiddlewareChannel: dc.tracingChannel('datadog:apollo:middleware'),
|
|
12
|
-
|
|
13
|
-
apolloServerCoreChannel: dc.tracingChannel('datadog:apollo-server-core:request'),
|
|
14
|
-
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
22
|
+
httpClientRequestStart: dc.channel('apm:http:client:request:start'),
|
|
15
23
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
queryParser: dc.channel('datadog:query:read:finish'),
|
|
20
|
-
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
24
|
+
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
25
|
+
multerParser: dc.channel('datadog:multer:read:finish'),
|
|
26
|
+
mysql2OuterQueryStart: dc.channel('datadog:mysql2:outerquery:start'),
|
|
21
27
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
22
28
|
nextQueryParsed: dc.channel('apm:next:query-parsed'),
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
passportUser: dc.channel('datadog:passport:deserializeUser:finish'),
|
|
30
|
+
passportVerify: dc.channel('datadog:passport:verify:finish'),
|
|
31
|
+
pgPoolQueryStart: dc.channel('datadog:pg:pool:query:start'),
|
|
32
|
+
pgQueryStart: dc.channel('apm:pg:query:start'),
|
|
33
|
+
queryParser: dc.channel('datadog:query:read:finish'),
|
|
25
34
|
responseBody: dc.channel('datadog:express:response:json:start'),
|
|
26
|
-
responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'),
|
|
27
|
-
httpClientRequestStart: dc.channel('apm:http:client:request:start'),
|
|
28
35
|
responseSetHeader: dc.channel('datadog:http:server:response:set-header:start'),
|
|
36
|
+
responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'),
|
|
37
|
+
routerParam: dc.channel('datadog:router:param:start'),
|
|
38
|
+
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
29
39
|
setUncaughtExceptionCaptureCallbackStart: dc.channel('datadog:process:setUncaughtExceptionCaptureCallback:start'),
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
mysql2OuterQueryStart: dc.channel('datadog:mysql2:outerquery:start'),
|
|
33
|
-
wafRunFinished: dc.channel('datadog:waf:run:finish'),
|
|
34
|
-
fsOperationStart: dc.channel('apm:fs:operation:start'),
|
|
35
|
-
expressMiddlewareError: dc.channel('apm:express:middleware:error'),
|
|
36
|
-
childProcessExecutionTracingChannel: dc.tracingChannel('datadog:child_process:execution')
|
|
40
|
+
startGraphqlResolve: dc.channel('datadog:graphql:resolver:start'),
|
|
41
|
+
wafRunFinished: dc.channel('datadog:waf:run:finish')
|
|
37
42
|
}
|
|
@@ -15,7 +15,7 @@ class SqlInjectionAnalyzer extends StoredInjectionAnalyzer {
|
|
|
15
15
|
|
|
16
16
|
onConfigure () {
|
|
17
17
|
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
|
|
18
|
-
this.addSub('
|
|
18
|
+
this.addSub('datadog:mysql2:outerquery:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
|
|
19
19
|
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, undefined, 'POSTGRES'))
|
|
20
20
|
|
|
21
21
|
this.addSub(
|
|
@@ -5,13 +5,17 @@ const { UNVALIDATED_REDIRECT } = require('../vulnerabilities')
|
|
|
5
5
|
const { getNodeModulesPaths } = require('../path-line')
|
|
6
6
|
const { getRanges } = require('../taint-tracking/operations')
|
|
7
7
|
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
HTTP_REQUEST_URI
|
|
8
|
+
HTTP_REQUEST_BODY,
|
|
9
|
+
HTTP_REQUEST_PARAMETER
|
|
11
10
|
} = require('../taint-tracking/source-types')
|
|
12
11
|
|
|
13
12
|
const EXCLUDED_PATHS = getNodeModulesPaths('express/lib/response.js')
|
|
14
13
|
|
|
14
|
+
const VULNERABLE_SOURCE_TYPES = new Set([
|
|
15
|
+
HTTP_REQUEST_BODY,
|
|
16
|
+
HTTP_REQUEST_PARAMETER
|
|
17
|
+
])
|
|
18
|
+
|
|
15
19
|
class UnvalidatedRedirectAnalyzer extends InjectionAnalyzer {
|
|
16
20
|
constructor () {
|
|
17
21
|
super(UNVALIDATED_REDIRECT)
|
|
@@ -35,28 +39,17 @@ class UnvalidatedRedirectAnalyzer extends InjectionAnalyzer {
|
|
|
35
39
|
if (!value) return false
|
|
36
40
|
|
|
37
41
|
const ranges = getRanges(iastContext, value)
|
|
38
|
-
return ranges
|
|
42
|
+
return ranges?.length > 0 && this._hasUnsafeRange(ranges)
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return ranges && ranges.every(
|
|
45
|
-
range => this._isPathParam(range) || this._isUrl(range) || this._isRefererHeader(range)
|
|
45
|
+
_hasUnsafeRange (ranges) {
|
|
46
|
+
return ranges.some(
|
|
47
|
+
range => this._isVulnerableRange(range)
|
|
46
48
|
)
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
return range.iinfo.type
|
|
51
|
-
range.iinfo.parameterName && range.iinfo.parameterName.toLowerCase() === 'referer'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
_isPathParam (range) {
|
|
55
|
-
return range.iinfo.type === HTTP_REQUEST_PATH_PARAM
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_isUrl (range) {
|
|
59
|
-
return range.iinfo.type === HTTP_REQUEST_URI
|
|
51
|
+
_isVulnerableRange (range) {
|
|
52
|
+
return VULNERABLE_SOURCE_TYPES.has(range.iinfo.type)
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
_getExcludedPaths () {
|
|
@@ -85,7 +85,7 @@ function hookModule (filename, module, controlsByFile) {
|
|
|
85
85
|
}
|
|
86
86
|
})
|
|
87
87
|
} catch (e) {
|
|
88
|
-
log.error('[ASM] Error initializing IAST security control for %', filename, e)
|
|
88
|
+
log.error('[ASM] Error initializing IAST security control for %s', filename, e)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
return module
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
4
4
|
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
5
|
+
const { HTTP_REQUEST_PARAMETER } = require('./source-types')
|
|
5
6
|
const log = require('../../../log')
|
|
6
7
|
|
|
8
|
+
const SEPARATOR = '\u0000' // Unit Separator (cannot be in URL keys)
|
|
9
|
+
|
|
7
10
|
function taintObject (iastContext, object, type) {
|
|
8
11
|
let result = object
|
|
9
12
|
const transactionId = iastContext?.[IAST_TRANSACTION_ID]
|
|
@@ -40,6 +43,46 @@ function taintObject (iastContext, object, type) {
|
|
|
40
43
|
return result
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
function taintQueryWithCache (iastContext, query) {
|
|
47
|
+
const transactionId = iastContext?.[IAST_TRANSACTION_ID]
|
|
48
|
+
if (!transactionId || !query) return query
|
|
49
|
+
|
|
50
|
+
iastContext.queryCache ??= new Map() // key: "a.b.c", value: tainted string
|
|
51
|
+
|
|
52
|
+
traverseAndTaint(query, '', iastContext.queryCache, transactionId)
|
|
53
|
+
return query
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function traverseAndTaint (node, path, cache, transactionId) {
|
|
57
|
+
if (node == null) return node
|
|
58
|
+
|
|
59
|
+
if (typeof node === 'string') {
|
|
60
|
+
const cachedValue = cache.get(path)
|
|
61
|
+
|
|
62
|
+
if (cachedValue === node) {
|
|
63
|
+
return cachedValue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tainted = TaintedUtils.newTaintedString(transactionId, node, path, HTTP_REQUEST_PARAMETER)
|
|
67
|
+
cache.set(path, tainted)
|
|
68
|
+
|
|
69
|
+
return tainted
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof node === 'object') {
|
|
73
|
+
const keys = Array.isArray(node) ? node.keys() : Object.keys(node)
|
|
74
|
+
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
const childPath = path ? `${path}${SEPARATOR}${key}` : String(key)
|
|
77
|
+
const tainted = traverseAndTaint(node[key], childPath, cache, transactionId)
|
|
78
|
+
node[key] = tainted
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return node
|
|
83
|
+
}
|
|
84
|
+
|
|
43
85
|
module.exports = {
|
|
44
|
-
taintObject
|
|
86
|
+
taintObject,
|
|
87
|
+
taintQueryWithCache
|
|
45
88
|
}
|
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
getTaintTrackingNoop,
|
|
12
12
|
lodashTaintTrackingHandler
|
|
13
13
|
} = require('./taint-tracking-impl')
|
|
14
|
-
const { taintObject } = require('./operations-taint-object')
|
|
14
|
+
const { taintObject, taintQueryWithCache } = require('./operations-taint-object')
|
|
15
15
|
|
|
16
16
|
const lodashOperationCh = dc.channel('datadog:lodash:operation')
|
|
17
17
|
|
|
@@ -98,6 +98,7 @@ module.exports = {
|
|
|
98
98
|
newTaintedString,
|
|
99
99
|
newTaintedObject,
|
|
100
100
|
taintObject,
|
|
101
|
+
taintQueryWithCache,
|
|
101
102
|
isTainted,
|
|
102
103
|
getRanges,
|
|
103
104
|
enableTaintOperations,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { SourceIastPlugin } = require('../iast-plugin')
|
|
4
4
|
const { getIastContext } = require('../iast-context')
|
|
5
5
|
const { storage } = require('../../../../../datadog-core')
|
|
6
|
-
const { taintObject, newTaintedString, getRanges } = require('./operations')
|
|
6
|
+
const { taintObject, newTaintedString, getRanges, taintQueryWithCache } = require('./operations')
|
|
7
7
|
const {
|
|
8
8
|
HTTP_REQUEST_BODY,
|
|
9
9
|
HTTP_REQUEST_COOKIE_VALUE,
|
|
@@ -63,7 +63,12 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
63
63
|
|
|
64
64
|
this.addSub(
|
|
65
65
|
{ channelName: 'datadog:express:query:finish', tag: HTTP_REQUEST_PARAMETER },
|
|
66
|
-
({ query }) =>
|
|
66
|
+
({ query }) => {
|
|
67
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
68
|
+
if (!iastContext || !query) return
|
|
69
|
+
|
|
70
|
+
taintQueryWithCache(iastContext, query)
|
|
71
|
+
}
|
|
67
72
|
)
|
|
68
73
|
|
|
69
74
|
this.addSub(
|
|
@@ -7,7 +7,6 @@ module.exports = {
|
|
|
7
7
|
HTTP_REQUEST_HEADER_NAME: 'http.request.header.name',
|
|
8
8
|
HTTP_REQUEST_HEADER_VALUE: 'http.request.header',
|
|
9
9
|
HTTP_REQUEST_PARAMETER: 'http.request.parameter',
|
|
10
|
-
HTTP_REQUEST_PATH: 'http.request.path',
|
|
11
10
|
HTTP_REQUEST_PATH_PARAM: 'http.request.path.parameter',
|
|
12
11
|
HTTP_REQUEST_URI: 'http.request.uri',
|
|
13
12
|
KAFKA_MESSAGE_KEY: 'kafka.message.key',
|
|
@@ -7,6 +7,8 @@ const {
|
|
|
7
7
|
bodyParser,
|
|
8
8
|
cookieParser,
|
|
9
9
|
multerParser,
|
|
10
|
+
fastifyBodyParser,
|
|
11
|
+
fastifyCookieParser,
|
|
10
12
|
incomingHttpRequestStart,
|
|
11
13
|
incomingHttpRequestEnd,
|
|
12
14
|
passportVerify,
|
|
@@ -16,10 +18,13 @@ const {
|
|
|
16
18
|
nextBodyParsed,
|
|
17
19
|
nextQueryParsed,
|
|
18
20
|
expressProcessParams,
|
|
21
|
+
fastifyQueryParams,
|
|
19
22
|
responseBody,
|
|
20
23
|
responseWriteHead,
|
|
21
24
|
responseSetHeader,
|
|
22
|
-
routerParam
|
|
25
|
+
routerParam,
|
|
26
|
+
fastifyResponseChannel,
|
|
27
|
+
fastifyPathParams
|
|
23
28
|
} = require('./channels')
|
|
24
29
|
const waf = require('./waf')
|
|
25
30
|
const addresses = require('./addresses')
|
|
@@ -37,6 +42,7 @@ const rasp = require('./rasp')
|
|
|
37
42
|
const { isInServerlessEnvironment } = require('../serverless')
|
|
38
43
|
|
|
39
44
|
const responseAnalyzedSet = new WeakSet()
|
|
45
|
+
const storedResponseHeaders = new WeakMap()
|
|
40
46
|
|
|
41
47
|
let isEnabled = false
|
|
42
48
|
let config
|
|
@@ -76,8 +82,13 @@ function enable (_config) {
|
|
|
76
82
|
nextBodyParsed.subscribe(onRequestBodyParsed)
|
|
77
83
|
nextQueryParsed.subscribe(onRequestQueryParsed)
|
|
78
84
|
expressProcessParams.subscribe(onRequestProcessParams)
|
|
85
|
+
fastifyBodyParser.subscribe(onRequestBodyParsed)
|
|
86
|
+
fastifyQueryParams.subscribe(onRequestQueryParsed)
|
|
87
|
+
fastifyCookieParser.subscribe(onRequestCookieParser)
|
|
88
|
+
fastifyPathParams.subscribe(onRequestProcessParams)
|
|
79
89
|
routerParam.subscribe(onRequestProcessParams)
|
|
80
90
|
responseBody.subscribe(onResponseBody)
|
|
91
|
+
fastifyResponseChannel.subscribe(onResponseBody)
|
|
81
92
|
responseWriteHead.subscribe(onResponseWriteHead)
|
|
82
93
|
responseSetHeader.subscribe(onResponseSetHeader)
|
|
83
94
|
|
|
@@ -187,7 +198,13 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
187
198
|
|
|
188
199
|
waf.disposeContext(req)
|
|
189
200
|
|
|
190
|
-
|
|
201
|
+
const storedHeaders = storedResponseHeaders.get(req) || {}
|
|
202
|
+
|
|
203
|
+
Reporter.finishRequest(req, res, storedHeaders)
|
|
204
|
+
|
|
205
|
+
if (storedHeaders) {
|
|
206
|
+
storedResponseHeaders.delete(req)
|
|
207
|
+
}
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
function onPassportVerify ({ framework, login, user, success, abortController }) {
|
|
@@ -285,6 +302,10 @@ function onResponseBody ({ req, res, body }) {
|
|
|
285
302
|
}
|
|
286
303
|
|
|
287
304
|
function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
|
|
305
|
+
if (Object.keys(responseHeaders).length) {
|
|
306
|
+
storedResponseHeaders.set(req, responseHeaders)
|
|
307
|
+
}
|
|
308
|
+
|
|
288
309
|
// avoid "write after end" error
|
|
289
310
|
if (isBlocked(res)) {
|
|
290
311
|
abortController?.abort()
|
|
@@ -356,8 +377,13 @@ function disable () {
|
|
|
356
377
|
if (nextBodyParsed.hasSubscribers) nextBodyParsed.unsubscribe(onRequestBodyParsed)
|
|
357
378
|
if (nextQueryParsed.hasSubscribers) nextQueryParsed.unsubscribe(onRequestQueryParsed)
|
|
358
379
|
if (expressProcessParams.hasSubscribers) expressProcessParams.unsubscribe(onRequestProcessParams)
|
|
380
|
+
if (fastifyBodyParser.hasSubscribers) fastifyBodyParser.unsubscribe(onRequestBodyParsed)
|
|
381
|
+
if (fastifyQueryParams.hasSubscribers) fastifyQueryParams.unsubscribe(onRequestQueryParsed)
|
|
382
|
+
if (fastifyCookieParser.hasSubscribers) fastifyCookieParser.unsubscribe(onRequestCookieParser)
|
|
383
|
+
if (fastifyPathParams.hasSubscribers) fastifyPathParams.unsubscribe(onRequestProcessParams)
|
|
359
384
|
if (routerParam.hasSubscribers) routerParam.unsubscribe(onRequestProcessParams)
|
|
360
385
|
if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
|
|
386
|
+
if (fastifyResponseChannel.hasSubscribers) fastifyResponseChannel.unsubscribe(onResponseBody)
|
|
361
387
|
if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
|
|
362
388
|
if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
|
|
363
389
|
}
|
|
@@ -58,11 +58,6 @@ function handleResult (result, req, res, abortController, config, raspRule) {
|
|
|
58
58
|
const abortError = new DatadogRaspAbortError(req, res, blockingAction, raspRule, ruleTriggered)
|
|
59
59
|
abortController.abort(abortError)
|
|
60
60
|
|
|
61
|
-
// TODO Delete this when support for node 16 is removed
|
|
62
|
-
if (!abortController.signal.reason) {
|
|
63
|
-
abortController.signal.reason = abortError
|
|
64
|
-
}
|
|
65
|
-
|
|
66
61
|
return
|
|
67
62
|
}
|
|
68
63
|
}
|
|
@@ -140,14 +140,16 @@ function filterExtendedHeaders (headers, excludedHeaderNames, tagPrefix, limit =
|
|
|
140
140
|
return result
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
function getCollectedHeaders (req, res, shouldCollectEventHeaders) {
|
|
143
|
+
function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedResponseHeaders = {}) {
|
|
144
144
|
// Mandatory
|
|
145
145
|
const mandatoryCollectedHeaders = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
|
|
146
146
|
|
|
147
147
|
// Basic collection
|
|
148
148
|
if (!shouldCollectEventHeaders) return mandatoryCollectedHeaders
|
|
149
149
|
|
|
150
|
-
const responseHeaders =
|
|
150
|
+
const responseHeaders = Object.keys(storedResponseHeaders).length === 0
|
|
151
|
+
? res.getHeaders()
|
|
152
|
+
: { ...storedResponseHeaders, ...res.getHeaders() }
|
|
151
153
|
|
|
152
154
|
const requestEventCollectedHeaders = filterHeaders(req.headers, EVENT_HEADERS_MAP)
|
|
153
155
|
const responseEventCollectedHeaders = filterHeaders(responseHeaders, RESPONSE_HEADERS_MAP)
|
|
@@ -399,7 +401,7 @@ function reportDerivatives (derivatives) {
|
|
|
399
401
|
rootSpan.addTags(tags)
|
|
400
402
|
}
|
|
401
403
|
|
|
402
|
-
function finishRequest (req, res) {
|
|
404
|
+
function finishRequest (req, res, storedResponseHeaders) {
|
|
403
405
|
const rootSpan = web.root(req)
|
|
404
406
|
if (!rootSpan) return
|
|
405
407
|
|
|
@@ -453,7 +455,7 @@ function finishRequest (req, res) {
|
|
|
453
455
|
|
|
454
456
|
const tags = rootSpan.context()._tags
|
|
455
457
|
|
|
456
|
-
const newTags = getCollectedHeaders(req, res, shouldCollectEventHeaders(tags))
|
|
458
|
+
const newTags = getCollectedHeaders(req, res, shouldCollectEventHeaders(tags), storedResponseHeaders)
|
|
457
459
|
|
|
458
460
|
if (tags['appsec.event'] === 'true' && typeof req.route?.path === 'string') {
|
|
459
461
|
newTags['http.endpoint'] = req.route.path
|
|
@@ -13,7 +13,7 @@ function getBaggageItem (key) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function getAllBaggageItems () {
|
|
16
|
-
return storage('baggage').getStore()
|
|
16
|
+
return storage('baggage').getStore() ?? {}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function removeBaggageItem (keyToRemove) {
|
|
@@ -23,7 +23,7 @@ function removeBaggageItem (keyToRemove) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function removeAllBaggageItems () {
|
|
26
|
-
storage('baggage').enterWith(
|
|
26
|
+
storage('baggage').enterWith()
|
|
27
27
|
return storage('baggage').getStore()
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -30,7 +30,7 @@ const probeIdToBreakpointId = new Map()
|
|
|
30
30
|
session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint], callFrames } }) => {
|
|
31
31
|
const probe = breakpointIdToProbe.get(hitBreakpoint)
|
|
32
32
|
if (!probe) {
|
|
33
|
-
log.warn(
|
|
33
|
+
log.warn('No probe found for breakpoint', hitBreakpoint)
|
|
34
34
|
return session.post('Debugger.resume')
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -95,13 +95,13 @@ async function addBreakpoint (probe) {
|
|
|
95
95
|
|
|
96
96
|
const script = findScriptFromPartialPath(file)
|
|
97
97
|
if (!script) {
|
|
98
|
-
log.error(
|
|
98
|
+
log.error('No loaded script found for', file)
|
|
99
99
|
throw new Error(`No loaded script found for ${file}`)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const { url, scriptId, sourceMapURL, source } = script
|
|
103
103
|
|
|
104
|
-
log.warn(
|
|
104
|
+
log.warn('Adding breakpoint at %s:%s', url, line)
|
|
105
105
|
|
|
106
106
|
let lineNumber = line
|
|
107
107
|
let columnNumber = 0
|
|
@@ -96,7 +96,7 @@ function getKnownTests ({
|
|
|
96
96
|
incrementCountMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, numTests)
|
|
97
97
|
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, res.length)
|
|
98
98
|
|
|
99
|
-
log.debug(
|
|
99
|
+
log.debug('Number of received known tests:', numTests)
|
|
100
100
|
|
|
101
101
|
done(null, knownTests)
|
|
102
102
|
} catch (err) {
|
|
@@ -48,7 +48,7 @@ class Writer extends BaseWriter {
|
|
|
48
48
|
const startRequestTime = Date.now()
|
|
49
49
|
|
|
50
50
|
incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'test_cycle' })
|
|
51
|
-
distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'test_cycle' }, data
|
|
51
|
+
distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'test_cycle' }, Buffer.byteLength(data))
|
|
52
52
|
|
|
53
53
|
request(data, options, (err, res, statusCode) => {
|
|
54
54
|
distributionMetric(
|
|
@@ -69,7 +69,7 @@ class Writer extends BaseWriter {
|
|
|
69
69
|
done()
|
|
70
70
|
return
|
|
71
71
|
}
|
|
72
|
-
log.debug(
|
|
72
|
+
log.debug('Response from the intake:', res)
|
|
73
73
|
done()
|
|
74
74
|
})
|
|
75
75
|
}
|
|
@@ -105,9 +105,9 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, ev
|
|
|
105
105
|
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType: 'network' })
|
|
106
106
|
return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
|
|
107
107
|
}
|
|
108
|
-
log.debug(
|
|
108
|
+
log.debug('There are %s commits to exclude.', alreadySeenCommits.length)
|
|
109
109
|
const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
|
|
110
|
-
log.debug(
|
|
110
|
+
log.debug('There are %s commits to include.', commitsToInclude.length)
|
|
111
111
|
|
|
112
112
|
if (!commitsToInclude.length) {
|
|
113
113
|
return callback(null, [])
|
|
@@ -195,11 +195,11 @@ function generateAndUploadPackFiles ({
|
|
|
195
195
|
repositoryUrl,
|
|
196
196
|
headCommit
|
|
197
197
|
}, callback) {
|
|
198
|
-
log.debug(
|
|
198
|
+
log.debug('There are %s commits to upload', commitsToUpload.length)
|
|
199
199
|
|
|
200
200
|
const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
|
|
201
201
|
|
|
202
|
-
log.debug(
|
|
202
|
+
log.debug('Uploading %s packfiles.', packFilesToUpload.length)
|
|
203
203
|
|
|
204
204
|
if (!packFilesToUpload.length) {
|
|
205
205
|
return callback(new Error('Failed to generate packfiles'))
|
|
@@ -253,14 +253,14 @@ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryU
|
|
|
253
253
|
repositoryUrl = getRepositoryUrl()
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
log.debug(
|
|
256
|
+
log.debug('Uploading git history for repository', repositoryUrl)
|
|
257
257
|
|
|
258
258
|
if (!repositoryUrl) {
|
|
259
259
|
return callback(new Error('Repository URL is empty'))
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
let latestCommits = getLatestCommits()
|
|
263
|
-
log.debug(
|
|
263
|
+
log.debug('There were %s commits since last month.', latestCommits.length)
|
|
264
264
|
|
|
265
265
|
const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
|
|
266
266
|
if (err) {
|
|
@@ -86,10 +86,9 @@ function getSkippableSuites ({
|
|
|
86
86
|
incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS, { statusCode })
|
|
87
87
|
done(err)
|
|
88
88
|
} else {
|
|
89
|
-
let skippableSuites = []
|
|
90
89
|
try {
|
|
91
90
|
const parsedResponse = JSON.parse(res)
|
|
92
|
-
skippableSuites = parsedResponse
|
|
91
|
+
const skippableSuites = parsedResponse
|
|
93
92
|
.data
|
|
94
93
|
.filter(({ type }) => type === testLevel)
|
|
95
94
|
.map(({ attributes: { suite, name } }) => {
|
|
@@ -107,7 +106,7 @@ function getSkippableSuites ({
|
|
|
107
106
|
skippableSuites.length
|
|
108
107
|
)
|
|
109
108
|
distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
|
|
110
|
-
log.debug(
|
|
109
|
+
log.debug('Number of received skippable %ss:', testLevel, skippableSuites.length)
|
|
111
110
|
done(null, skippableSuites, correlationId)
|
|
112
111
|
} catch (err) {
|
|
113
112
|
done(err)
|
|
@@ -122,15 +122,15 @@ function getLibraryConfiguration ({
|
|
|
122
122
|
isImpactedTestsEnabled
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
log.debug(
|
|
125
|
+
log.debug('Remote settings: %j', settings)
|
|
126
126
|
|
|
127
127
|
if (getEnvironmentVariable('DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE')) {
|
|
128
128
|
settings.isCodeCoverageEnabled = true
|
|
129
|
-
log.debug(
|
|
129
|
+
log.debug('Dangerously set code coverage to true')
|
|
130
130
|
}
|
|
131
131
|
if (getEnvironmentVariable('DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING')) {
|
|
132
132
|
settings.isSuitesSkippingEnabled = true
|
|
133
|
-
log.debug(
|
|
133
|
+
log.debug('Dangerously set test skipping to true')
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, settings)
|