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.
Files changed (97) hide show
  1. package/index.d.ts +3 -7
  2. package/package.json +4 -4
  3. package/packages/datadog-core/index.js +1 -1
  4. package/packages/datadog-core/src/storage.js +76 -31
  5. package/packages/datadog-instrumentations/src/aws-sdk.js +16 -0
  6. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  7. package/packages/datadog-instrumentations/src/jest.js +3 -7
  8. package/packages/datadog-instrumentations/src/passport.js +45 -0
  9. package/packages/datadog-plugin-aerospike/src/index.js +1 -1
  10. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +1 -1
  11. package/packages/datadog-plugin-apollo/src/gateway/index.js +1 -1
  12. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -1
  13. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
  14. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +33 -6
  15. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -4
  16. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -2
  17. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  18. package/packages/datadog-plugin-couchbase/src/index.js +2 -2
  19. package/packages/datadog-plugin-cucumber/src/index.js +11 -11
  20. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -1
  21. package/packages/datadog-plugin-cypress/src/support.js +36 -29
  22. package/packages/datadog-plugin-dd-trace-api/src/index.js +120 -0
  23. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  24. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  25. package/packages/datadog-plugin-hapi/src/index.js +1 -1
  26. package/packages/datadog-plugin-http/src/client.js +1 -1
  27. package/packages/datadog-plugin-http/src/server.js +1 -1
  28. package/packages/datadog-plugin-http2/src/client.js +3 -3
  29. package/packages/datadog-plugin-http2/src/server.js +1 -1
  30. package/packages/datadog-plugin-jest/src/index.js +6 -11
  31. package/packages/datadog-plugin-langchain/src/tracing.js +1 -1
  32. package/packages/datadog-plugin-mariadb/src/index.js +3 -3
  33. package/packages/datadog-plugin-mocha/src/index.js +13 -13
  34. package/packages/datadog-plugin-next/src/index.js +4 -4
  35. package/packages/datadog-plugin-openai/src/tracing.js +1 -1
  36. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  37. package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
  38. package/packages/datadog-plugin-router/src/index.js +2 -2
  39. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  40. package/packages/datadog-plugin-vitest/src/index.js +11 -11
  41. package/packages/dd-trace/src/appsec/blocking.js +4 -1
  42. package/packages/dd-trace/src/appsec/channels.js +1 -0
  43. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  44. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +6 -6
  46. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +2 -2
  47. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -5
  48. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +2 -2
  49. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
  50. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +2 -2
  51. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +2 -2
  52. package/packages/dd-trace/src/appsec/iast/index.js +2 -2
  53. package/packages/dd-trace/src/appsec/iast/taint-tracking/constants.js +6 -0
  54. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +8 -8
  55. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +1 -1
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +65 -0
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +14 -5
  58. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +80 -2
  59. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
  60. package/packages/dd-trace/src/appsec/index.js +20 -3
  61. package/packages/dd-trace/src/appsec/rasp/command_injection.js +1 -1
  62. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +5 -5
  63. package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
  64. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +2 -2
  65. package/packages/dd-trace/src/appsec/rasp/ssrf.js +1 -1
  66. package/packages/dd-trace/src/appsec/reporter.js +3 -3
  67. package/packages/dd-trace/src/appsec/sdk/set_user.js +9 -0
  68. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -1
  69. package/packages/dd-trace/src/appsec/telemetry.js +10 -0
  70. package/packages/dd-trace/src/appsec/user_tracking.js +32 -6
  71. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +2 -0
  73. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +17 -10
  74. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +3 -3
  75. package/packages/dd-trace/src/config.js +2 -0
  76. package/packages/dd-trace/src/data_streams_context.js +2 -2
  77. package/packages/dd-trace/src/debugger/devtools_client/state.js +8 -3
  78. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  79. package/packages/dd-trace/src/exporters/common/request.js +3 -3
  80. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +49 -4
  81. package/packages/dd-trace/src/log/writer.js +3 -3
  82. package/packages/dd-trace/src/noop/span.js +1 -1
  83. package/packages/dd-trace/src/opentracing/propagation/text_map.js +5 -4
  84. package/packages/dd-trace/src/opentracing/span.js +1 -1
  85. package/packages/dd-trace/src/plugin_manager.js +6 -1
  86. package/packages/dd-trace/src/plugins/apollo.js +1 -1
  87. package/packages/dd-trace/src/plugins/ci_plugin.js +35 -1
  88. package/packages/dd-trace/src/plugins/index.js +1 -0
  89. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  90. package/packages/dd-trace/src/plugins/plugin.js +8 -8
  91. package/packages/dd-trace/src/plugins/tracing.js +3 -3
  92. package/packages/dd-trace/src/plugins/util/git.js +3 -3
  93. package/packages/dd-trace/src/plugins/util/test.js +5 -1
  94. package/packages/dd-trace/src/profiling/exporters/agent.js +3 -3
  95. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  96. package/packages/dd-trace/src/scope.js +5 -5
  97. 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).end(body)
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) {
@@ -0,0 +1,6 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ LOG_MESSAGE: 'LOG',
5
+ REWRITTEN_MESSAGE: 'REWRITTEN'
6
+ }
@@ -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
- const metrics = response.metrics
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
- module.exports = { getRewriteFunction }
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
 
@@ -39,7 +39,7 @@ function getTransactionId (iastContext) {
39
39
  }
40
40
 
41
41
  function getContextDefault () {
42
- const store = storage.getStore()
42
+ const store = storage('legacy').getStore()
43
43
  return iastContextFunctions.getIastContext(store)
44
44
  }
45
45
 
@@ -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)
@@ -27,7 +27,7 @@ function disable () {
27
27
  function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
28
28
  if (!file) return
29
29
 
30
- const store = storage.getStore()
30
+ const store = storage('legacy').getStore()
31
31
  const req = store?.req
32
32
  if (!req) return
33
33