dd-trace 4.49.0 → 4.50.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/LICENSE-3rdparty.csv +2 -0
- package/index.d.ts +10 -8
- package/init.js +60 -47
- package/package.json +5 -2
- package/packages/datadog-core/index.js +1 -3
- package/packages/datadog-core/src/storage.js +21 -0
- package/packages/datadog-instrumentations/src/express.js +1 -1
- package/packages/datadog-instrumentations/src/handlebars.js +40 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +6 -2
- package/packages/datadog-instrumentations/src/pug.js +23 -0
- package/packages/datadog-instrumentations/src/router.js +2 -3
- package/packages/datadog-plugin-amqplib/src/consumer.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -7
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +34 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +10 -9
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +59 -45
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
- package/packages/datadog-plugin-grpc/src/server.js +2 -1
- package/packages/datadog-plugin-http/src/client.js +42 -1
- package/packages/datadog-plugin-http2/src/client.js +26 -1
- package/packages/datadog-plugin-jest/src/index.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-moleculer/src/server.js +2 -2
- package/packages/datadog-plugin-rhea/src/consumer.js +2 -1
- package/packages/datadog-plugin-vitest/src/index.js +2 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +50 -27
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +33 -16
- package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +18 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +6 -6
- package/packages/dd-trace/src/appsec/recommended.json +353 -155
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +0 -7
- package/packages/dd-trace/src/appsec/reporter.js +1 -0
- package/packages/dd-trace/src/config.js +13 -4
- package/packages/dd-trace/src/constants.js +6 -1
- package/packages/dd-trace/src/crashtracking/crashtracker.js +98 -0
- package/packages/dd-trace/src/crashtracking/index.js +15 -0
- package/packages/dd-trace/src/crashtracking/noop.js +8 -0
- package/packages/dd-trace/src/llmobs/sdk.js +1 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/writers/spans/base.js +3 -0
- package/packages/dd-trace/src/log/index.js +10 -13
- package/packages/dd-trace/src/log/log.js +52 -0
- package/packages/dd-trace/src/log/writer.js +50 -19
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +15 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +35 -22
- package/packages/dd-trace/src/opentracing/span.js +14 -0
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- package/packages/dd-trace/src/plugins/tracing.js +3 -3
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +121 -0
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +0 -1
- package/packages/dd-trace/src/plugins/util/web.js +39 -11
- package/packages/dd-trace/src/proxy.js +5 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +16 -11
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -8
- package/packages/dd-trace/src/telemetry/metrics.js +6 -1
- package/packages/dd-trace/src/util.js +16 -1
- package/version.js +4 -2
- /package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/{code-injection-sensitive-analyzer.js → tainted-range-based-sensitive-analyzer.js} +0 -0
|
@@ -88,6 +88,7 @@ afterEach(function () {
|
|
|
88
88
|
const testInfo = {
|
|
89
89
|
testName: currentTest.fullTitle(),
|
|
90
90
|
testSuite: Cypress.mocha.getRootSuite().file,
|
|
91
|
+
testSuiteAbsolutePath: Cypress.spec && Cypress.spec.absolute,
|
|
91
92
|
state: currentTest.state,
|
|
92
93
|
error: currentTest.err,
|
|
93
94
|
isNew: currentTest._ddIsNew,
|
|
@@ -58,7 +58,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
58
58
|
span._spanContext._trace.record = false
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
if (this.
|
|
61
|
+
if (this.shouldInjectTraceHeaders(options, uri)) {
|
|
62
62
|
this.tracer.inject(span, HTTP_HEADERS, options.headers)
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -71,6 +71,18 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
71
71
|
return message.currentStore
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
shouldInjectTraceHeaders (options, uri) {
|
|
75
|
+
if (hasAmazonSignature(options) && !this.config.enablePropagationWithAmazonHeaders) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!this.config.propagationFilter(uri)) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
bindAsyncStart ({ parentStore }) {
|
|
75
87
|
return parentStore
|
|
76
88
|
}
|
|
@@ -200,6 +212,31 @@ function getHooks (config) {
|
|
|
200
212
|
return { request }
|
|
201
213
|
}
|
|
202
214
|
|
|
215
|
+
function hasAmazonSignature (options) {
|
|
216
|
+
if (!options) {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (options.headers) {
|
|
221
|
+
const headers = Object.keys(options.headers)
|
|
222
|
+
.reduce((prev, next) => Object.assign(prev, {
|
|
223
|
+
[next.toLowerCase()]: options.headers[next]
|
|
224
|
+
}), {})
|
|
225
|
+
|
|
226
|
+
if (headers['x-amz-signature']) {
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if ([].concat(headers.authorization).some(startsWith('AWS4-HMAC-SHA256'))) {
|
|
231
|
+
return true
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const search = options.search || options.path
|
|
236
|
+
|
|
237
|
+
return search && search.toLowerCase().indexOf('x-amz-signature=') !== -1
|
|
238
|
+
}
|
|
239
|
+
|
|
203
240
|
function extractSessionDetails (options) {
|
|
204
241
|
if (typeof options === 'string') {
|
|
205
242
|
return new URL(options).host
|
|
@@ -211,4 +248,8 @@ function extractSessionDetails (options) {
|
|
|
211
248
|
return { host, port }
|
|
212
249
|
}
|
|
213
250
|
|
|
251
|
+
function startsWith (searchString) {
|
|
252
|
+
return value => String(value).startsWith(searchString)
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
module.exports = HttpClientPlugin
|
|
@@ -62,7 +62,9 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
62
62
|
|
|
63
63
|
addHeaderTags(span, headers, HTTP_REQUEST_HEADERS, this.config)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
if (!hasAmazonSignature(headers, path)) {
|
|
66
|
+
this.tracer.inject(span, HTTP_HEADERS, headers)
|
|
67
|
+
}
|
|
66
68
|
|
|
67
69
|
message.parentStore = store
|
|
68
70
|
message.currentStore = { ...store, span }
|
|
@@ -132,6 +134,29 @@ function extractSessionDetails (authority, options) {
|
|
|
132
134
|
return { protocol, port, host }
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
function hasAmazonSignature (headers, path) {
|
|
138
|
+
if (headers) {
|
|
139
|
+
headers = Object.keys(headers)
|
|
140
|
+
.reduce((prev, next) => Object.assign(prev, {
|
|
141
|
+
[next.toLowerCase()]: headers[next]
|
|
142
|
+
}), {})
|
|
143
|
+
|
|
144
|
+
if (headers['x-amz-signature']) {
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if ([].concat(headers.authorization).some(startsWith('AWS4-HMAC-SHA256'))) {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return path && path.toLowerCase().indexOf('x-amz-signature=') !== -1
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function startsWith (searchString) {
|
|
157
|
+
return value => String(value).startsWith(searchString)
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
function getStatusValidator (config) {
|
|
136
161
|
if (typeof config.validateStatus === 'function') {
|
|
137
162
|
return config.validateStatus
|
|
@@ -219,7 +219,8 @@ class JestPlugin extends CiPlugin {
|
|
|
219
219
|
[COMPONENT]: this.constructor.id,
|
|
220
220
|
...this.testEnvironmentMetadata,
|
|
221
221
|
...testSuiteMetadata
|
|
222
|
-
}
|
|
222
|
+
},
|
|
223
|
+
extractedLinks: testSessionSpanContext?._links
|
|
223
224
|
})
|
|
224
225
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
225
226
|
if (_ddTestCodeCoverageEnabled) {
|
|
@@ -85,7 +85,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const relativeCoverageFiles = [...coverageFiles, suiteFile]
|
|
88
|
-
.map(filename => getTestSuitePath(filename, this.sourceRoot))
|
|
88
|
+
.map(filename => getTestSuitePath(filename, this.repositoryRoot || this.sourceRoot))
|
|
89
89
|
|
|
90
90
|
const { _traceId, _spanId } = testSuiteSpan.context()
|
|
91
91
|
|
|
@@ -9,7 +9,6 @@ class MoleculerServerPlugin extends ServerPlugin {
|
|
|
9
9
|
|
|
10
10
|
start ({ action, ctx, broker }) {
|
|
11
11
|
const followsFrom = this.tracer.extract('text_map', ctx.meta)
|
|
12
|
-
|
|
13
12
|
this.startSpan(this.operationName(), {
|
|
14
13
|
childOf: followsFrom || this.activeSpan,
|
|
15
14
|
service: this.config.service || this.serviceName(),
|
|
@@ -19,7 +18,8 @@ class MoleculerServerPlugin extends ServerPlugin {
|
|
|
19
18
|
meta: {
|
|
20
19
|
'resource.name': action.name,
|
|
21
20
|
...moleculerTags(broker, ctx, this.config)
|
|
22
|
-
}
|
|
21
|
+
},
|
|
22
|
+
extractedLinks: followsFrom?._links
|
|
23
23
|
})
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -191,7 +191,8 @@ class VitestPlugin extends CiPlugin {
|
|
|
191
191
|
[COMPONENT]: this.constructor.id,
|
|
192
192
|
...this.testEnvironmentMetadata,
|
|
193
193
|
...testSuiteMetadata
|
|
194
|
-
}
|
|
194
|
+
},
|
|
195
|
+
extractedLinks: testSessionSpanContext?._links
|
|
195
196
|
})
|
|
196
197
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
197
198
|
const store = storage.getStore()
|
|
@@ -1,61 +1,84 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const TTLCache = require('@isaacs/ttlcache')
|
|
4
|
+
const web = require('../plugins/util/web')
|
|
3
5
|
const log = require('../log')
|
|
6
|
+
const { AUTO_REJECT, USER_REJECT } = require('../../../../ext/priority')
|
|
7
|
+
|
|
8
|
+
const MAX_SIZE = 4096
|
|
4
9
|
|
|
5
10
|
let enabled
|
|
6
|
-
let
|
|
11
|
+
let sampledRequests
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
class NoopTTLCache {
|
|
14
|
+
clear () { }
|
|
15
|
+
set (key) { return undefined }
|
|
16
|
+
has (key) { return false }
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
function configure ({ apiSecurity }) {
|
|
11
20
|
enabled = apiSecurity.enabled
|
|
12
|
-
|
|
21
|
+
sampledRequests = apiSecurity.sampleDelay === 0
|
|
22
|
+
? new NoopTTLCache()
|
|
23
|
+
: new TTLCache({ max: MAX_SIZE, ttl: apiSecurity.sampleDelay * 1000 })
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
function disable () {
|
|
16
27
|
enabled = false
|
|
28
|
+
sampledRequests?.clear()
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
}
|
|
31
|
+
function sampleRequest (req, res, force = false) {
|
|
32
|
+
if (!enabled) return false
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
const key = computeKey(req, res)
|
|
35
|
+
if (!key || isSampled(key)) return false
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
const rootSpan = web.root(req)
|
|
38
|
+
if (!rootSpan) return false
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
let priority = getSpanPriority(rootSpan)
|
|
41
|
+
if (!priority) {
|
|
42
|
+
rootSpan._prioritySampler?.sample(rootSpan)
|
|
43
|
+
priority = getSpanPriority(rootSpan)
|
|
32
44
|
}
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function sampleRequest (req) {
|
|
38
|
-
if (!enabled || !requestSampling) {
|
|
46
|
+
if (priority === AUTO_REJECT || priority === USER_REJECT) {
|
|
39
47
|
return false
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (shouldSample) {
|
|
45
|
-
sampledRequests.add(req)
|
|
50
|
+
if (force) {
|
|
51
|
+
sampledRequests.set(key)
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
return
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isSampled (key) {
|
|
58
|
+
return sampledRequests.has(key)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function computeKey (req, res) {
|
|
62
|
+
const route = web.getContext(req)?.paths?.join('') || ''
|
|
63
|
+
const method = req.method
|
|
64
|
+
const status = res.statusCode
|
|
65
|
+
|
|
66
|
+
if (!method || !status) {
|
|
67
|
+
log.warn('Unsupported groupkey for API security')
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
return method + route + status
|
|
49
71
|
}
|
|
50
72
|
|
|
51
|
-
function
|
|
52
|
-
|
|
73
|
+
function getSpanPriority (span) {
|
|
74
|
+
const spanContext = span.context?.()
|
|
75
|
+
return spanContext._sampling?.priority
|
|
53
76
|
}
|
|
54
77
|
|
|
55
78
|
module.exports = {
|
|
56
79
|
configure,
|
|
57
80
|
disable,
|
|
58
|
-
setRequestSampling,
|
|
59
81
|
sampleRequest,
|
|
60
|
-
isSampled
|
|
82
|
+
isSampled,
|
|
83
|
+
computeKey
|
|
61
84
|
}
|
|
@@ -15,6 +15,7 @@ module.exports = {
|
|
|
15
15
|
PATH_TRAVERSAL_ANALYZER: require('./path-traversal-analyzer'),
|
|
16
16
|
SQL_INJECTION_ANALYZER: require('./sql-injection-analyzer'),
|
|
17
17
|
SSRF: require('./ssrf-analyzer'),
|
|
18
|
+
TEMPLATE_INJECTION_ANALYZER: require('./template-injection-analyzer'),
|
|
18
19
|
UNVALIDATED_REDIRECT_ANALYZER: require('./unvalidated-redirect-analyzer'),
|
|
19
20
|
WEAK_CIPHER_ANALYZER: require('./weak-cipher-analyzer'),
|
|
20
21
|
WEAK_HASH_ANALYZER: require('./weak-hash-analyzer'),
|
|
@@ -6,7 +6,6 @@ const { getNodeModulesPaths } = require('../path-line')
|
|
|
6
6
|
const { HEADER_NAME_VALUE_SEPARATOR } = require('../vulnerabilities-formatter/constants')
|
|
7
7
|
const { getRanges } = require('../taint-tracking/operations')
|
|
8
8
|
const {
|
|
9
|
-
HTTP_REQUEST_COOKIE_NAME,
|
|
10
9
|
HTTP_REQUEST_COOKIE_VALUE,
|
|
11
10
|
HTTP_REQUEST_HEADER_VALUE
|
|
12
11
|
} = require('../taint-tracking/source-types')
|
|
@@ -45,13 +44,7 @@ class HeaderInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
45
44
|
if (this.isExcludedHeaderName(lowerCasedHeaderName) || typeof value !== 'string') return
|
|
46
45
|
|
|
47
46
|
const ranges = getRanges(iastContext, value)
|
|
48
|
-
|
|
49
|
-
return !(this.isCookieExclusion(lowerCasedHeaderName, ranges) ||
|
|
50
|
-
this.isSameHeaderExclusion(lowerCasedHeaderName, ranges) ||
|
|
51
|
-
this.isAccessControlAllowExclusion(lowerCasedHeaderName, ranges))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false
|
|
47
|
+
return ranges?.length > 0 && !this.shouldIgnoreHeader(lowerCasedHeaderName, ranges)
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
_getEvidence (headerInfo, iastContext) {
|
|
@@ -75,28 +68,52 @@ class HeaderInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
75
68
|
return EXCLUDED_HEADER_NAMES.includes(name)
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
isAllRangesFromHeader (ranges, headerName) {
|
|
72
|
+
return ranges
|
|
73
|
+
.every(range =>
|
|
74
|
+
range.iinfo.type === HTTP_REQUEST_HEADER_VALUE && range.iinfo.parameterName?.toLowerCase() === headerName
|
|
75
|
+
)
|
|
76
|
+
}
|
|
83
77
|
|
|
84
|
-
|
|
78
|
+
isAllRangesFromSource (ranges, source) {
|
|
79
|
+
return ranges
|
|
80
|
+
.every(range => range.iinfo.type === source)
|
|
85
81
|
}
|
|
86
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Exclude access-control-allow-*: when the header starts with access-control-allow- and the
|
|
85
|
+
* source of the tainted range is a request header
|
|
86
|
+
*/
|
|
87
87
|
isAccessControlAllowExclusion (name, ranges) {
|
|
88
88
|
if (name?.startsWith('access-control-allow-')) {
|
|
89
|
-
return ranges
|
|
90
|
-
.every(range => range.iinfo.type === HTTP_REQUEST_HEADER_VALUE)
|
|
89
|
+
return this.isAllRangesFromSource(ranges, HTTP_REQUEST_HEADER_VALUE)
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
return false
|
|
94
93
|
}
|
|
95
94
|
|
|
95
|
+
/** Exclude when the header is reflected from the request */
|
|
96
96
|
isSameHeaderExclusion (name, ranges) {
|
|
97
97
|
return ranges.length === 1 && name === ranges[0].iinfo.parameterName?.toLowerCase()
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
shouldIgnoreHeader (headerName, ranges) {
|
|
101
|
+
switch (headerName) {
|
|
102
|
+
case 'set-cookie':
|
|
103
|
+
/** Exclude set-cookie header if the source of all the tainted ranges are cookies */
|
|
104
|
+
return this.isAllRangesFromSource(ranges, HTTP_REQUEST_COOKIE_VALUE)
|
|
105
|
+
case 'pragma':
|
|
106
|
+
/** Ignore pragma headers when the source is the cache control header. */
|
|
107
|
+
return this.isAllRangesFromHeader(ranges, 'cache-control')
|
|
108
|
+
case 'transfer-encoding':
|
|
109
|
+
case 'content-encoding':
|
|
110
|
+
/** Ignore transfer and content encoding headers when the source is the accept encoding header. */
|
|
111
|
+
return this.isAllRangesFromHeader(ranges, 'accept-encoding')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return this.isAccessControlAllowExclusion(headerName, ranges) || this.isSameHeaderExclusion(headerName, ranges)
|
|
115
|
+
}
|
|
116
|
+
|
|
100
117
|
_getExcludedPaths () {
|
|
101
118
|
return EXCLUDED_PATHS
|
|
102
119
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
4
|
+
const { TEMPLATE_INJECTION } = require('../vulnerabilities')
|
|
5
|
+
|
|
6
|
+
class TemplateInjectionAnalyzer extends InjectionAnalyzer {
|
|
7
|
+
constructor () {
|
|
8
|
+
super(TEMPLATE_INJECTION)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
onConfigure () {
|
|
12
|
+
this.addSub('datadog:handlebars:compile:start', ({ source }) => this.analyze(source))
|
|
13
|
+
this.addSub('datadog:handlebars:register-partial:start', ({ partial }) => this.analyze(partial))
|
|
14
|
+
this.addSub('datadog:pug:compile:start', ({ source }) => this.analyze(source))
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = new TemplateInjectionAnalyzer()
|
|
@@ -5,13 +5,13 @@ const vulnerabilities = require('../../vulnerabilities')
|
|
|
5
5
|
|
|
6
6
|
const { contains, intersects, remove } = require('./range-utils')
|
|
7
7
|
|
|
8
|
-
const codeInjectionSensitiveAnalyzer = require('./sensitive-analyzers/code-injection-sensitive-analyzer')
|
|
9
8
|
const commandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')
|
|
10
9
|
const hardcodedPasswordAnalyzer = require('./sensitive-analyzers/hardcoded-password-analyzer')
|
|
11
10
|
const headerSensitiveAnalyzer = require('./sensitive-analyzers/header-sensitive-analyzer')
|
|
12
11
|
const jsonSensitiveAnalyzer = require('./sensitive-analyzers/json-sensitive-analyzer')
|
|
13
12
|
const ldapSensitiveAnalyzer = require('./sensitive-analyzers/ldap-sensitive-analyzer')
|
|
14
13
|
const sqlSensitiveAnalyzer = require('./sensitive-analyzers/sql-sensitive-analyzer')
|
|
14
|
+
const taintedRangeBasedSensitiveAnalyzer = require('./sensitive-analyzers/tainted-range-based-sensitive-analyzer')
|
|
15
15
|
const urlSensitiveAnalyzer = require('./sensitive-analyzers/url-sensitive-analyzer')
|
|
16
16
|
|
|
17
17
|
const { DEFAULT_IAST_REDACTION_NAME_PATTERN, DEFAULT_IAST_REDACTION_VALUE_PATTERN } = require('./sensitive-regex')
|
|
@@ -24,7 +24,8 @@ class SensitiveHandler {
|
|
|
24
24
|
this._valuePattern = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
25
25
|
|
|
26
26
|
this._sensitiveAnalyzers = new Map()
|
|
27
|
-
this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION,
|
|
27
|
+
this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
|
|
28
|
+
this._sensitiveAnalyzers.set(vulnerabilities.TEMPLATE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
|
|
28
29
|
this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, commandSensitiveAnalyzer)
|
|
29
30
|
this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)
|
|
30
31
|
this._sensitiveAnalyzers.set(vulnerabilities.LDAP_INJECTION, ldapSensitiveAnalyzer)
|
|
@@ -145,10 +145,6 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
|
145
145
|
persistent[addresses.HTTP_CLIENT_IP] = clientIp
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
if (apiSecuritySampler.sampleRequest(req)) {
|
|
149
|
-
persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
|
|
150
|
-
}
|
|
151
|
-
|
|
152
148
|
const actions = waf.run({ persistent }, req)
|
|
153
149
|
|
|
154
150
|
handleResults(actions, req, res, rootSpan, abortController)
|
|
@@ -172,6 +168,10 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
172
168
|
persistent[addresses.HTTP_INCOMING_QUERY] = req.query
|
|
173
169
|
}
|
|
174
170
|
|
|
171
|
+
if (apiSecuritySampler.sampleRequest(req, res, true)) {
|
|
172
|
+
persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
175
|
if (Object.keys(persistent).length) {
|
|
176
176
|
waf.run({ persistent }, req)
|
|
177
177
|
}
|
|
@@ -228,9 +228,9 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
|
|
|
228
228
|
handleResults(results, req, res, rootSpan, abortController)
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
function onResponseBody ({ req, body }) {
|
|
231
|
+
function onResponseBody ({ req, res, body }) {
|
|
232
232
|
if (!body || typeof body !== 'object') return
|
|
233
|
-
if (!apiSecuritySampler.
|
|
233
|
+
if (!apiSecuritySampler.sampleRequest(req, res)) return
|
|
234
234
|
|
|
235
235
|
// we don't support blocking at this point, so no results needed
|
|
236
236
|
waf.run({
|