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.
- package/index.d.ts +6 -0
- package/package.json +5 -8
- package/packages/datadog-code-origin/index.js +3 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +5 -0
- package/packages/datadog-instrumentations/src/azure-service-bus.js +38 -0
- package/packages/datadog-instrumentations/src/fastify.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/next.js +17 -18
- package/packages/datadog-instrumentations/src/sequelize.js +4 -14
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +6 -38
- package/packages/datadog-plugin-azure-functions/src/index.js +57 -28
- package/packages/datadog-plugin-azure-service-bus/src/index.js +15 -0
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +36 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -23
- package/packages/datadog-plugin-langchain/src/handlers/default.js +0 -18
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +0 -48
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +18 -0
- package/packages/datadog-plugin-langchain/src/tracing.js +5 -17
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +8 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +11 -10
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +25 -18
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +5 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
- package/packages/dd-trace/src/appsec/iast/index.js +25 -7
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +79 -21
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +0 -4
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +4 -8
- package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +2 -4
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +8 -0
- package/packages/dd-trace/src/plugins/util/git.js +50 -15
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +4 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +5 -4
- package/packages/dd-trace/src/remote_config/scheduler.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
- package/packages/dd-trace/src/supported-configurations.json +1 -0
- package/packages/datadog-plugin-langchain/src/handlers/chain.js +0 -50
- package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +0 -101
- package/packages/datadog-plugin-langchain/src/handlers/language_models/index.js +0 -48
- 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
|
|
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
|
|
28
|
-
chat_model: new
|
|
29
|
-
llm: new
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
37
|
-
|
|
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 (
|
|
57
|
-
if (
|
|
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
|
-
|
|
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:
|
|
61
|
-
({
|
|
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 (
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
}
|