dd-trace 5.60.0 → 5.61.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 (45) hide show
  1. package/index.d.ts +6 -0
  2. package/package.json +5 -8
  3. package/packages/datadog-code-origin/index.js +3 -0
  4. package/packages/datadog-instrumentations/src/azure-functions.js +5 -0
  5. package/packages/datadog-instrumentations/src/azure-service-bus.js +38 -0
  6. package/packages/datadog-instrumentations/src/fastify.js +17 -0
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/next.js +17 -18
  9. package/packages/datadog-instrumentations/src/sequelize.js +4 -14
  10. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +6 -38
  11. package/packages/datadog-plugin-azure-functions/src/index.js +57 -28
  12. package/packages/datadog-plugin-azure-service-bus/src/index.js +15 -0
  13. package/packages/datadog-plugin-azure-service-bus/src/producer.js +36 -0
  14. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -23
  15. package/packages/datadog-plugin-langchain/src/handlers/default.js +0 -18
  16. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +0 -48
  17. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +18 -0
  18. package/packages/datadog-plugin-langchain/src/tracing.js +5 -17
  19. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +8 -1
  20. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +2 -2
  21. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +11 -10
  22. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +25 -18
  23. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -5
  24. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +5 -1
  25. package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
  26. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
  27. package/packages/dd-trace/src/appsec/iast/index.js +25 -7
  28. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +79 -21
  29. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
  30. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +0 -4
  31. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +4 -8
  32. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +2 -4
  33. package/packages/dd-trace/src/plugins/index.js +1 -0
  34. package/packages/dd-trace/src/plugins/util/ci.js +8 -0
  35. package/packages/dd-trace/src/plugins/util/git.js +50 -15
  36. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  37. package/packages/dd-trace/src/profiling/profilers/space.js +4 -3
  38. package/packages/dd-trace/src/profiling/profilers/wall.js +5 -4
  39. package/packages/dd-trace/src/remote_config/scheduler.js +2 -1
  40. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
  41. package/packages/dd-trace/src/supported-configurations.json +1 -0
  42. package/packages/datadog-plugin-langchain/src/handlers/chain.js +0 -50
  43. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +0 -101
  44. package/packages/datadog-plugin-langchain/src/handlers/language_models/index.js +0 -48
  45. package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +0 -58
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ const LangChainHandler = require('./default')
4
+
5
+ class LangChainLanguageModelHandler extends LangChainHandler {
6
+ extractProvider (instance) {
7
+ return typeof instance._llmType === 'function' && instance._llmType().split('-')[0]
8
+ }
9
+
10
+ extractModel (instance) {
11
+ for (const attr of ['model', 'modelName', 'modelId', 'modelKey', 'repoId']) {
12
+ const modelName = instance[attr]
13
+ if (modelName) return modelName
14
+ }
15
+ }
16
+ }
17
+
18
+ module.exports = LangChainLanguageModelHandler
@@ -4,15 +4,12 @@ const { MEASURED } = require('../../../ext/tags')
4
4
  const { storage } = require('../../datadog-core')
5
5
  const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
6
6
 
7
- const API_KEY = 'langchain.request.api_key'
8
7
  const MODEL = 'langchain.request.model'
9
8
  const PROVIDER = 'langchain.request.provider'
10
9
  const TYPE = 'langchain.request.type'
11
10
 
12
11
  const LangChainHandler = require('./handlers/default')
13
- const LangChainChatModelHandler = require('./handlers/language_models/chat_model')
14
- const LangChainLLMHandler = require('./handlers/language_models/llm')
15
- const LangChainChainHandler = require('./handlers/chain')
12
+ const LangChainLanguageModelHandler = require('./handlers/language_models')
16
13
  const LangChainEmbeddingHandler = require('./handlers/embedding')
17
14
 
18
15
  class BaseLangChainTracingPlugin extends TracingPlugin {
@@ -24,9 +21,9 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
24
21
  super(...arguments)
25
22
 
26
23
  this.handlers = {
27
- chain: new LangChainChainHandler(this._tracerConfig),
28
- chat_model: new LangChainChatModelHandler(this._tracerConfig),
29
- llm: new LangChainLLMHandler(this._tracerConfig),
24
+ chain: new LangChainHandler(this._tracerConfig),
25
+ chat_model: new LangChainLanguageModelHandler(this._tracerConfig),
26
+ llm: new LangChainLanguageModelHandler(this._tracerConfig),
30
27
  embedding: new LangChainEmbeddingHandler(this._tracerConfig),
31
28
  default: new LangChainHandler(this._tracerConfig)
32
29
  }
@@ -50,7 +47,6 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
50
47
  const handler = this.handlers[type] || this.handlers.default
51
48
 
52
49
  const instance = ctx.instance
53
- const apiKey = handler.extractApiKey(instance)
54
50
  const provider = handler.extractProvider(instance)
55
51
  const model = handler.extractModel(instance)
56
52
 
@@ -63,9 +59,8 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
63
59
  }
64
60
  }, false)
65
61
 
66
- const tags = handler.getSpanStartTags(ctx, provider, span) || []
62
+ const tags = {}
67
63
 
68
- if (apiKey) tags[API_KEY] = apiKey
69
64
  if (provider) tags[PROVIDER] = provider
70
65
  if (model) tags[MODEL] = model
71
66
  if (type) tags[TYPE] = type
@@ -81,13 +76,6 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
81
76
  asyncEnd (ctx) {
82
77
  const span = ctx.currentStore.span
83
78
 
84
- const { type } = ctx
85
-
86
- const handler = this.handlers[type] || this.handlers.default
87
- const tags = handler.getSpanEndTags(ctx, span) || {}
88
-
89
- span.addTags(tags)
90
-
91
79
  span.finish()
92
80
  }
93
81
 
@@ -3,7 +3,14 @@
3
3
  const Analyzer = require('./vulnerability-analyzer')
4
4
  const { getNodeModulesPaths } = require('../path-line')
5
5
 
6
- const EXCLUDED_PATHS = getNodeModulesPaths('express/lib/response.js')
6
+ const EXCLUDED_PATHS = [
7
+ // Express
8
+ getNodeModulesPaths('express/lib/response.js'),
9
+ // Fastify
10
+ getNodeModulesPaths('fastify/lib/reply.js'),
11
+ getNodeModulesPaths('fastify/lib/hooks.js'),
12
+ getNodeModulesPaths('@fastify/cookie/plugin.js')
13
+ ]
7
14
 
8
15
  class CookieAnalyzer extends Analyzer {
9
16
  constructor (type, propertyToBeSafe) {
@@ -10,8 +10,8 @@ class HstsHeaderMissingAnalyzer extends MissingHeaderAnalyzer {
10
10
  super(HSTS_HEADER_MISSING, HSTS_HEADER_NAME)
11
11
  }
12
12
 
13
- _isVulnerableFromRequestAndResponse (req, res) {
14
- const headerValues = this._getHeaderValues(res, HSTS_HEADER_NAME)
13
+ _isVulnerableFromRequestAndResponse (req, res, storedHeaders) {
14
+ const headerValues = this._getHeaderValues(res, storedHeaders, HSTS_HEADER_NAME)
15
15
  return this._isHttpsProtocol(req) && (
16
16
  headerValues.length === 0 ||
17
17
  headerValues.some(headerValue => !this._isHeaderValid(headerValue))
@@ -28,8 +28,9 @@ class MissingHeaderAnalyzer extends Analyzer {
28
28
  }, (data) => this.analyze(data))
29
29
  }
30
30
 
31
- _getHeaderValues (res, headerName) {
32
- const headerValue = res.getHeader(headerName)
31
+ _getHeaderValues (res, storedHeaders, headerName) {
32
+ headerName = headerName.toLowerCase()
33
+ const headerValue = res.getHeader(headerName) || storedHeaders[headerName]
33
34
  if (Array.isArray(headerValue)) {
34
35
  return headerValue
35
36
  }
@@ -46,8 +47,8 @@ class MissingHeaderAnalyzer extends Analyzer {
46
47
  return `${type}:${this.config.tracerConfig.service}`
47
48
  }
48
49
 
49
- _getEvidence ({ res }) {
50
- const headerValues = this._getHeaderValues(res, this.headerName)
50
+ _getEvidence ({ res, storedHeaders }) {
51
+ const headerValues = this._getHeaderValues(res, storedHeaders, this.headerName)
51
52
  let value
52
53
  if (headerValues.length === 1) {
53
54
  value = headerValues[0]
@@ -57,19 +58,19 @@ class MissingHeaderAnalyzer extends Analyzer {
57
58
  return { value }
58
59
  }
59
60
 
60
- _isVulnerable ({ req, res }, context) {
61
- if (!IGNORED_RESPONSE_STATUS_LIST.has(res.statusCode) && this._isResponseHtml(res)) {
62
- return this._isVulnerableFromRequestAndResponse(req, res)
61
+ _isVulnerable ({ req, res, storedHeaders }, context) {
62
+ if (!IGNORED_RESPONSE_STATUS_LIST.has(res.statusCode) && this._isResponseHtml(res, storedHeaders)) {
63
+ return this._isVulnerableFromRequestAndResponse(req, res, storedHeaders)
63
64
  }
64
65
  return false
65
66
  }
66
67
 
67
- _isVulnerableFromRequestAndResponse (req, res) {
68
+ _isVulnerableFromRequestAndResponse (req, res, storedHeaders) {
68
69
  return false
69
70
  }
70
71
 
71
- _isResponseHtml (res) {
72
- const contentTypes = this._getHeaderValues(res, 'content-type')
72
+ _isResponseHtml (res, storedHeaders) {
73
+ const contentTypes = this._getHeaderValues(res, storedHeaders, 'content-type')
73
74
  return contentTypes.some(contentType => {
74
75
  return contentType && HTML_CONTENT_TYPES.some(htmlContentType => {
75
76
  return htmlContentType === contentType || contentType.startsWith(htmlContentType + ';')
@@ -7,25 +7,32 @@ class SetCookiesHeaderInterceptor extends Plugin {
7
7
  constructor () {
8
8
  super()
9
9
  this.cookiesInRequest = new WeakMap()
10
- this.addSub('datadog:http:server:response:set-header:finish', ({ name, value, res }) => {
11
- if (name.toLowerCase() === 'set-cookie') {
12
- let allCookies = value
13
- if (typeof value === 'string') {
14
- allCookies = [value]
15
- }
16
- const alreadyCheckedCookies = this._getAlreadyCheckedCookiesInResponse(res)
17
-
18
- let location
19
- allCookies.forEach(cookieString => {
20
- if (!alreadyCheckedCookies.includes(cookieString)) {
21
- alreadyCheckedCookies.push(cookieString)
22
- const parsedCookie = this._parseCookie(cookieString, location)
23
- setCookieChannel.publish(parsedCookie)
24
- location = parsedCookie.location
25
- }
26
- })
10
+
11
+ this.addSub('datadog:http:server:response:set-header:finish',
12
+ ({ name, value, res }) => this._handleCookies(name, value, res))
13
+
14
+ this.addSub('datadog:fastify:set-header:finish',
15
+ ({ name, value, res }) => this._handleCookies(name, value, res))
16
+ }
17
+
18
+ _handleCookies (name, value, res) {
19
+ if (name.toLowerCase() === 'set-cookie') {
20
+ let allCookies = value
21
+ if (typeof value === 'string') {
22
+ allCookies = [value]
27
23
  }
28
- })
24
+ const alreadyCheckedCookies = this._getAlreadyCheckedCookiesInResponse(res)
25
+
26
+ let location
27
+ allCookies.forEach(cookieString => {
28
+ if (!alreadyCheckedCookies.includes(cookieString)) {
29
+ alreadyCheckedCookies.push(cookieString)
30
+ const parsedCookie = this._parseCookie(cookieString, location)
31
+ setCookieChannel.publish(parsedCookie)
32
+ location = parsedCookie.location
33
+ }
34
+ })
35
+ }
29
36
  }
30
37
 
31
38
  _parseCookie (cookieString, location) {
@@ -18,31 +18,39 @@ class SqlInjectionAnalyzer extends StoredInjectionAnalyzer {
18
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
- this.addSub(
21
+ this.addBind(
22
22
  'datadog:sequelize:query:start',
23
23
  ({ sql, dialect }) => this.getStoreAndAnalyze(sql, dialect.toUpperCase())
24
24
  )
25
25
  this.addSub('datadog:sequelize:query:finish', () => this.returnToParentStore())
26
26
 
27
- this.addSub('datadog:pg:pool:query:start', ({ query }) => this.getStoreAndAnalyze(query.text, 'POSTGRES'))
27
+ this.addSub('datadog:pg:pool:query:start', ({ query }) => this.setStoreAndAnalyze(query.text, 'POSTGRES'))
28
28
  this.addSub('datadog:pg:pool:query:finish', () => this.returnToParentStore())
29
29
 
30
- this.addSub('datadog:mysql:pool:query:start', ({ sql }) => this.getStoreAndAnalyze(sql, 'MYSQL'))
30
+ this.addSub('datadog:mysql:pool:query:start', ({ sql }) => this.setStoreAndAnalyze(sql, 'MYSQL'))
31
31
  this.addSub('datadog:mysql:pool:query:finish', () => this.returnToParentStore())
32
32
 
33
33
  this.addSub('datadog:knex:raw:start', ({ sql, dialect: knexDialect }) => {
34
34
  const dialect = this.normalizeKnexDialect(knexDialect)
35
- this.getStoreAndAnalyze(sql, dialect)
35
+ this.setStoreAndAnalyze(sql, dialect)
36
36
  })
37
37
  this.addSub('datadog:knex:raw:finish', () => this.returnToParentStore())
38
38
  }
39
39
 
40
+ setStoreAndAnalyze (query, dialect) {
41
+ const store = this.getStoreAndAnalyze(query, dialect)
42
+
43
+ if (store) {
44
+ storage('legacy').enterWith(store)
45
+ }
46
+ }
47
+
40
48
  getStoreAndAnalyze (query, dialect) {
41
49
  const parentStore = storage('legacy').getStore()
42
50
  if (parentStore) {
43
51
  this.analyze(query, parentStore, dialect)
44
52
 
45
- storage('legacy').enterWith({ ...parentStore, sqlAnalyzed: true, sqlParentStore: parentStore })
53
+ return { ...parentStore, sqlAnalyzed: true, sqlParentStore: parentStore }
46
54
  }
47
55
  }
48
56
 
@@ -9,7 +9,10 @@ const {
9
9
  HTTP_REQUEST_PARAMETER
10
10
  } = require('../taint-tracking/source-types')
11
11
 
12
- const EXCLUDED_PATHS = getNodeModulesPaths('express/lib/response.js')
12
+ const EXCLUDED_PATHS = [
13
+ getNodeModulesPaths('express/lib/response.js'),
14
+ getNodeModulesPaths('fastify/lib/reply.js'),
15
+ ]
13
16
 
14
17
  const VULNERABLE_SOURCE_TYPES = new Set([
15
18
  HTTP_REQUEST_BODY,
@@ -23,6 +26,7 @@ class UnvalidatedRedirectAnalyzer extends InjectionAnalyzer {
23
26
 
24
27
  onConfigure () {
25
28
  this.addSub('datadog:http:server:response:set-header:finish', ({ name, value }) => this.analyze(name, value))
29
+ this.addSub('datadog:fastify:set-header:finish', ({ name, value }) => this.analyze(name, value))
26
30
  }
27
31
 
28
32
  analyze (name, value) {
@@ -10,8 +10,8 @@ class XcontenttypeHeaderMissingAnalyzer extends MissingHeaderAnalyzer {
10
10
  super(XCONTENTTYPE_HEADER_MISSING, XCONTENTTYPEOPTIONS_HEADER_NAME)
11
11
  }
12
12
 
13
- _isVulnerableFromRequestAndResponse (req, res) {
14
- const headerValues = this._getHeaderValues(res, XCONTENTTYPEOPTIONS_HEADER_NAME)
13
+ _isVulnerableFromRequestAndResponse (req, res, storedHeaders) {
14
+ const headerValues = this._getHeaderValues(res, storedHeaders, XCONTENTTYPEOPTIONS_HEADER_NAME)
15
15
  return headerValues.length === 0 || headerValues.some(headerValue => headerValue.trim().toLowerCase() !== 'nosniff')
16
16
  }
17
17
  }
@@ -47,6 +47,10 @@ class IastPluginSubscription {
47
47
  }
48
48
 
49
49
  matchesModuleInstrumented (name) {
50
+ // Remove node: prefix if present
51
+ if (name.startsWith('node:')) {
52
+ name = name.slice(5)
53
+ }
50
54
  // https module is a special case because it's events are published as http
51
55
  name = name === 'https' ? 'http' : name
52
56
  return this.moduleName === name
@@ -18,11 +18,12 @@ const { IAST_ENABLED_TAG_KEY } = require('./tags')
18
18
  const iastTelemetry = require('./telemetry')
19
19
  const { enable: enableFsPlugin, disable: disableFsPlugin, IAST_MODULE } = require('../rasp/fs-plugin')
20
20
  const securityControls = require('./security-controls')
21
+ const { incomingHttpRequestStart, incomingHttpRequestEnd, responseWriteHead } = require('../channels')
22
+
23
+ const collectedResponseHeaders = new WeakMap()
21
24
 
22
25
  // TODO Change to `apm:http:server:request:[start|close]` when the subscription
23
26
  // order of the callbacks can be enforce
24
- const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
25
- const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
26
27
  const iastResponseEnd = dc.channel('datadog:iast:response-end')
27
28
  let isEnabled = false
28
29
 
@@ -33,8 +34,9 @@ function enable (config, _tracer) {
33
34
  enableFsPlugin(IAST_MODULE)
34
35
  enableAllAnalyzers(config)
35
36
  enableTaintTracking(config.iast, iastTelemetry.verbosity)
36
- requestStart.subscribe(onIncomingHttpRequestStart)
37
- requestClose.subscribe(onIncomingHttpRequestEnd)
37
+ incomingHttpRequestStart.subscribe(onIncomingHttpRequestStart)
38
+ incomingHttpRequestEnd.subscribe(onIncomingHttpRequestEnd)
39
+ responseWriteHead.subscribe(onResponseWriteHeadCollect)
38
40
  overheadController.configure(config.iast)
39
41
  overheadController.startGlobalContext()
40
42
  securityControls.configure(config.iast)
@@ -53,8 +55,9 @@ function disable () {
53
55
  disableAllAnalyzers()
54
56
  disableTaintTracking()
55
57
  overheadController.finishGlobalContext()
56
- if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
57
- if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
58
+ if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onIncomingHttpRequestStart)
59
+ if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(onIncomingHttpRequestEnd)
60
+ if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHeadCollect)
58
61
  vulnerabilityReporter.stop()
59
62
  }
60
63
 
@@ -89,7 +92,13 @@ function onIncomingHttpRequestEnd (data) {
89
92
  const topContext = web.getContext(data.req)
90
93
  const iastContext = iastContextFunctions.getIastContext(store, topContext)
91
94
  if (iastContext?.rootSpan) {
92
- iastResponseEnd.publish(data)
95
+ const storedHeaders = collectedResponseHeaders.get(data.res) || {}
96
+
97
+ iastResponseEnd.publish({ ...data, storedHeaders })
98
+
99
+ if (Object.keys(storedHeaders).length) {
100
+ collectedResponseHeaders.delete(data.res)
101
+ }
93
102
 
94
103
  const vulnerabilities = iastContext.vulnerabilities
95
104
  const rootSpan = iastContext.rootSpan
@@ -105,4 +114,13 @@ function onIncomingHttpRequestEnd (data) {
105
114
  }
106
115
  }
107
116
 
117
+ // Response headers are collected here because they are not available in the onIncomingHttpRequestEnd when using Fastify
118
+ function onResponseWriteHeadCollect ({ res, responseHeaders = {} }) {
119
+ if (!res) return
120
+
121
+ if (Object.keys(responseHeaders).length) {
122
+ collectedResponseHeaders.set(res, responseHeaders)
123
+ }
124
+ }
125
+
108
126
  module.exports = { enable, disable, onIncomingHttpRequestEnd, onIncomingHttpRequestStart }
@@ -38,6 +38,25 @@ class TaintTrackingPlugin extends SourceIastPlugin {
38
38
  }
39
39
 
40
40
  onConfigure () {
41
+ this.addBodyParsingSubscriptions()
42
+
43
+ this.addQueryParameterSubscriptions()
44
+
45
+ this.addCookieSubscriptions()
46
+
47
+ this.addDatabaseSubscriptions()
48
+
49
+ this.addPathParameterSubscriptions()
50
+
51
+ this.addGraphQLSubscriptions()
52
+
53
+ this.addURLParsingSubscriptions()
54
+
55
+ // this is a special case to increment INSTRUMENTED_SOURCE metric for header
56
+ this.addInstrumentedSource('http', [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME])
57
+ }
58
+
59
+ addBodyParsingSubscriptions () {
41
60
  const onRequestBody = ({ req }) => {
42
61
  const iastContext = getIastContext(storage('legacy').getStore())
43
62
  if (iastContext && iastContext.body !== req.body) {
@@ -57,17 +76,13 @@ class TaintTrackingPlugin extends SourceIastPlugin {
57
76
  )
58
77
 
59
78
  this.addSub(
60
- { channelName: 'datadog:query:read:finish', tag: HTTP_REQUEST_PARAMETER },
61
- ({ query }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
62
- )
63
-
64
- this.addSub(
65
- { channelName: 'datadog:express:query:finish', tag: HTTP_REQUEST_PARAMETER },
66
- ({ query }) => {
79
+ { channelName: 'datadog:fastify:body-parser:finish', tag: HTTP_REQUEST_BODY },
80
+ ({ body }) => {
67
81
  const iastContext = getIastContext(storage('legacy').getStore())
68
- if (!iastContext || !query) return
69
-
70
- taintQueryWithCache(iastContext, query)
82
+ if (iastContext && iastContext.body !== body) {
83
+ this._taintTrackingHandler(HTTP_REQUEST_BODY, body)
84
+ iastContext.body = body
85
+ }
71
86
  }
72
87
  )
73
88
 
@@ -83,12 +98,45 @@ class TaintTrackingPlugin extends SourceIastPlugin {
83
98
  }
84
99
  }
85
100
  )
101
+ }
102
+
103
+ addQueryParameterSubscriptions () {
104
+ this.addSub(
105
+ { channelName: 'datadog:query:read:finish', tag: HTTP_REQUEST_PARAMETER },
106
+ ({ query }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
107
+ )
108
+
109
+ this.addSub(
110
+ { channelName: 'datadog:fastify:query-params:finish', tag: HTTP_REQUEST_PARAMETER },
111
+ ({ query }) => {
112
+ this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
113
+ }
114
+ )
86
115
 
116
+ this.addSub(
117
+ { channelName: 'datadog:express:query:finish', tag: HTTP_REQUEST_PARAMETER },
118
+ ({ query }) => {
119
+ const iastContext = getIastContext(storage('legacy').getStore())
120
+ if (!iastContext || !query) return
121
+
122
+ taintQueryWithCache(iastContext, query)
123
+ }
124
+ )
125
+ }
126
+
127
+ addCookieSubscriptions () {
87
128
  this.addSub(
88
129
  { channelName: 'datadog:cookie:parse:finish', tag: [HTTP_REQUEST_COOKIE_VALUE, HTTP_REQUEST_COOKIE_NAME] },
89
130
  ({ cookies }) => this._cookiesTaintTrackingHandler(cookies)
90
131
  )
91
132
 
133
+ this.addSub(
134
+ { channelName: 'datadog:fastify-cookie:read:finish', tag: [HTTP_REQUEST_COOKIE_VALUE, HTTP_REQUEST_COOKIE_NAME] },
135
+ ({ cookies }) => this._cookiesTaintTrackingHandler(cookies)
136
+ )
137
+ }
138
+
139
+ addDatabaseSubscriptions () {
92
140
  this.addSub(
93
141
  { channelName: 'datadog:sequelize:query:finish', tag: SQL_ROW_VALUE },
94
142
  ({ result }) => this._taintDatabaseResult(result, 'sequelize')
@@ -98,25 +146,36 @@ class TaintTrackingPlugin extends SourceIastPlugin {
98
146
  { channelName: 'apm:pg:query:finish', tag: SQL_ROW_VALUE },
99
147
  ({ result }) => this._taintDatabaseResult(result, 'pg')
100
148
  )
149
+ }
150
+
151
+ addPathParameterSubscriptions () {
152
+ const pathParamHandler = ({ req }) => {
153
+ if (req && req.params !== null && typeof req.params === 'object') {
154
+ this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
155
+ }
156
+ }
101
157
 
102
158
  this.addSub(
103
159
  { channelName: 'datadog:express:process_params:start', tag: HTTP_REQUEST_PATH_PARAM },
104
- ({ req }) => {
105
- if (req && req.params !== null && typeof req.params === 'object') {
106
- this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
107
- }
108
- }
160
+ pathParamHandler
109
161
  )
110
162
 
111
163
  this.addSub(
112
164
  { channelName: 'datadog:router:param:start', tag: HTTP_REQUEST_PATH_PARAM },
113
- ({ req }) => {
114
- if (req && req.params !== null && typeof req.params === 'object') {
115
- this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
165
+ pathParamHandler
166
+ )
167
+
168
+ this.addSub(
169
+ { channelName: 'datadog:fastify:path-params:finish', tag: HTTP_REQUEST_PATH_PARAM },
170
+ ({ req, params }) => {
171
+ if (req) {
172
+ this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, params)
116
173
  }
117
174
  }
118
175
  )
176
+ }
119
177
 
178
+ addGraphQLSubscriptions () {
120
179
  this.addSub(
121
180
  { channelName: 'apm:graphql:resolve:start', tag: HTTP_REQUEST_BODY },
122
181
  (data) => {
@@ -128,7 +187,9 @@ class TaintTrackingPlugin extends SourceIastPlugin {
128
187
  }
129
188
  }
130
189
  )
190
+ }
131
191
 
192
+ addURLParsingSubscriptions () {
132
193
  const urlResultTaintedProperties = ['host', 'origin', 'hostname']
133
194
  this.addSub(
134
195
  { channelName: 'datadog:url:parse:finish' },
@@ -162,9 +223,6 @@ class TaintTrackingPlugin extends SourceIastPlugin {
162
223
  context.result =
163
224
  newTaintedString(iastContext, context.result, origRange.iinfo.parameterName, origRange.iinfo.type)
164
225
  })
165
-
166
- // this is a special case to increment INSTRUMENTED_SOURCE metric for header
167
- this.addInstrumentedSource('http', [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME])
168
226
  }
169
227
 
170
228
  _taintTrackingHandler (type, target, property, iastContext = getIastContext(storage('legacy').getStore())) {
@@ -4,9 +4,7 @@ const sensitiveHandler = require('./evidence-redaction/sensitive-handler')
4
4
  const { stringifyWithRanges } = require('./utils')
5
5
 
6
6
  class VulnerabilityFormatter {
7
- constructor () {
8
- this._redactVulnearbilities = true
9
- }
7
+ _redactVulnearbilities = true
10
8
 
11
9
  setRedactVulnerabilities (shouldRedactVulnerabilities, redactionNamePattern, redactionValuePattern) {
12
10
  this._redactVulnearbilities = shouldRedactVulnerabilities
@@ -35,10 +35,6 @@ class AppsecFsPlugin extends Plugin {
35
35
  this.addBind('apm:fs:operation:finish', this._onFsOperationFinishOrRenderEnd)
36
36
  this.addBind('tracing:datadog:express:response:render:start', this._onResponseRenderStart)
37
37
  this.addBind('tracing:datadog:express:response:render:end', this._onFsOperationFinishOrRenderEnd)
38
- // TODO Remove this when dc-polyfill is fixed&updated
39
- // hack to node 18 and early 20.x
40
- // with dc-polyfill addBind is not enough to force a channel.hasSubscribers === true
41
- this.addSub('tracing:datadog:express:response:render:start', () => {})
42
38
 
43
39
  super.configure(true)
44
40
  }
@@ -66,17 +66,13 @@ class SchemaBuilder {
66
66
  }
67
67
 
68
68
  class OpenApiSchema {
69
- constructor () {
70
- this.openapi = '3.0.0'
71
- this.components = new OpenApiComponents()
72
- }
69
+ openapi = '3.0.0'
70
+ components = new OpenApiComponents()
73
71
  }
74
72
 
75
73
  OpenApiSchema.SCHEMA = class {
76
- constructor () {
77
- this.type = 'object'
78
- this.properties = {}
79
- }
74
+ type = 'object'
75
+ properties = {}
80
76
  }
81
77
 
82
78
  OpenApiSchema.PROPERTY = class {
@@ -3,10 +3,8 @@
3
3
  const SAMPLE_INTERVAL_MILLIS = 30 * 1000
4
4
 
5
5
  class SchemaSampler {
6
- constructor () {
7
- this.weight = 0
8
- this.lastSampleMs = 0
9
- }
6
+ weight = 0
7
+ lastSampleMs = 0
10
8
 
11
9
  trySample (currentTimeMs) {
12
10
  if (currentTimeMs >= this.lastSampleMs + SAMPLE_INTERVAL_MILLIS) {
@@ -4,6 +4,7 @@ module.exports = {
4
4
  get '@apollo/gateway' () { return require('../../../datadog-plugin-apollo/src') },
5
5
  get '@aws-sdk/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
6
6
  get '@azure/functions' () { return require('../../../datadog-plugin-azure-functions/src') },
7
+ get '@azure/service-bus' () { return require('../../../datadog-plugin-azure-service-bus/src') },
7
8
  get '@cucumber/cucumber' () { return require('../../../datadog-plugin-cucumber/src') },
8
9
  get '@playwright/test' () { return require('../../../datadog-plugin-playwright/src') },
9
10
  get '@elastic/elasticsearch' () { return require('../../../datadog-plugin-elasticsearch/src') },
@@ -86,6 +86,13 @@ function resolveTilde (filePath) {
86
86
  return filePath
87
87
  }
88
88
 
89
+ function normalizeNumber (number) {
90
+ if (typeof number !== 'number') {
91
+ return number
92
+ }
93
+ return number.toString()
94
+ }
95
+
89
96
  function getGitHubEventPayload () {
90
97
  if (!getEnvironmentVariable('GITHUB_EVENT_PATH')) {
91
98
  return
@@ -735,6 +742,7 @@ module.exports = {
735
742
  normalizeTag(tags, GIT_BRANCH, normalizeRef)
736
743
  normalizeTag(tags, GIT_TAG, normalizeRef)
737
744
  normalizeTag(tags, GIT_PULL_REQUEST_BASE_BRANCH, normalizeRef)
745
+ normalizeTag(tags, PR_NUMBER, normalizeNumber)
738
746
 
739
747
  return removeEmptyValues(tags)
740
748
  }