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.
Files changed (150) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +44 -2
  3. package/init.js +4 -1
  4. package/package.json +24 -23
  5. package/packages/datadog-core/src/utils/src/set.js +8 -10
  6. package/packages/datadog-esbuild/index.js +22 -0
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +43 -60
  8. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +12 -12
  9. package/packages/datadog-instrumentations/src/cucumber.js +4 -6
  10. package/packages/datadog-instrumentations/src/elasticsearch.js +16 -19
  11. package/packages/datadog-instrumentations/src/fastify.js +91 -9
  12. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +20 -5
  13. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -2
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/register.js +17 -5
  16. package/packages/datadog-instrumentations/src/ioredis.js +8 -13
  17. package/packages/datadog-instrumentations/src/iovalkey.js +10 -14
  18. package/packages/datadog-instrumentations/src/jest.js +423 -325
  19. package/packages/datadog-instrumentations/src/memcached.js +17 -24
  20. package/packages/datadog-instrumentations/src/mocha/main.js +7 -6
  21. package/packages/datadog-instrumentations/src/moleculer/client.js +9 -10
  22. package/packages/datadog-instrumentations/src/moleculer/server.js +12 -13
  23. package/packages/datadog-instrumentations/src/openai.js +30 -2
  24. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  25. package/packages/datadog-instrumentations/src/prisma.js +116 -0
  26. package/packages/datadog-instrumentations/src/redis.js +32 -43
  27. package/packages/datadog-instrumentations/src/router.js +1 -1
  28. package/packages/datadog-instrumentations/src/sharedb.js +10 -16
  29. package/packages/datadog-instrumentations/src/vitest.js +4 -4
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +6 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +9 -4
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +3 -2
  33. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -1
  34. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
  35. package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
  36. package/packages/datadog-plugin-bunyan/src/index.js +2 -2
  37. package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -2
  38. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +1 -1
  39. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +1 -1
  40. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +1 -1
  41. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +1 -1
  42. package/packages/datadog-plugin-cucumber/src/index.js +4 -2
  43. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -5
  44. package/packages/datadog-plugin-elasticsearch/src/index.js +12 -4
  45. package/packages/datadog-plugin-http/src/client.js +1 -0
  46. package/packages/datadog-plugin-http/src/server.js +2 -1
  47. package/packages/datadog-plugin-http2/src/client.js +1 -0
  48. package/packages/datadog-plugin-http2/src/server.js +1 -0
  49. package/packages/datadog-plugin-jest/src/index.js +4 -3
  50. package/packages/datadog-plugin-memcached/src/index.js +6 -2
  51. package/packages/datadog-plugin-mocha/src/index.js +3 -2
  52. package/packages/datadog-plugin-moleculer/src/client.js +15 -9
  53. package/packages/datadog-plugin-moleculer/src/server.js +9 -5
  54. package/packages/datadog-plugin-next/src/index.js +2 -1
  55. package/packages/datadog-plugin-openai/src/tracing.js +127 -80
  56. package/packages/datadog-plugin-oracledb/src/index.js +2 -1
  57. package/packages/datadog-plugin-pino/src/index.js +2 -2
  58. package/packages/datadog-plugin-prisma/src/client.js +62 -0
  59. package/packages/datadog-plugin-prisma/src/engine.js +81 -0
  60. package/packages/datadog-plugin-prisma/src/index.js +22 -0
  61. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  62. package/packages/datadog-plugin-redis/src/index.js +9 -3
  63. package/packages/datadog-plugin-router/src/index.js +1 -0
  64. package/packages/datadog-plugin-sharedb/src/index.js +13 -5
  65. package/packages/datadog-plugin-winston/src/index.js +2 -2
  66. package/packages/dd-trace/src/appsec/channels.js +26 -21
  67. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  68. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +13 -20
  69. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
  70. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
  71. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
  72. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
  73. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +0 -1
  74. package/packages/dd-trace/src/appsec/index.js +28 -2
  75. package/packages/dd-trace/src/appsec/rasp/utils.js +0 -5
  76. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  77. package/packages/dd-trace/src/baggage.js +2 -2
  78. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -3
  79. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  80. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  81. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  82. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  83. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -6
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -3
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -3
  86. package/packages/dd-trace/src/config.js +334 -312
  87. package/packages/dd-trace/src/constants.js +2 -1
  88. package/packages/dd-trace/src/crashtracking/crashtracker.js +12 -14
  89. package/packages/dd-trace/src/datastreams/context.js +1 -1
  90. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  91. package/packages/dd-trace/src/datastreams/writer.js +3 -3
  92. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -3
  93. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  94. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -3
  95. package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
  96. package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -4
  97. package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
  98. package/packages/dd-trace/src/dogstatsd.js +3 -3
  99. package/packages/dd-trace/src/exporters/agent/index.js +10 -5
  100. package/packages/dd-trace/src/exporters/agent/writer.js +4 -2
  101. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +2 -2
  102. package/packages/dd-trace/src/exporters/log/index.js +1 -1
  103. package/packages/dd-trace/src/exporters/span-stats/writer.js +2 -2
  104. package/packages/dd-trace/src/guardrails/index.js +3 -1
  105. package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
  106. package/packages/dd-trace/src/llmobs/index.js +11 -5
  107. package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
  108. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  109. package/packages/dd-trace/src/llmobs/tagger.js +13 -13
  110. package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
  111. package/packages/dd-trace/src/llmobs/writers/spans.js +2 -2
  112. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  113. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -28
  114. package/packages/dd-trace/src/opentracing/span.js +1 -0
  115. package/packages/dd-trace/src/plugin_manager.js +3 -3
  116. package/packages/dd-trace/src/plugins/cache.js +2 -2
  117. package/packages/dd-trace/src/plugins/ci_plugin.js +11 -7
  118. package/packages/dd-trace/src/plugins/database.js +3 -1
  119. package/packages/dd-trace/src/plugins/index.js +1 -0
  120. package/packages/dd-trace/src/plugins/log_plugin.js +5 -1
  121. package/packages/dd-trace/src/plugins/outbound.js +8 -6
  122. package/packages/dd-trace/src/plugins/structured_log_plugin.js +9 -0
  123. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  124. package/packages/dd-trace/src/plugins/util/ci.js +83 -30
  125. package/packages/dd-trace/src/plugins/util/git.js +1 -0
  126. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +3 -2
  127. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -0
  128. package/packages/dd-trace/src/plugins/util/tags.js +4 -1
  129. package/packages/dd-trace/src/plugins/util/test.js +80 -10
  130. package/packages/dd-trace/src/plugins/util/web.js +1 -0
  131. package/packages/dd-trace/src/profiler.js +0 -2
  132. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -3
  133. package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
  134. package/packages/dd-trace/src/profiling/ssi-heuristics.js +18 -126
  135. package/packages/dd-trace/src/proxy.js +12 -27
  136. package/packages/dd-trace/src/runtime_metrics/index.js +1 -1
  137. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +14 -45
  138. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +2 -2
  139. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +4 -0
  140. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +2 -2
  141. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  142. package/packages/dd-trace/src/supported-configurations.json +13 -3
  143. package/packages/dd-trace/src/telemetry/telemetry.js +11 -4
  144. package/packages/dd-trace/src/tracer.js +11 -0
  145. package/packages/dd-trace/src/tracer_metadata.js +25 -0
  146. package/packages/dd-trace/src/util.js +11 -4
  147. package/version.js +3 -1
  148. package/packages/datadog-core/src/utils/src/get.js +0 -11
  149. package/packages/datadog-core/src/utils/src/has.js +0 -14
  150. 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
- start ({ actionName, request }) {
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
- finish ({ request, res }) {
24
- const span = this.activeSpan
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 LogPlugin = require('../../dd-trace/src/plugins/log_plugin')
3
+ const StructuredLogPlugin = require('../../dd-trace/src/plugins/structured_log_plugin')
4
4
 
5
- class WinstonPlugin extends LogPlugin {
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
- multerParser: dc.channel('datadog:multer:read:finish'),
10
- startGraphqlResolve: dc.channel('datadog:graphql:resolver:start'),
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
- apolloChannel: dc.tracingChannel('datadog:apollo:request'),
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
- passportVerify: dc.channel('datadog:passport:verify:finish'),
17
- passportUser: dc.channel('datadog:passport:deserializeUser:finish'),
18
- expressSession: dc.channel('datadog:express-session:middleware:finish'),
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
- expressProcessParams: dc.channel('datadog:express:process_params:start'),
24
- routerParam: dc.channel('datadog:router:param:start'),
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
- pgQueryStart: dc.channel('apm:pg:query:start'),
31
- pgPoolQueryStart: dc.channel('datadog:pg:pool:query:start'),
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('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
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
- HTTP_REQUEST_HEADER_VALUE,
9
- HTTP_REQUEST_PATH_PARAM,
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 && ranges.length > 0 && !this._areSafeRanges(ranges)
42
+ return ranges?.length > 0 && this._hasUnsafeRange(ranges)
39
43
  }
40
44
 
41
- // Do not report vulnerability if ranges sources are exclusively url,
42
- // path params or referer header to avoid false positives.
43
- _areSafeRanges (ranges) {
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
- _isRefererHeader (range) {
50
- return range.iinfo.type === HTTP_REQUEST_HEADER_VALUE &&
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 }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, 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
- Reporter.finishRequest(req, res)
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 = res.getHeaders()
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(`No probe found for breakpoint ${hitBreakpoint}`)
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(`No loaded script found for ${file}`)
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(`Adding breakpoint at ${url}:${line}`)
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(() => `Number of received known tests: ${numTests}`)
99
+ log.debug('Number of received known tests:', numTests)
100
100
 
101
101
  done(null, knownTests)
102
102
  } catch (err) {
@@ -68,7 +68,7 @@ class Writer extends BaseWriter {
68
68
  done()
69
69
  return
70
70
  }
71
- log.debug(`Response from the intake: ${res}`)
71
+ log.debug('Response from the intake:', res)
72
72
  done()
73
73
  })
74
74
  }
@@ -45,7 +45,7 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
45
45
  done()
46
46
  return
47
47
  }
48
- log.debug(`Response from the logs intake: ${res}`)
48
+ log.debug('Response from the logs intake:', res)
49
49
  done()
50
50
  })
51
51
  }
@@ -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.length)
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(`Response from the intake: ${res}`)
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(`There are ${alreadySeenCommits.length} commits to exclude.`)
108
+ log.debug('There are %s commits to exclude.', alreadySeenCommits.length)
109
109
  const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
110
- log.debug(`There are ${commitsToInclude.length} commits to include.`)
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(`There are ${commitsToUpload.length} commits to upload`)
198
+ log.debug('There are %s commits to upload', commitsToUpload.length)
199
199
 
200
200
  const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
201
201
 
202
- log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
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(`Uploading git history for repository ${repositoryUrl}`)
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(`There were ${latestCommits.length} commits since last month.`)
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(() => `Number of received skippable ${testLevel}s: ${skippableSuites.length}`)
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(() => `Remote settings: ${JSON.stringify(settings)}`)
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(() => 'Dangerously set code coverage to true')
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(() => 'Dangerously set test skipping to true')
133
+ log.debug('Dangerously set test skipping to true')
134
134
  }
135
135
 
136
136
  incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, settings)