dd-trace 5.34.0 → 5.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +3 -7
- package/package.json +4 -4
- package/packages/datadog-core/index.js +1 -1
- package/packages/datadog-core/src/storage.js +76 -31
- package/packages/datadog-instrumentations/src/aws-sdk.js +16 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +3 -7
- package/packages/datadog-instrumentations/src/passport.js +45 -0
- package/packages/datadog-plugin-aerospike/src/index.js +1 -1
- package/packages/datadog-plugin-apollo/src/gateway/fetch.js +1 -1
- package/packages/datadog-plugin-apollo/src/gateway/index.js +1 -1
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +33 -6
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -2
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +11 -11
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -1
- package/packages/datadog-plugin-cypress/src/support.js +36 -29
- package/packages/datadog-plugin-dd-trace-api/src/index.js +120 -0
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-hapi/src/index.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +3 -3
- package/packages/datadog-plugin-http2/src/server.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -11
- package/packages/datadog-plugin-langchain/src/tracing.js +1 -1
- package/packages/datadog-plugin-mariadb/src/index.js +3 -3
- package/packages/datadog-plugin-mocha/src/index.js +13 -13
- package/packages/datadog-plugin-next/src/index.js +4 -4
- package/packages/datadog-plugin-openai/src/tracing.js +1 -1
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +2 -2
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +11 -11
- package/packages/dd-trace/src/appsec/blocking.js +4 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/graphql.js +6 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +6 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/constants.js +6 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +8 -8
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +65 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +14 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +80 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +20 -3
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +5 -5
- package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +2 -2
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +3 -3
- package/packages/dd-trace/src/appsec/sdk/set_user.js +9 -0
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -1
- package/packages/dd-trace/src/appsec/telemetry.js +10 -0
- package/packages/dd-trace/src/appsec/user_tracking.js +32 -6
- package/packages/dd-trace/src/appsec/waf/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +17 -10
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +3 -3
- package/packages/dd-trace/src/config.js +2 -0
- package/packages/dd-trace/src/data_streams_context.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/state.js +8 -3
- package/packages/dd-trace/src/exporters/common/agents.js +1 -1
- package/packages/dd-trace/src/exporters/common/request.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +49 -4
- package/packages/dd-trace/src/log/writer.js +3 -3
- package/packages/dd-trace/src/noop/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +5 -4
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/plugin_manager.js +6 -1
- package/packages/dd-trace/src/plugins/apollo.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +35 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +8 -8
- package/packages/dd-trace/src/plugins/tracing.js +3 -3
- package/packages/dd-trace/src/plugins/util/git.js +3 -3
- package/packages/dd-trace/src/plugins/util/test.js +5 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/scope.js +5 -5
- package/packages/dd-trace/src/tracer.js +0 -14
|
@@ -115,7 +115,10 @@ function block (req, res, rootSpan, abortController, actionParameters = defaultB
|
|
|
115
115
|
res.removeHeader(headerName)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
res.writeHead(statusCode, headers)
|
|
118
|
+
res.writeHead(statusCode, headers)
|
|
119
|
+
|
|
120
|
+
// this is needed to call the original end method, since express-session replaces it
|
|
121
|
+
res.constructor.prototype.end.call(res, body)
|
|
119
122
|
|
|
120
123
|
responseBlockedSet.add(res)
|
|
121
124
|
|
|
@@ -14,6 +14,7 @@ module.exports = {
|
|
|
14
14
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
15
15
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
16
16
|
passportVerify: dc.channel('datadog:passport:verify:finish'),
|
|
17
|
+
passportUser: dc.channel('datadog:passport:deserializeUser:finish'),
|
|
17
18
|
queryParser: dc.channel('datadog:query:read:finish'),
|
|
18
19
|
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
19
20
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
@@ -30,7 +30,7 @@ function disable () {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function onGraphqlStartResolve ({ context, resolverInfo }) {
|
|
33
|
-
const req = storage.getStore()?.req
|
|
33
|
+
const req = storage('legacy').getStore()?.req
|
|
34
34
|
|
|
35
35
|
if (!req) return
|
|
36
36
|
|
|
@@ -49,7 +49,7 @@ function onGraphqlStartResolve ({ context, resolverInfo }) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function enterInApolloMiddleware (data) {
|
|
52
|
-
const req = data?.req || storage.getStore()?.req
|
|
52
|
+
const req = data?.req || storage('legacy').getStore()?.req
|
|
53
53
|
if (!req) return
|
|
54
54
|
|
|
55
55
|
graphqlRequestData.set(req, {
|
|
@@ -59,7 +59,7 @@ function enterInApolloMiddleware (data) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function enterInApolloServerCoreRequest () {
|
|
62
|
-
const req = storage.getStore()?.req
|
|
62
|
+
const req = storage('legacy').getStore()?.req
|
|
63
63
|
if (!req) return
|
|
64
64
|
|
|
65
65
|
graphqlRequestData.set(req, {
|
|
@@ -69,13 +69,13 @@ function enterInApolloServerCoreRequest () {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function exitFromApolloMiddleware (data) {
|
|
72
|
-
const req = data?.req || storage.getStore()?.req
|
|
72
|
+
const req = data?.req || storage('legacy').getStore()?.req
|
|
73
73
|
const requestData = graphqlRequestData.get(req)
|
|
74
74
|
if (requestData) requestData.inApolloMiddleware = false
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function enterInApolloRequest () {
|
|
78
|
-
const req = storage.getStore()?.req
|
|
78
|
+
const req = storage('legacy').getStore()?.req
|
|
79
79
|
|
|
80
80
|
const requestData = graphqlRequestData.get(req)
|
|
81
81
|
if (requestData?.inApolloMiddleware) {
|
|
@@ -85,7 +85,7 @@ function enterInApolloRequest () {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function beforeWriteApolloGraphqlResponse ({ abortController, abortData }) {
|
|
88
|
-
const req = storage.getStore()?.req
|
|
88
|
+
const req = storage('legacy').getStore()?.req
|
|
89
89
|
if (!req) return
|
|
90
90
|
|
|
91
91
|
const requestData = graphqlRequestData.get(req)
|
|
@@ -15,7 +15,7 @@ class CodeInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
15
15
|
onConfigure () {
|
|
16
16
|
this.addSub('datadog:eval:call', ({ script }) => {
|
|
17
17
|
if (!this.evalInstrumentedInc) {
|
|
18
|
-
const store = storage.getStore()
|
|
18
|
+
const store = storage('legacy').getStore()
|
|
19
19
|
const iastContext = getIastContext(store)
|
|
20
20
|
const tags = INSTRUMENTED_SINK.formatTags(CODE_INJECTION)
|
|
21
21
|
|
|
@@ -38,7 +38,7 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
38
38
|
this.configureSanitizers()
|
|
39
39
|
|
|
40
40
|
const onStart = ({ filters }) => {
|
|
41
|
-
const store = storage.getStore()
|
|
41
|
+
const store = storage('legacy').getStore()
|
|
42
42
|
if (store && !store.nosqlAnalyzed && filters?.length) {
|
|
43
43
|
filters.forEach(filter => {
|
|
44
44
|
this.analyze({ filter }, store)
|
|
@@ -51,14 +51,14 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
51
51
|
const onStartAndEnterWithStore = (message) => {
|
|
52
52
|
const store = onStart(message || {})
|
|
53
53
|
if (store) {
|
|
54
|
-
storage.enterWith({ ...store, nosqlAnalyzed: true, nosqlParentStore: store })
|
|
54
|
+
storage('legacy').enterWith({ ...store, nosqlAnalyzed: true, nosqlParentStore: store })
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const onFinish = () => {
|
|
59
|
-
const store = storage.getStore()
|
|
59
|
+
const store = storage('legacy').getStore()
|
|
60
60
|
if (store?.nosqlParentStore) {
|
|
61
|
-
storage.enterWith(store.nosqlParentStore)
|
|
61
|
+
storage('legacy').enterWith(store.nosqlParentStore)
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -74,7 +74,7 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
74
74
|
|
|
75
75
|
configureSanitizers () {
|
|
76
76
|
this.addNotSinkSub('datadog:express-mongo-sanitize:filter:finish', ({ sanitizedProperties, req }) => {
|
|
77
|
-
const store = storage.getStore()
|
|
77
|
+
const store = storage('legacy').getStore()
|
|
78
78
|
const iastContext = getIastContext(store)
|
|
79
79
|
|
|
80
80
|
if (iastContext) { // do nothing if we are not in an iast request
|
|
@@ -100,7 +100,7 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
|
|
|
100
100
|
})
|
|
101
101
|
|
|
102
102
|
this.addNotSinkSub('datadog:express-mongo-sanitize:sanitize:finish', ({ sanitizedObject }) => {
|
|
103
|
-
const store = storage.getStore()
|
|
103
|
+
const store = storage('legacy').getStore()
|
|
104
104
|
const iastContext = getIastContext(store)
|
|
105
105
|
|
|
106
106
|
if (iastContext) { // do nothing if we are not in an iast request
|
|
@@ -29,7 +29,7 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
29
29
|
|
|
30
30
|
onConfigure () {
|
|
31
31
|
this.addSub('apm:fs:operation:start', (obj) => {
|
|
32
|
-
const store = storage.getStore()
|
|
32
|
+
const store = storage('legacy').getStore()
|
|
33
33
|
const outOfReqOrChild = !store?.fs?.root
|
|
34
34
|
|
|
35
35
|
// we could filter out all the nested fs.operations based on store.fs.root
|
|
@@ -84,7 +84,7 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
analyze (value) {
|
|
87
|
-
const iastContext = getIastContext(storage.getStore())
|
|
87
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
88
88
|
if (!iastContext) {
|
|
89
89
|
return
|
|
90
90
|
}
|
|
@@ -38,18 +38,18 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
getStoreAndAnalyze (query, dialect) {
|
|
41
|
-
const parentStore = storage.getStore()
|
|
41
|
+
const parentStore = storage('legacy').getStore()
|
|
42
42
|
if (parentStore) {
|
|
43
43
|
this.analyze(query, parentStore, dialect)
|
|
44
44
|
|
|
45
|
-
storage.enterWith({ ...parentStore, sqlAnalyzed: true, sqlParentStore: parentStore })
|
|
45
|
+
storage('legacy').enterWith({ ...parentStore, sqlAnalyzed: true, sqlParentStore: parentStore })
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
returnToParentStore () {
|
|
50
|
-
const store = storage.getStore()
|
|
50
|
+
const store = storage('legacy').getStore()
|
|
51
51
|
if (store && store.sqlParentStore) {
|
|
52
|
-
storage.enterWith(store.sqlParentStore)
|
|
52
|
+
storage('legacy').enterWith(store.sqlParentStore)
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -59,7 +59,7 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
analyze (value, store, dialect) {
|
|
62
|
-
store = store || storage.getStore()
|
|
62
|
+
store = store || storage('legacy').getStore()
|
|
63
63
|
if (!(store && store.sqlAnalyzed)) {
|
|
64
64
|
super.analyze(value, store, dialect)
|
|
65
65
|
}
|
|
@@ -91,7 +91,7 @@ class Analyzer extends SinkIastPlugin {
|
|
|
91
91
|
return store && !iastContext
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
analyze (value, store = storage.getStore(), meta) {
|
|
94
|
+
analyze (value, store = storage('legacy').getStore(), meta) {
|
|
95
95
|
const iastContext = getIastContext(store)
|
|
96
96
|
if (this._isInvalidContext(store, iastContext)) return
|
|
97
97
|
|
|
@@ -99,7 +99,7 @@ class Analyzer extends SinkIastPlugin {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
analyzeAll (...values) {
|
|
102
|
-
const store = storage.getStore()
|
|
102
|
+
const store = storage('legacy').getStore()
|
|
103
103
|
const iastContext = getIastContext(store)
|
|
104
104
|
if (this._isInvalidContext(store, iastContext)) return
|
|
105
105
|
|
|
@@ -22,7 +22,8 @@ const EXCLUDED_LOCATIONS = getNodeModulesPaths(
|
|
|
22
22
|
'sqreen/lib/package-reader/index.js',
|
|
23
23
|
'ws/lib/websocket-server.js',
|
|
24
24
|
'google-gax/build/src/grpc.js',
|
|
25
|
-
'cookie-signature/index.js'
|
|
25
|
+
'cookie-signature/index.js',
|
|
26
|
+
'express-session/index.js'
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
const EXCLUDED_PATHS_FROM_STACK = [
|
|
@@ -48,7 +48,7 @@ class IastContextPlugin extends IastPlugin {
|
|
|
48
48
|
let isRequestAcquired = false
|
|
49
49
|
let iastContext
|
|
50
50
|
|
|
51
|
-
const store = storage.getStore()
|
|
51
|
+
const store = storage('legacy').getStore()
|
|
52
52
|
if (store) {
|
|
53
53
|
const topContext = this.getTopContext()
|
|
54
54
|
const rootSpan = this.getRootSpan(store)
|
|
@@ -70,7 +70,7 @@ class IastContextPlugin extends IastPlugin {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
finishContext () {
|
|
73
|
-
const store = storage.getStore()
|
|
73
|
+
const store = storage('legacy').getStore()
|
|
74
74
|
if (store) {
|
|
75
75
|
const topContext = this.getTopContext()
|
|
76
76
|
const iastContext = iastContextFunctions.getIastContext(store, topContext)
|
|
@@ -62,12 +62,12 @@ class IastPlugin extends Plugin {
|
|
|
62
62
|
|
|
63
63
|
_getTelemetryHandler (iastSub) {
|
|
64
64
|
return () => {
|
|
65
|
-
const iastContext = getIastContext(storage.getStore())
|
|
65
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
66
66
|
iastSub.increaseExecuted(iastContext)
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
_execHandlerAndIncMetric ({ handler, metric, tags, iastContext = getIastContext(storage.getStore()) }) {
|
|
70
|
+
_execHandlerAndIncMetric ({ handler, metric, tags, iastContext = getIastContext(storage('legacy').getStore()) }) {
|
|
71
71
|
try {
|
|
72
72
|
const result = handler()
|
|
73
73
|
if (iastTelemetry.isEnabled()) {
|
|
@@ -57,7 +57,7 @@ function disable () {
|
|
|
57
57
|
|
|
58
58
|
function onIncomingHttpRequestStart (data) {
|
|
59
59
|
if (data?.req) {
|
|
60
|
-
const store = storage.getStore()
|
|
60
|
+
const store = storage('legacy').getStore()
|
|
61
61
|
if (store) {
|
|
62
62
|
const topContext = web.getContext(data.req)
|
|
63
63
|
if (topContext) {
|
|
@@ -82,7 +82,7 @@ function onIncomingHttpRequestStart (data) {
|
|
|
82
82
|
|
|
83
83
|
function onIncomingHttpRequestEnd (data) {
|
|
84
84
|
if (data?.req) {
|
|
85
|
-
const store = storage.getStore()
|
|
85
|
+
const store = storage('legacy').getStore()
|
|
86
86
|
const topContext = web.getContext(data.req)
|
|
87
87
|
const iastContext = iastContextFunctions.getIastContext(store, topContext)
|
|
88
88
|
if (iastContext?.rootSpan) {
|
|
@@ -39,7 +39,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
39
39
|
|
|
40
40
|
onConfigure () {
|
|
41
41
|
const onRequestBody = ({ req }) => {
|
|
42
|
-
const iastContext = getIastContext(storage.getStore())
|
|
42
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
43
43
|
if (iastContext && iastContext.body !== req.body) {
|
|
44
44
|
this._taintTrackingHandler(HTTP_REQUEST_BODY, req, 'body', iastContext)
|
|
45
45
|
iastContext.body = req.body
|
|
@@ -70,7 +70,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
70
70
|
{ channelName: 'apm:express:middleware:next', tag: HTTP_REQUEST_BODY },
|
|
71
71
|
({ req }) => {
|
|
72
72
|
if (req && req.body !== null && typeof req.body === 'object') {
|
|
73
|
-
const iastContext = getIastContext(storage.getStore())
|
|
73
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
74
74
|
if (iastContext && iastContext.body !== req.body) {
|
|
75
75
|
this._taintTrackingHandler(HTTP_REQUEST_BODY, req, 'body', iastContext)
|
|
76
76
|
iastContext.body = req.body
|
|
@@ -115,7 +115,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
115
115
|
this.addSub(
|
|
116
116
|
{ channelName: 'apm:graphql:resolve:start', tag: HTTP_REQUEST_BODY },
|
|
117
117
|
(data) => {
|
|
118
|
-
const iastContext = getIastContext(storage.getStore())
|
|
118
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
119
119
|
const source = data.context?.source
|
|
120
120
|
const ranges = source && getRanges(iastContext, source)
|
|
121
121
|
if (ranges?.length) {
|
|
@@ -128,7 +128,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
128
128
|
this.addSub(
|
|
129
129
|
{ channelName: 'datadog:url:parse:finish' },
|
|
130
130
|
({ input, base, parsed, isURL }) => {
|
|
131
|
-
const iastContext = getIastContext(storage.getStore())
|
|
131
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
132
132
|
let ranges
|
|
133
133
|
|
|
134
134
|
if (base) {
|
|
@@ -157,7 +157,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
157
157
|
const origRange = this._taintedURLs.get(context.urlObject)
|
|
158
158
|
if (!origRange) return
|
|
159
159
|
|
|
160
|
-
const iastContext = getIastContext(storage.getStore())
|
|
160
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
161
161
|
if (!iastContext) return
|
|
162
162
|
|
|
163
163
|
context.result =
|
|
@@ -168,7 +168,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
168
168
|
this.addInstrumentedSource('http', [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME])
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
_taintTrackingHandler (type, target, property, iastContext = getIastContext(storage.getStore())) {
|
|
171
|
+
_taintTrackingHandler (type, target, property, iastContext = getIastContext(storage('legacy').getStore())) {
|
|
172
172
|
if (!property) {
|
|
173
173
|
taintObject(iastContext, target, type)
|
|
174
174
|
} else if (target[property]) {
|
|
@@ -177,7 +177,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
_cookiesTaintTrackingHandler (target) {
|
|
180
|
-
const iastContext = getIastContext(storage.getStore())
|
|
180
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
181
181
|
// Prevent tainting cookie names since it leads to taint literal string with same value.
|
|
182
182
|
taintObject(iastContext, target, HTTP_REQUEST_COOKIE_VALUE)
|
|
183
183
|
}
|
|
@@ -206,7 +206,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
206
206
|
this.taintUrl(req, iastContext)
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
_taintDatabaseResult (result, dbOrigin, iastContext = getIastContext(storage.getStore()), name) {
|
|
209
|
+
_taintDatabaseResult (result, dbOrigin, iastContext = getIastContext(storage('legacy').getStore()), name) {
|
|
210
210
|
if (!iastContext) return result
|
|
211
211
|
|
|
212
212
|
if (this._rowsToTaint === 0) return result
|
|
@@ -22,7 +22,7 @@ class KafkaConsumerIastPlugin extends SourceIastPlugin {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
taintKafkaMessage (message) {
|
|
25
|
-
const iastContext = getIastContext(storage.getStore())
|
|
25
|
+
const iastContext = getIastContext(storage('legacy').getStore())
|
|
26
26
|
|
|
27
27
|
if (iastContext && message) {
|
|
28
28
|
const { key, value } = message
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { URL } from 'url'
|
|
5
|
+
import { getName } from '../telemetry/verbosity.js'
|
|
6
|
+
import { isNotLibraryFile, isPrivateModule } from './filter.js'
|
|
7
|
+
import constants from './constants.js'
|
|
8
|
+
|
|
9
|
+
const currentUrl = new URL(import.meta.url)
|
|
10
|
+
const ddTraceDir = path.join(currentUrl.pathname, '..', '..', '..', '..', '..', '..')
|
|
11
|
+
|
|
12
|
+
let port, rewriter
|
|
13
|
+
|
|
14
|
+
export async function initialize (data) {
|
|
15
|
+
if (rewriter) return Promise.reject(new Error('ALREADY INITIALIZED'))
|
|
16
|
+
|
|
17
|
+
const { csiMethods, telemetryVerbosity, chainSourceMap } = data
|
|
18
|
+
port = data.port
|
|
19
|
+
|
|
20
|
+
const iastRewriter = await import('@datadog/native-iast-rewriter')
|
|
21
|
+
|
|
22
|
+
const { NonCacheRewriter } = iastRewriter.default
|
|
23
|
+
|
|
24
|
+
rewriter = new NonCacheRewriter({
|
|
25
|
+
csiMethods,
|
|
26
|
+
telemetryVerbosity: getName(telemetryVerbosity),
|
|
27
|
+
chainSourceMap
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function load (url, context, nextLoad) {
|
|
32
|
+
const result = await nextLoad(url, context)
|
|
33
|
+
|
|
34
|
+
if (!port) return result
|
|
35
|
+
if (!result.source) return result
|
|
36
|
+
if (url.includes(ddTraceDir) || url.includes('iitm=true')) return result
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (isPrivateModule(url) && isNotLibraryFile(url)) {
|
|
40
|
+
const rewritten = rewriter.rewrite(result.source.toString(), url)
|
|
41
|
+
|
|
42
|
+
if (rewritten?.content) {
|
|
43
|
+
result.source = rewritten.content || result.source
|
|
44
|
+
const data = { url, rewritten }
|
|
45
|
+
port.postMessage({ type: constants.REWRITTEN_MESSAGE, data })
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
const newErrObject = {
|
|
50
|
+
message: e.message,
|
|
51
|
+
stack: e.stack
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = {
|
|
55
|
+
level: 'error',
|
|
56
|
+
messages: ['[ASM] Error rewriting file %s', url, newErrObject]
|
|
57
|
+
}
|
|
58
|
+
port.postMessage({
|
|
59
|
+
type: constants.LOG_MESSAGE,
|
|
60
|
+
data
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
}
|
|
@@ -12,10 +12,7 @@ const telemetryRewriter = {
|
|
|
12
12
|
information (content, filename, rewriter) {
|
|
13
13
|
const response = this.off(content, filename, rewriter)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
if (metrics && metrics.instrumentedPropagation) {
|
|
17
|
-
INSTRUMENTED_PROPAGATION.inc(undefined, metrics.instrumentedPropagation)
|
|
18
|
-
}
|
|
15
|
+
incrementTelemetry(response.metrics)
|
|
19
16
|
|
|
20
17
|
return response
|
|
21
18
|
}
|
|
@@ -30,4 +27,16 @@ function getRewriteFunction (rewriter) {
|
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
function incrementTelemetry (metrics) {
|
|
31
|
+
if (metrics?.instrumentedPropagation) {
|
|
32
|
+
INSTRUMENTED_PROPAGATION.inc(undefined, metrics.instrumentedPropagation)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function incrementTelemetryIfNeeded (metrics) {
|
|
37
|
+
if (iastTelemetry.verbosity !== Verbosity.OFF) {
|
|
38
|
+
incrementTelemetry(metrics)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { getRewriteFunction, incrementTelemetryIfNeeded }
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const Module = require('module')
|
|
4
|
+
const { pathToFileURL } = require('url')
|
|
5
|
+
const { MessageChannel } = require('worker_threads')
|
|
4
6
|
const shimmer = require('../../../../../datadog-shimmer')
|
|
5
7
|
const { isPrivateModule, isNotLibraryFile } = require('./filter')
|
|
6
8
|
const { csiMethods } = require('./csi-methods')
|
|
7
9
|
const { getName } = require('../telemetry/verbosity')
|
|
8
|
-
const { getRewriteFunction } = require('./rewriter-telemetry')
|
|
10
|
+
const { getRewriteFunction, incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
|
|
9
11
|
const dc = require('dc-polyfill')
|
|
10
12
|
const log = require('../../../log')
|
|
13
|
+
const { isMainThread } = require('worker_threads')
|
|
14
|
+
const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
|
|
11
15
|
|
|
12
16
|
const hardcodedSecretCh = dc.channel('datadog:secrets:result')
|
|
13
17
|
let rewriter
|
|
14
|
-
let getPrepareStackTrace
|
|
18
|
+
let getPrepareStackTrace, cacheRewrittenSourceMap
|
|
15
19
|
let kSymbolPrepareStackTrace
|
|
20
|
+
let esmRewriterEnabled = false
|
|
16
21
|
|
|
17
22
|
let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
|
|
18
23
|
return { path, line, column }
|
|
@@ -46,6 +51,7 @@ function getRewriter (telemetryVerbosity) {
|
|
|
46
51
|
const Rewriter = iastRewriter.Rewriter
|
|
47
52
|
getPrepareStackTrace = iastRewriter.getPrepareStackTrace
|
|
48
53
|
kSymbolPrepareStackTrace = iastRewriter.kSymbolPrepareStackTrace
|
|
54
|
+
cacheRewrittenSourceMap = iastRewriter.cacheRewrittenSourceMap
|
|
49
55
|
|
|
50
56
|
const chainSourceMap = isFlagPresent('--enable-source-maps')
|
|
51
57
|
const getOriginalPathAndLineFromSourceMap = iastRewriter.getOriginalPathAndLineFromSourceMap
|
|
@@ -104,6 +110,24 @@ function getCompileMethodFn (compileMethod) {
|
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
function esmRewritePostProcess (rewritten, filename) {
|
|
114
|
+
const { literalsResult, metrics } = rewritten
|
|
115
|
+
|
|
116
|
+
if (metrics?.status === 'modified') {
|
|
117
|
+
if (filename.startsWith('file://')) {
|
|
118
|
+
filename = filename.substring(7)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cacheRewrittenSourceMap(filename, rewritten.content)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
incrementTelemetryIfNeeded(metrics)
|
|
125
|
+
|
|
126
|
+
if (literalsResult && hardcodedSecretCh.hasSubscribers) {
|
|
127
|
+
hardcodedSecretCh.publish(literalsResult)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
107
131
|
function enableRewriter (telemetryVerbosity) {
|
|
108
132
|
try {
|
|
109
133
|
const rewriter = getRewriter(telemetryVerbosity)
|
|
@@ -114,11 +138,65 @@ function enableRewriter (telemetryVerbosity) {
|
|
|
114
138
|
}
|
|
115
139
|
shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
|
|
116
140
|
}
|
|
141
|
+
|
|
142
|
+
enableEsmRewriter(telemetryVerbosity)
|
|
117
143
|
} catch (e) {
|
|
118
144
|
log.error('[ASM] Error enabling TaintTracking Rewriter', e)
|
|
119
145
|
}
|
|
120
146
|
}
|
|
121
147
|
|
|
148
|
+
function isEsmConfigured () {
|
|
149
|
+
const hasLoaderArg = isFlagPresent('--loader') || isFlagPresent('--experimental-loader')
|
|
150
|
+
if (hasLoaderArg) return true
|
|
151
|
+
|
|
152
|
+
const initializeLoaded = Object.keys(require.cache).find(file => file.includes('import-in-the-middle/hook.js'))
|
|
153
|
+
return !!initializeLoaded
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function enableEsmRewriter (telemetryVerbosity) {
|
|
157
|
+
if (isMainThread && Module.register && !esmRewriterEnabled && isEsmConfigured()) {
|
|
158
|
+
esmRewriterEnabled = true
|
|
159
|
+
|
|
160
|
+
const { port1, port2 } = new MessageChannel()
|
|
161
|
+
|
|
162
|
+
port1.on('message', (message) => {
|
|
163
|
+
const { type, data } = message
|
|
164
|
+
switch (type) {
|
|
165
|
+
case LOG_MESSAGE:
|
|
166
|
+
log[data.level]?.(...data.messages)
|
|
167
|
+
break
|
|
168
|
+
|
|
169
|
+
case REWRITTEN_MESSAGE:
|
|
170
|
+
esmRewritePostProcess(data.rewritten, data.url)
|
|
171
|
+
break
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
port1.unref()
|
|
176
|
+
port2.unref()
|
|
177
|
+
|
|
178
|
+
const chainSourceMap = isFlagPresent('--enable-source-maps')
|
|
179
|
+
const data = {
|
|
180
|
+
port: port2,
|
|
181
|
+
csiMethods,
|
|
182
|
+
telemetryVerbosity,
|
|
183
|
+
chainSourceMap
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
Module.register('./rewriter-esm.mjs', {
|
|
188
|
+
parentURL: pathToFileURL(__filename),
|
|
189
|
+
transferList: [port2],
|
|
190
|
+
data
|
|
191
|
+
})
|
|
192
|
+
} catch (e) {
|
|
193
|
+
log.error('[ASM] Error enabling ESM Rewriter', e)
|
|
194
|
+
port1.close()
|
|
195
|
+
port2.close()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
122
200
|
function disableRewriter () {
|
|
123
201
|
shimmer.unwrap(Module.prototype, '_compile')
|
|
124
202
|
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
incomingHttpRequestStart,
|
|
11
11
|
incomingHttpRequestEnd,
|
|
12
12
|
passportVerify,
|
|
13
|
+
passportUser,
|
|
13
14
|
queryParser,
|
|
14
15
|
nextBodyParsed,
|
|
15
16
|
nextQueryParsed,
|
|
@@ -67,6 +68,7 @@ function enable (_config) {
|
|
|
67
68
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
68
69
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
69
70
|
passportVerify.subscribe(onPassportVerify) // possible optimization: only subscribe if collection mode is enabled
|
|
71
|
+
passportUser.subscribe(onPassportDeserializeUser)
|
|
70
72
|
queryParser.subscribe(onRequestQueryParsed)
|
|
71
73
|
nextBodyParsed.subscribe(onRequestBodyParsed)
|
|
72
74
|
nextQueryParsed.subscribe(onRequestQueryParsed)
|
|
@@ -89,7 +91,7 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
|
89
91
|
if (body === undefined || body === null) return
|
|
90
92
|
|
|
91
93
|
if (!req) {
|
|
92
|
-
const store = storage.getStore()
|
|
94
|
+
const store = storage('legacy').getStore()
|
|
93
95
|
req = store?.req
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -184,7 +186,7 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
function onPassportVerify ({ framework, login, user, success, abortController }) {
|
|
187
|
-
const store = storage.getStore()
|
|
189
|
+
const store = storage('legacy').getStore()
|
|
188
190
|
const rootSpan = store?.req && web.root(store.req)
|
|
189
191
|
|
|
190
192
|
if (!rootSpan) {
|
|
@@ -197,11 +199,25 @@ function onPassportVerify ({ framework, login, user, success, abortController })
|
|
|
197
199
|
handleResults(results, store.req, store.req.res, rootSpan, abortController)
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
function onPassportDeserializeUser ({ user, abortController }) {
|
|
203
|
+
const store = storage('legacy').getStore()
|
|
204
|
+
const rootSpan = store?.req && web.root(store.req)
|
|
205
|
+
|
|
206
|
+
if (!rootSpan) {
|
|
207
|
+
log.warn('[ASM] No rootSpan found in onPassportDeserializeUser')
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const results = UserTracking.trackUser(user, rootSpan)
|
|
212
|
+
|
|
213
|
+
handleResults(results, store.req, store.req.res, rootSpan, abortController)
|
|
214
|
+
}
|
|
215
|
+
|
|
200
216
|
function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
201
217
|
if (!query || typeof query !== 'object') return
|
|
202
218
|
|
|
203
219
|
if (!req) {
|
|
204
|
-
const store = storage.getStore()
|
|
220
|
+
const store = storage('legacy').getStore()
|
|
205
221
|
req = store?.req
|
|
206
222
|
}
|
|
207
223
|
|
|
@@ -310,6 +326,7 @@ function disable () {
|
|
|
310
326
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
|
|
311
327
|
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
|
|
312
328
|
if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify)
|
|
329
|
+
if (passportUser.hasSubscribers) passportUser.unsubscribe(onPassportDeserializeUser)
|
|
313
330
|
if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed)
|
|
314
331
|
if (nextBodyParsed.hasSubscribers) nextBodyParsed.unsubscribe(onRequestBodyParsed)
|
|
315
332
|
if (nextQueryParsed.hasSubscribers) nextQueryParsed.unsubscribe(onRequestQueryParsed)
|