dd-trace 5.18.0 → 5.20.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 +0 -2
- package/ext/formats.d.ts +1 -0
- package/ext/formats.js +2 -1
- package/index.d.ts +61 -39
- package/init.js +3 -15
- package/package.json +9 -12
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +1 -1
- package/packages/datadog-instrumentations/src/hapi.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +13 -11
- package/packages/datadog-instrumentations/src/http/client.js +8 -2
- package/packages/datadog-instrumentations/src/http/server.js +50 -13
- package/packages/datadog-instrumentations/src/jest.js +17 -2
- package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +21 -8
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +46 -32
- package/packages/datadog-instrumentations/src/process.js +29 -0
- package/packages/datadog-instrumentations/src/restify.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +98 -28
- package/packages/datadog-plugin-aws-sdk/src/base.js +16 -2
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -48
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +7 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/index.js +5 -5
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +19 -7
- package/packages/dd-trace/src/analytics_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/blocking.js +10 -1
- package/packages/dd-trace/src/appsec/channels.js +4 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +11 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/code-injection-sensitive-analyzer.js +25 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +15 -10
- package/packages/dd-trace/src/appsec/passport.js +1 -1
- package/packages/dd-trace/src/appsec/rasp.js +121 -7
- package/packages/dd-trace/src/appsec/recommended.json +220 -2
- package/packages/dd-trace/src/appsec/reporter.js +0 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/config.js +68 -66
- package/packages/dd-trace/src/data_streams.js +44 -0
- package/packages/dd-trace/src/datastreams/pathway.js +4 -2
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/log/index.js +32 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +43 -0
- package/packages/dd-trace/src/opentracing/tracer.js +10 -6
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -2
- package/packages/dd-trace/src/plugins/plugin.js +12 -1
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/tracer.js +2 -0
|
@@ -5,6 +5,7 @@ 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')
|
|
8
9
|
const commandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')
|
|
9
10
|
const hardcodedPasswordAnalyzer = require('./sensitive-analyzers/hardcoded-password-analyzer')
|
|
10
11
|
const headerSensitiveAnalyzer = require('./sensitive-analyzers/header-sensitive-analyzer')
|
|
@@ -23,6 +24,7 @@ class SensitiveHandler {
|
|
|
23
24
|
this._valuePattern = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
24
25
|
|
|
25
26
|
this._sensitiveAnalyzers = new Map()
|
|
27
|
+
this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, codeInjectionSensitiveAnalyzer)
|
|
26
28
|
this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, commandSensitiveAnalyzer)
|
|
27
29
|
this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)
|
|
28
30
|
this._sensitiveAnalyzers.set(vulnerabilities.LDAP_INJECTION, ldapSensitiveAnalyzer)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// eslint-disable-next-line max-len
|
|
2
|
-
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)'
|
|
2
|
+
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|(?:sur|last)name|user(?:name)?|address|e?mail)'
|
|
3
3
|
// eslint-disable-next-line max-len
|
|
4
|
-
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,}))'
|
|
4
|
+
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,})|[\\w\\.-]+@[a-zA-Z\\d\\.-]+\\.[a-zA-Z]{2,})'
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
DEFAULT_IAST_REDACTION_NAME_PATTERN,
|
|
@@ -43,6 +43,8 @@ class VulnerabilityFormatter {
|
|
|
43
43
|
const valueParts = []
|
|
44
44
|
let fromIndex = 0
|
|
45
45
|
|
|
46
|
+
if (evidence.value == null) return { valueParts }
|
|
47
|
+
|
|
46
48
|
if (typeof evidence.value === 'object' && evidence.rangesToApply) {
|
|
47
49
|
const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply)
|
|
48
50
|
evidence.value = value
|
|
@@ -69,7 +71,7 @@ class VulnerabilityFormatter {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
formatEvidence (type, evidence, sourcesIndexes, sources) {
|
|
72
|
-
if (
|
|
74
|
+
if (evidence.value === undefined) {
|
|
73
75
|
return undefined
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -13,7 +13,8 @@ const {
|
|
|
13
13
|
nextBodyParsed,
|
|
14
14
|
nextQueryParsed,
|
|
15
15
|
responseBody,
|
|
16
|
-
responseWriteHead
|
|
16
|
+
responseWriteHead,
|
|
17
|
+
responseSetHeader
|
|
17
18
|
} = require('./channels')
|
|
18
19
|
const waf = require('./waf')
|
|
19
20
|
const addresses = require('./addresses')
|
|
@@ -23,7 +24,7 @@ const apiSecuritySampler = require('./api_security_sampler')
|
|
|
23
24
|
const web = require('../plugins/util/web')
|
|
24
25
|
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
25
26
|
const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
|
|
26
|
-
const { block, setTemplates, getBlockingAction } = require('./blocking')
|
|
27
|
+
const { isBlocked, block, setTemplates, getBlockingAction } = require('./blocking')
|
|
27
28
|
const { passportTrackEvent } = require('./passport')
|
|
28
29
|
const { storage } = require('../../../datadog-core')
|
|
29
30
|
const graphql = require('./graphql')
|
|
@@ -62,6 +63,7 @@ function enable (_config) {
|
|
|
62
63
|
cookieParser.subscribe(onRequestCookieParser)
|
|
63
64
|
responseBody.subscribe(onResponseBody)
|
|
64
65
|
responseWriteHead.subscribe(onResponseWriteHead)
|
|
66
|
+
responseSetHeader.subscribe(onResponseSetHeader)
|
|
65
67
|
|
|
66
68
|
if (_config.appsec.eventTracking.enabled) {
|
|
67
69
|
passportVerify.subscribe(onPassportVerify)
|
|
@@ -121,16 +123,16 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
// TODO: temporary express instrumentation, will use express plugin later
|
|
124
|
-
if (req.params && typeof req.params === 'object') {
|
|
126
|
+
if (req.params !== null && typeof req.params === 'object') {
|
|
125
127
|
persistent[addresses.HTTP_INCOMING_PARAMS] = req.params
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// we need to keep this to support other cookie parsers
|
|
129
|
-
if (req.cookies && typeof req.cookies === 'object') {
|
|
131
|
+
if (req.cookies !== null && typeof req.cookies === 'object') {
|
|
130
132
|
persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
if (req.query && typeof req.query === 'object') {
|
|
135
|
+
if (req.query !== null && typeof req.query === 'object') {
|
|
134
136
|
persistent[addresses.HTTP_INCOMING_QUERY] = req.query
|
|
135
137
|
}
|
|
136
138
|
|
|
@@ -223,11 +225,10 @@ function onPassportVerify ({ credentials, user }) {
|
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
const responseAnalyzedSet = new WeakSet()
|
|
226
|
-
const responseBlockedSet = new WeakSet()
|
|
227
228
|
|
|
228
229
|
function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
|
|
229
230
|
// avoid "write after end" error
|
|
230
|
-
if (
|
|
231
|
+
if (isBlocked(res)) {
|
|
231
232
|
abortController?.abort()
|
|
232
233
|
return
|
|
233
234
|
}
|
|
@@ -255,15 +256,18 @@ function onResponseWriteHead ({ req, res, abortController, statusCode, responseH
|
|
|
255
256
|
handleResults(results, req, res, rootSpan, abortController)
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
function onResponseSetHeader ({ res, abortController }) {
|
|
260
|
+
if (isBlocked(res)) {
|
|
261
|
+
abortController?.abort()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
258
265
|
function handleResults (actions, req, res, rootSpan, abortController) {
|
|
259
266
|
if (!actions || !req || !res || !rootSpan || !abortController) return
|
|
260
267
|
|
|
261
268
|
const blockingAction = getBlockingAction(actions)
|
|
262
269
|
if (blockingAction) {
|
|
263
270
|
block(req, res, rootSpan, abortController, blockingAction)
|
|
264
|
-
if (!abortController.signal || abortController.signal.aborted) {
|
|
265
|
-
responseBlockedSet.add(res)
|
|
266
|
-
}
|
|
267
271
|
}
|
|
268
272
|
}
|
|
269
273
|
|
|
@@ -290,6 +294,7 @@ function disable () {
|
|
|
290
294
|
if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
|
|
291
295
|
if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify)
|
|
292
296
|
if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
|
|
297
|
+
if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
module.exports = {
|
|
@@ -13,7 +13,7 @@ const regexSdkEvent = new RegExp(SDK_USER_EVENT_PATTERN, 'i')
|
|
|
13
13
|
function isSdkCalled (tags) {
|
|
14
14
|
let called = false
|
|
15
15
|
|
|
16
|
-
if (tags && typeof tags === 'object') {
|
|
16
|
+
if (tags !== null && typeof tags === 'object') {
|
|
17
17
|
called = Object.entries(tags).some(([key, value]) => regexSdkEvent.test(key) && value === 'true')
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -3,23 +3,118 @@
|
|
|
3
3
|
const { storage } = require('../../../datadog-core')
|
|
4
4
|
const web = require('./../plugins/util/web')
|
|
5
5
|
const addresses = require('./addresses')
|
|
6
|
-
const { httpClientRequestStart } = require('./channels')
|
|
6
|
+
const { httpClientRequestStart, setUncaughtExceptionCaptureCallbackStart } = require('./channels')
|
|
7
7
|
const { reportStackTrace } = require('./stack_trace')
|
|
8
8
|
const waf = require('./waf')
|
|
9
|
+
const { getBlockingAction, block } = require('./blocking')
|
|
10
|
+
const log = require('../log')
|
|
9
11
|
|
|
10
12
|
const RULE_TYPES = {
|
|
11
13
|
SSRF: 'ssrf'
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
class DatadogRaspAbortError extends Error {
|
|
17
|
+
constructor (req, res, blockingAction) {
|
|
18
|
+
super('DatadogRaspAbortError')
|
|
19
|
+
this.name = 'DatadogRaspAbortError'
|
|
20
|
+
this.req = req
|
|
21
|
+
this.res = res
|
|
22
|
+
this.blockingAction = blockingAction
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let config, abortOnUncaughtException
|
|
27
|
+
|
|
28
|
+
function removeAllListeners (emitter, event) {
|
|
29
|
+
const listeners = emitter.listeners(event)
|
|
30
|
+
emitter.removeAllListeners(event)
|
|
31
|
+
|
|
32
|
+
let cleaned = false
|
|
33
|
+
return function () {
|
|
34
|
+
if (cleaned === true) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
cleaned = true
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < listeners.length; ++i) {
|
|
40
|
+
emitter.on(event, listeners[i])
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function findDatadogRaspAbortError (err, deep = 10) {
|
|
46
|
+
if (err instanceof DatadogRaspAbortError) {
|
|
47
|
+
return err
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (err.cause && deep > 0) {
|
|
51
|
+
return findDatadogRaspAbortError(err.cause, deep - 1)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleUncaughtExceptionMonitor (err) {
|
|
56
|
+
const abortError = findDatadogRaspAbortError(err)
|
|
57
|
+
if (!abortError) return
|
|
58
|
+
|
|
59
|
+
const { req, res, blockingAction } = abortError
|
|
60
|
+
block(req, res, web.root(req), null, blockingAction)
|
|
61
|
+
|
|
62
|
+
if (!process.hasUncaughtExceptionCaptureCallback()) {
|
|
63
|
+
const cleanUp = removeAllListeners(process, 'uncaughtException')
|
|
64
|
+
const handler = () => {
|
|
65
|
+
process.removeListener('uncaughtException', handler)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
process.removeListener('uncaughtException', handler)
|
|
70
|
+
cleanUp()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
process.on('uncaughtException', handler)
|
|
74
|
+
} else {
|
|
75
|
+
// uncaughtException event is not executed when hasUncaughtExceptionCaptureCallback is true
|
|
76
|
+
let previousCb
|
|
77
|
+
const cb = ({ currentCallback, abortController }) => {
|
|
78
|
+
setUncaughtExceptionCaptureCallbackStart.unsubscribe(cb)
|
|
79
|
+
if (!currentCallback) {
|
|
80
|
+
abortController.abort()
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
previousCb = currentCallback
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setUncaughtExceptionCaptureCallbackStart.subscribe(cb)
|
|
88
|
+
|
|
89
|
+
process.setUncaughtExceptionCaptureCallback(null)
|
|
90
|
+
|
|
91
|
+
// For some reason, previous callback was defined before the instrumentation
|
|
92
|
+
// We can not restore it, so we let the app decide
|
|
93
|
+
if (previousCb) {
|
|
94
|
+
process.setUncaughtExceptionCaptureCallback(() => {
|
|
95
|
+
process.setUncaughtExceptionCaptureCallback(null)
|
|
96
|
+
process.setUncaughtExceptionCaptureCallback(previousCb)
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
15
101
|
|
|
16
102
|
function enable (_config) {
|
|
17
103
|
config = _config
|
|
18
104
|
httpClientRequestStart.subscribe(analyzeSsrf)
|
|
105
|
+
|
|
106
|
+
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
107
|
+
abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
|
|
108
|
+
|
|
109
|
+
if (abortOnUncaughtException) {
|
|
110
|
+
log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
|
|
111
|
+
}
|
|
19
112
|
}
|
|
20
113
|
|
|
21
114
|
function disable () {
|
|
22
115
|
if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
|
|
116
|
+
|
|
117
|
+
process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
23
118
|
}
|
|
24
119
|
|
|
25
120
|
function analyzeSsrf (ctx) {
|
|
@@ -32,17 +127,18 @@ function analyzeSsrf (ctx) {
|
|
|
32
127
|
const persistent = {
|
|
33
128
|
[addresses.HTTP_OUTGOING_URL]: url
|
|
34
129
|
}
|
|
35
|
-
|
|
36
|
-
// block the request if SSRF attempt
|
|
130
|
+
|
|
37
131
|
const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
|
|
38
|
-
|
|
132
|
+
|
|
133
|
+
const res = store?.res
|
|
134
|
+
handleResult(result, req, res, ctx.abortController)
|
|
39
135
|
}
|
|
40
136
|
|
|
41
137
|
function getGenerateStackTraceAction (actions) {
|
|
42
138
|
return actions?.generate_stack
|
|
43
139
|
}
|
|
44
140
|
|
|
45
|
-
function handleResult (actions, req) {
|
|
141
|
+
function handleResult (actions, req, res, abortController) {
|
|
46
142
|
const generateStackTraceAction = getGenerateStackTraceAction(actions)
|
|
47
143
|
if (generateStackTraceAction && config.appsec.stackTrace.enabled) {
|
|
48
144
|
const rootSpan = web.root(req)
|
|
@@ -53,10 +149,28 @@ function handleResult (actions, req) {
|
|
|
53
149
|
config.appsec.stackTrace.maxStackTraces
|
|
54
150
|
)
|
|
55
151
|
}
|
|
152
|
+
|
|
153
|
+
if (!abortController || abortOnUncaughtException) return
|
|
154
|
+
|
|
155
|
+
const blockingAction = getBlockingAction(actions)
|
|
156
|
+
if (blockingAction) {
|
|
157
|
+
const rootSpan = web.root(req)
|
|
158
|
+
// Should block only in express
|
|
159
|
+
if (rootSpan?.context()._name === 'express.request') {
|
|
160
|
+
const abortError = new DatadogRaspAbortError(req, res, blockingAction)
|
|
161
|
+
abortController.abort(abortError)
|
|
162
|
+
|
|
163
|
+
// TODO Delete this when support for node 16 is removed
|
|
164
|
+
if (!abortController.signal.reason) {
|
|
165
|
+
abortController.signal.reason = abortError
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
56
169
|
}
|
|
57
170
|
|
|
58
171
|
module.exports = {
|
|
59
172
|
enable,
|
|
60
173
|
disable,
|
|
61
|
-
handleResult
|
|
174
|
+
handleResult,
|
|
175
|
+
handleUncaughtExceptionMonitor // exported only for testing purpose
|
|
62
176
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "2.2",
|
|
3
3
|
"metadata": {
|
|
4
|
-
"rules_version": "1.
|
|
4
|
+
"rules_version": "1.13.0"
|
|
5
5
|
},
|
|
6
6
|
"rules": [
|
|
7
7
|
{
|
|
@@ -6285,6 +6285,55 @@
|
|
|
6285
6285
|
"stack_trace"
|
|
6286
6286
|
]
|
|
6287
6287
|
},
|
|
6288
|
+
{
|
|
6289
|
+
"id": "rasp-932-100",
|
|
6290
|
+
"name": "Shell injection exploit",
|
|
6291
|
+
"enabled": false,
|
|
6292
|
+
"tags": {
|
|
6293
|
+
"type": "command_injection",
|
|
6294
|
+
"category": "vulnerability_trigger",
|
|
6295
|
+
"cwe": "77",
|
|
6296
|
+
"capec": "1000/152/248/88",
|
|
6297
|
+
"confidence": "0",
|
|
6298
|
+
"module": "rasp"
|
|
6299
|
+
},
|
|
6300
|
+
"conditions": [
|
|
6301
|
+
{
|
|
6302
|
+
"parameters": {
|
|
6303
|
+
"resource": [
|
|
6304
|
+
{
|
|
6305
|
+
"address": "server.sys.shell.cmd"
|
|
6306
|
+
}
|
|
6307
|
+
],
|
|
6308
|
+
"params": [
|
|
6309
|
+
{
|
|
6310
|
+
"address": "server.request.query"
|
|
6311
|
+
},
|
|
6312
|
+
{
|
|
6313
|
+
"address": "server.request.body"
|
|
6314
|
+
},
|
|
6315
|
+
{
|
|
6316
|
+
"address": "server.request.path_params"
|
|
6317
|
+
},
|
|
6318
|
+
{
|
|
6319
|
+
"address": "grpc.server.request.message"
|
|
6320
|
+
},
|
|
6321
|
+
{
|
|
6322
|
+
"address": "graphql.server.all_resolvers"
|
|
6323
|
+
},
|
|
6324
|
+
{
|
|
6325
|
+
"address": "graphql.server.resolver"
|
|
6326
|
+
}
|
|
6327
|
+
]
|
|
6328
|
+
},
|
|
6329
|
+
"operator": "shi_detector"
|
|
6330
|
+
}
|
|
6331
|
+
],
|
|
6332
|
+
"transformers": [],
|
|
6333
|
+
"on_match": [
|
|
6334
|
+
"stack_trace"
|
|
6335
|
+
]
|
|
6336
|
+
},
|
|
6288
6337
|
{
|
|
6289
6338
|
"id": "rasp-934-100",
|
|
6290
6339
|
"name": "Server-side request forgery exploit",
|
|
@@ -8388,6 +8437,57 @@
|
|
|
8388
8437
|
}
|
|
8389
8438
|
],
|
|
8390
8439
|
"processors": [
|
|
8440
|
+
{
|
|
8441
|
+
"id": "http-endpoint-fingerprint",
|
|
8442
|
+
"generator": "http_endpoint_fingerprint",
|
|
8443
|
+
"conditions": [
|
|
8444
|
+
{
|
|
8445
|
+
"operator": "exists",
|
|
8446
|
+
"parameters": {
|
|
8447
|
+
"inputs": [
|
|
8448
|
+
{
|
|
8449
|
+
"address": "waf.context.event"
|
|
8450
|
+
},
|
|
8451
|
+
{
|
|
8452
|
+
"address": "server.business_logic.users.login.failure"
|
|
8453
|
+
},
|
|
8454
|
+
{
|
|
8455
|
+
"address": "server.business_logic.users.login.success"
|
|
8456
|
+
}
|
|
8457
|
+
]
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
],
|
|
8461
|
+
"parameters": {
|
|
8462
|
+
"mappings": [
|
|
8463
|
+
{
|
|
8464
|
+
"method": [
|
|
8465
|
+
{
|
|
8466
|
+
"address": "server.request.method"
|
|
8467
|
+
}
|
|
8468
|
+
],
|
|
8469
|
+
"uri_raw": [
|
|
8470
|
+
{
|
|
8471
|
+
"address": "server.request.uri.raw"
|
|
8472
|
+
}
|
|
8473
|
+
],
|
|
8474
|
+
"body": [
|
|
8475
|
+
{
|
|
8476
|
+
"address": "server.request.body"
|
|
8477
|
+
}
|
|
8478
|
+
],
|
|
8479
|
+
"query": [
|
|
8480
|
+
{
|
|
8481
|
+
"address": "server.request.query"
|
|
8482
|
+
}
|
|
8483
|
+
],
|
|
8484
|
+
"output": "_dd.appsec.fp.http.endpoint"
|
|
8485
|
+
}
|
|
8486
|
+
]
|
|
8487
|
+
},
|
|
8488
|
+
"evaluate": false,
|
|
8489
|
+
"output": true
|
|
8490
|
+
},
|
|
8391
8491
|
{
|
|
8392
8492
|
"id": "extract-content",
|
|
8393
8493
|
"generator": "extract_schema",
|
|
@@ -8537,6 +8637,124 @@
|
|
|
8537
8637
|
},
|
|
8538
8638
|
"evaluate": false,
|
|
8539
8639
|
"output": true
|
|
8640
|
+
},
|
|
8641
|
+
{
|
|
8642
|
+
"id": "http-header-fingerprint",
|
|
8643
|
+
"generator": "http_header_fingerprint",
|
|
8644
|
+
"conditions": [
|
|
8645
|
+
{
|
|
8646
|
+
"operator": "exists",
|
|
8647
|
+
"parameters": {
|
|
8648
|
+
"inputs": [
|
|
8649
|
+
{
|
|
8650
|
+
"address": "waf.context.event"
|
|
8651
|
+
},
|
|
8652
|
+
{
|
|
8653
|
+
"address": "server.business_logic.users.login.failure"
|
|
8654
|
+
},
|
|
8655
|
+
{
|
|
8656
|
+
"address": "server.business_logic.users.login.success"
|
|
8657
|
+
}
|
|
8658
|
+
]
|
|
8659
|
+
}
|
|
8660
|
+
}
|
|
8661
|
+
],
|
|
8662
|
+
"parameters": {
|
|
8663
|
+
"mappings": [
|
|
8664
|
+
{
|
|
8665
|
+
"headers": [
|
|
8666
|
+
{
|
|
8667
|
+
"address": "server.request.headers.no_cookies"
|
|
8668
|
+
}
|
|
8669
|
+
],
|
|
8670
|
+
"output": "_dd.appsec.fp.http.header"
|
|
8671
|
+
}
|
|
8672
|
+
]
|
|
8673
|
+
},
|
|
8674
|
+
"evaluate": false,
|
|
8675
|
+
"output": true
|
|
8676
|
+
},
|
|
8677
|
+
{
|
|
8678
|
+
"id": "http-network-fingerprint",
|
|
8679
|
+
"generator": "http_network_fingerprint",
|
|
8680
|
+
"conditions": [
|
|
8681
|
+
{
|
|
8682
|
+
"operator": "exists",
|
|
8683
|
+
"parameters": {
|
|
8684
|
+
"inputs": [
|
|
8685
|
+
{
|
|
8686
|
+
"address": "waf.context.event"
|
|
8687
|
+
},
|
|
8688
|
+
{
|
|
8689
|
+
"address": "server.business_logic.users.login.failure"
|
|
8690
|
+
},
|
|
8691
|
+
{
|
|
8692
|
+
"address": "server.business_logic.users.login.success"
|
|
8693
|
+
}
|
|
8694
|
+
]
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
],
|
|
8698
|
+
"parameters": {
|
|
8699
|
+
"mappings": [
|
|
8700
|
+
{
|
|
8701
|
+
"headers": [
|
|
8702
|
+
{
|
|
8703
|
+
"address": "server.request.headers.no_cookies"
|
|
8704
|
+
}
|
|
8705
|
+
],
|
|
8706
|
+
"output": "_dd.appsec.fp.http.network"
|
|
8707
|
+
}
|
|
8708
|
+
]
|
|
8709
|
+
},
|
|
8710
|
+
"evaluate": false,
|
|
8711
|
+
"output": true
|
|
8712
|
+
},
|
|
8713
|
+
{
|
|
8714
|
+
"id": "session-fingerprint",
|
|
8715
|
+
"generator": "session_fingerprint",
|
|
8716
|
+
"conditions": [
|
|
8717
|
+
{
|
|
8718
|
+
"operator": "exists",
|
|
8719
|
+
"parameters": {
|
|
8720
|
+
"inputs": [
|
|
8721
|
+
{
|
|
8722
|
+
"address": "waf.context.event"
|
|
8723
|
+
},
|
|
8724
|
+
{
|
|
8725
|
+
"address": "server.business_logic.users.login.failure"
|
|
8726
|
+
},
|
|
8727
|
+
{
|
|
8728
|
+
"address": "server.business_logic.users.login.success"
|
|
8729
|
+
}
|
|
8730
|
+
]
|
|
8731
|
+
}
|
|
8732
|
+
}
|
|
8733
|
+
],
|
|
8734
|
+
"parameters": {
|
|
8735
|
+
"mappings": [
|
|
8736
|
+
{
|
|
8737
|
+
"cookies": [
|
|
8738
|
+
{
|
|
8739
|
+
"address": "server.request.cookies"
|
|
8740
|
+
}
|
|
8741
|
+
],
|
|
8742
|
+
"session_id": [
|
|
8743
|
+
{
|
|
8744
|
+
"address": "usr.session_id"
|
|
8745
|
+
}
|
|
8746
|
+
],
|
|
8747
|
+
"user_id": [
|
|
8748
|
+
{
|
|
8749
|
+
"address": "usr.id"
|
|
8750
|
+
}
|
|
8751
|
+
],
|
|
8752
|
+
"output": "_dd.appsec.fp.session"
|
|
8753
|
+
}
|
|
8754
|
+
]
|
|
8755
|
+
},
|
|
8756
|
+
"evaluate": false,
|
|
8757
|
+
"output": true
|
|
8540
8758
|
}
|
|
8541
8759
|
],
|
|
8542
8760
|
"scanners": [
|
|
@@ -9562,4 +9780,4 @@
|
|
|
9562
9780
|
}
|
|
9563
9781
|
}
|
|
9564
9782
|
]
|
|
9565
|
-
}
|
|
9783
|
+
}
|
|
@@ -207,10 +207,6 @@ function finishRequest (req, res) {
|
|
|
207
207
|
|
|
208
208
|
// collect some headers even when no attack is detected
|
|
209
209
|
const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
|
|
210
|
-
const ua = mandatoryTags['http.request.headers.user-agent']
|
|
211
|
-
if (ua) {
|
|
212
|
-
mandatoryTags['http.useragent'] = ua
|
|
213
|
-
}
|
|
214
210
|
rootSpan.addTags(mandatoryTags)
|
|
215
211
|
|
|
216
212
|
const tags = rootSpan.context()._tags
|
|
@@ -25,7 +25,7 @@ class WAFContextWrapper {
|
|
|
25
25
|
const inputs = {}
|
|
26
26
|
const newAddressesToSkip = new Set(this.addressesToSkip)
|
|
27
27
|
|
|
28
|
-
if (persistent && typeof persistent === 'object') {
|
|
28
|
+
if (persistent !== null && typeof persistent === 'object') {
|
|
29
29
|
// TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
|
|
30
30
|
for (const key of Object.keys(persistent)) {
|
|
31
31
|
// TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
|