dd-trace 4.47.1 → 4.48.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 -1
- package/ext/types.d.ts +1 -0
- package/ext/types.js +1 -0
- package/index.d.ts +26 -0
- package/package.json +6 -7
- package/packages/datadog-code-origin/index.js +38 -0
- package/packages/datadog-core/index.js +2 -2
- package/packages/datadog-instrumentations/src/avsc.js +37 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/child_process.js +17 -8
- package/packages/datadog-instrumentations/src/express.js +37 -4
- package/packages/datadog-instrumentations/src/fastify.js +12 -1
- package/packages/datadog-instrumentations/src/fs.js +27 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/jest.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +220 -1
- package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
- package/packages/datadog-instrumentations/src/winston.js +22 -0
- package/packages/datadog-plugin-avsc/src/index.js +9 -0
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +169 -0
- package/packages/datadog-plugin-azure-functions/src/index.js +77 -0
- package/packages/datadog-plugin-fastify/src/code_origin.js +31 -0
- package/packages/datadog-plugin-fastify/src/index.js +10 -12
- package/packages/datadog-plugin-fastify/src/tracing.js +19 -0
- package/packages/datadog-plugin-protobufjs/src/index.js +14 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +180 -0
- package/packages/dd-trace/src/appsec/addresses.js +6 -1
- package/packages/dd-trace/src/appsec/channels.js +5 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +13 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +8 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +3 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
- package/packages/dd-trace/src/appsec/index.js +58 -43
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
- package/packages/dd-trace/src/appsec/rasp/index.js +24 -10
- package/packages/dd-trace/src/appsec/rasp/lfi.js +112 -0
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +24 -4
- package/packages/dd-trace/src/appsec/rasp/utils.js +2 -1
- package/packages/dd-trace/src/appsec/recommended.json +2 -4
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +5 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +8 -0
- package/packages/dd-trace/src/appsec/reporter.js +12 -5
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -0
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
- package/packages/dd-trace/src/config.js +12 -1
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +25 -17
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +56 -5
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +153 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +30 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +241 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +10 -4
- package/packages/dd-trace/src/exporters/common/request.js +8 -34
- package/packages/dd-trace/src/exporters/common/url-to-http-options-polyfill.js +31 -0
- package/packages/dd-trace/src/payload-tagging/index.js +1 -1
- package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +2094 -0
- package/packages/dd-trace/src/plugin_manager.js +4 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/schema.js +35 -0
- package/packages/dd-trace/src/plugins/util/ci.js +23 -1
- package/packages/dd-trace/src/plugins/util/serverless.js +7 -0
- package/packages/dd-trace/src/plugins/util/stacktrace.js +94 -0
- package/packages/dd-trace/src/plugins/util/tags.js +7 -0
- package/packages/dd-trace/src/plugins/util/test.js +20 -22
- package/packages/dd-trace/src/plugins/util/web.js +6 -4
- package/packages/dd-trace/src/profiling/profiler.js +24 -14
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +94 -66
- package/packages/dd-trace/src/proxy.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
- package/packages/datadog-core/src/storage/async_resource.js +0 -108
- package/packages/datadog-core/src/storage/index.js +0 -5
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const web = require('../../plugins/util/web')
|
|
4
|
-
const { setUncaughtExceptionCaptureCallbackStart } = require('../channels')
|
|
5
|
-
const { block } = require('../blocking')
|
|
4
|
+
const { setUncaughtExceptionCaptureCallbackStart, expressMiddlewareError } = require('../channels')
|
|
5
|
+
const { block, isBlocked } = require('../blocking')
|
|
6
6
|
const ssrf = require('./ssrf')
|
|
7
7
|
const sqli = require('./sql_injection')
|
|
8
|
+
const lfi = require('./lfi')
|
|
8
9
|
|
|
9
10
|
const { DatadogRaspAbortError } = require('./utils')
|
|
10
11
|
|
|
@@ -30,17 +31,13 @@ function findDatadogRaspAbortError (err, deep = 10) {
|
|
|
30
31
|
return err
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
if (err
|
|
34
|
+
if (err?.cause && deep > 0) {
|
|
34
35
|
return findDatadogRaspAbortError(err.cause, deep - 1)
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function handleUncaughtExceptionMonitor (
|
|
39
|
-
|
|
40
|
-
if (!abortError) return
|
|
41
|
-
|
|
42
|
-
const { req, res, blockingAction } = abortError
|
|
43
|
-
block(req, res, web.root(req), null, blockingAction)
|
|
39
|
+
function handleUncaughtExceptionMonitor (error) {
|
|
40
|
+
if (!blockOnDatadogRaspAbortError({ error })) return
|
|
44
41
|
|
|
45
42
|
if (!process.hasUncaughtExceptionCaptureCallback()) {
|
|
46
43
|
const cleanUp = removeAllListeners(process, 'uncaughtException')
|
|
@@ -82,22 +79,39 @@ function handleUncaughtExceptionMonitor (err) {
|
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
function blockOnDatadogRaspAbortError ({ error }) {
|
|
83
|
+
const abortError = findDatadogRaspAbortError(error)
|
|
84
|
+
if (!abortError) return false
|
|
85
|
+
|
|
86
|
+
const { req, res, blockingAction } = abortError
|
|
87
|
+
if (!isBlocked(res)) {
|
|
88
|
+
block(req, res, web.root(req), null, blockingAction)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
function enable (config) {
|
|
86
95
|
ssrf.enable(config)
|
|
87
96
|
sqli.enable(config)
|
|
97
|
+
lfi.enable(config)
|
|
88
98
|
|
|
89
99
|
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
100
|
+
expressMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
function disable () {
|
|
93
104
|
ssrf.disable()
|
|
94
105
|
sqli.disable()
|
|
106
|
+
lfi.disable()
|
|
95
107
|
|
|
96
108
|
process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
109
|
+
if (expressMiddlewareError.hasSubscribers) expressMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
module.exports = {
|
|
100
113
|
enable,
|
|
101
114
|
disable,
|
|
102
|
-
handleUncaughtExceptionMonitor // exported only for testing purpose
|
|
115
|
+
handleUncaughtExceptionMonitor, // exported only for testing purpose
|
|
116
|
+
blockOnDatadogRaspAbortError // exported only for testing purpose
|
|
103
117
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { fsOperationStart, incomingHttpRequestStart } = require('../channels')
|
|
4
|
+
const { storage } = require('../../../../datadog-core')
|
|
5
|
+
const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin')
|
|
6
|
+
const { FS_OPERATION_PATH } = require('../addresses')
|
|
7
|
+
const waf = require('../waf')
|
|
8
|
+
const { RULE_TYPES, handleResult } = require('./utils')
|
|
9
|
+
const { isAbsolute } = require('path')
|
|
10
|
+
|
|
11
|
+
let config
|
|
12
|
+
let enabled
|
|
13
|
+
let analyzeSubscribed
|
|
14
|
+
|
|
15
|
+
function enable (_config) {
|
|
16
|
+
config = _config
|
|
17
|
+
|
|
18
|
+
if (enabled) return
|
|
19
|
+
|
|
20
|
+
enabled = true
|
|
21
|
+
|
|
22
|
+
incomingHttpRequestStart.subscribe(onFirstReceivedRequest)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function disable () {
|
|
26
|
+
if (fsOperationStart.hasSubscribers) fsOperationStart.unsubscribe(analyzeLfi)
|
|
27
|
+
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
|
|
28
|
+
|
|
29
|
+
disableFsPlugin(RASP_MODULE)
|
|
30
|
+
|
|
31
|
+
enabled = false
|
|
32
|
+
analyzeSubscribed = false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onFirstReceivedRequest () {
|
|
36
|
+
// nodejs unsubscribe during publish bug: https://github.com/nodejs/node/pull/55116
|
|
37
|
+
process.nextTick(() => {
|
|
38
|
+
incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
enableFsPlugin(RASP_MODULE)
|
|
42
|
+
|
|
43
|
+
if (!analyzeSubscribed) {
|
|
44
|
+
fsOperationStart.subscribe(analyzeLfi)
|
|
45
|
+
analyzeSubscribed = true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function analyzeLfi (ctx) {
|
|
50
|
+
const store = storage.getStore()
|
|
51
|
+
if (!store) return
|
|
52
|
+
|
|
53
|
+
const { req, fs, res } = store
|
|
54
|
+
if (!req || !fs) return
|
|
55
|
+
|
|
56
|
+
getPaths(ctx, fs).forEach(path => {
|
|
57
|
+
const persistent = {
|
|
58
|
+
[FS_OPERATION_PATH]: path
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = waf.run({ persistent }, req, RULE_TYPES.LFI)
|
|
62
|
+
handleResult(result, req, res, ctx.abortController, config)
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getPaths (ctx, fs) {
|
|
67
|
+
// these properties could have String, Buffer, URL, Integer or FileHandle types
|
|
68
|
+
const pathArguments = [
|
|
69
|
+
ctx.dest,
|
|
70
|
+
ctx.existingPath,
|
|
71
|
+
ctx.file,
|
|
72
|
+
ctx.newPath,
|
|
73
|
+
ctx.oldPath,
|
|
74
|
+
ctx.path,
|
|
75
|
+
ctx.prefix,
|
|
76
|
+
ctx.src,
|
|
77
|
+
ctx.target
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
return pathArguments
|
|
81
|
+
.map(path => pathToStr(path))
|
|
82
|
+
.filter(path => shouldAnalyze(path, fs))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function pathToStr (path) {
|
|
86
|
+
if (!path) return
|
|
87
|
+
|
|
88
|
+
if (typeof path === 'string' ||
|
|
89
|
+
path instanceof String ||
|
|
90
|
+
path instanceof Buffer ||
|
|
91
|
+
path instanceof URL) {
|
|
92
|
+
return path.toString()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shouldAnalyze (path, fs) {
|
|
97
|
+
if (!path) return
|
|
98
|
+
|
|
99
|
+
const notExcludedRootOp = !fs.opExcluded && fs.root
|
|
100
|
+
return notExcludedRootOp && (isAbsolute(path) || path.includes('../') || shouldAnalyzeURLFile(path, fs))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function shouldAnalyzeURLFile (path, fs) {
|
|
104
|
+
if (path.startsWith('file://')) {
|
|
105
|
+
return shouldAnalyze(path.substring(7), fs)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
enable,
|
|
111
|
+
disable
|
|
112
|
+
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
pgQueryStart,
|
|
5
|
+
pgPoolQueryStart,
|
|
6
|
+
wafRunFinished,
|
|
7
|
+
mysql2OuterQueryStart
|
|
8
|
+
} = require('../channels')
|
|
4
9
|
const { storage } = require('../../../../datadog-core')
|
|
5
10
|
const addresses = require('../addresses')
|
|
6
11
|
const waf = require('../waf')
|
|
7
12
|
const { RULE_TYPES, handleResult } = require('./utils')
|
|
8
13
|
|
|
9
14
|
const DB_SYSTEM_POSTGRES = 'postgresql'
|
|
15
|
+
const DB_SYSTEM_MYSQL = 'mysql'
|
|
10
16
|
const reqQueryMap = new WeakMap() // WeakMap<Request, Set<querytext>>
|
|
11
17
|
|
|
12
18
|
let config
|
|
@@ -17,18 +23,32 @@ function enable (_config) {
|
|
|
17
23
|
pgQueryStart.subscribe(analyzePgSqlInjection)
|
|
18
24
|
pgPoolQueryStart.subscribe(analyzePgSqlInjection)
|
|
19
25
|
wafRunFinished.subscribe(clearQuerySet)
|
|
26
|
+
|
|
27
|
+
mysql2OuterQueryStart.subscribe(analyzeMysql2SqlInjection)
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
function disable () {
|
|
23
31
|
if (pgQueryStart.hasSubscribers) pgQueryStart.unsubscribe(analyzePgSqlInjection)
|
|
24
32
|
if (pgPoolQueryStart.hasSubscribers) pgPoolQueryStart.unsubscribe(analyzePgSqlInjection)
|
|
25
33
|
if (wafRunFinished.hasSubscribers) wafRunFinished.unsubscribe(clearQuerySet)
|
|
34
|
+
if (mysql2OuterQueryStart.hasSubscribers) mysql2OuterQueryStart.unsubscribe(analyzeMysql2SqlInjection)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function analyzeMysql2SqlInjection (ctx) {
|
|
38
|
+
const query = ctx.sql
|
|
39
|
+
if (!query) return
|
|
40
|
+
|
|
41
|
+
analyzeSqlInjection(query, DB_SYSTEM_MYSQL, ctx.abortController)
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
function analyzePgSqlInjection (ctx) {
|
|
29
45
|
const query = ctx.query?.text
|
|
30
46
|
if (!query) return
|
|
31
47
|
|
|
48
|
+
analyzeSqlInjection(query, DB_SYSTEM_POSTGRES, ctx.abortController)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function analyzeSqlInjection (query, dbSystem, abortController) {
|
|
32
52
|
const store = storage.getStore()
|
|
33
53
|
if (!store) return
|
|
34
54
|
|
|
@@ -39,7 +59,7 @@ function analyzePgSqlInjection (ctx) {
|
|
|
39
59
|
let executedQueries = reqQueryMap.get(req)
|
|
40
60
|
if (executedQueries?.has(query)) return
|
|
41
61
|
|
|
42
|
-
// Do not waste time
|
|
62
|
+
// Do not waste time checking same query twice
|
|
43
63
|
// This also will prevent double calls in pg.Pool internal queries
|
|
44
64
|
if (!executedQueries) {
|
|
45
65
|
executedQueries = new Set()
|
|
@@ -49,12 +69,12 @@ function analyzePgSqlInjection (ctx) {
|
|
|
49
69
|
|
|
50
70
|
const persistent = {
|
|
51
71
|
[addresses.DB_STATEMENT]: query,
|
|
52
|
-
[addresses.DB_SYSTEM]:
|
|
72
|
+
[addresses.DB_SYSTEM]: dbSystem
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
const result = waf.run({ persistent }, req, RULE_TYPES.SQL_INJECTION)
|
|
56
76
|
|
|
57
|
-
handleResult(result, req, res,
|
|
77
|
+
handleResult(result, req, res, abortController, config)
|
|
58
78
|
}
|
|
59
79
|
|
|
60
80
|
function hasInputAddress (payload) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "2.2",
|
|
3
3
|
"metadata": {
|
|
4
|
-
"rules_version": "1.13.
|
|
4
|
+
"rules_version": "1.13.1"
|
|
5
5
|
},
|
|
6
6
|
"rules": [
|
|
7
7
|
{
|
|
@@ -6239,7 +6239,6 @@
|
|
|
6239
6239
|
{
|
|
6240
6240
|
"id": "rasp-930-100",
|
|
6241
6241
|
"name": "Local file inclusion exploit",
|
|
6242
|
-
"enabled": false,
|
|
6243
6242
|
"tags": {
|
|
6244
6243
|
"type": "lfi",
|
|
6245
6244
|
"category": "vulnerability_trigger",
|
|
@@ -6287,8 +6286,7 @@
|
|
|
6287
6286
|
},
|
|
6288
6287
|
{
|
|
6289
6288
|
"id": "rasp-932-100",
|
|
6290
|
-
"name": "
|
|
6291
|
-
"enabled": false,
|
|
6289
|
+
"name": "Command injection exploit",
|
|
6292
6290
|
"tags": {
|
|
6293
6291
|
"type": "command_injection",
|
|
6294
6292
|
"category": "vulnerability_trigger",
|
|
@@ -18,6 +18,10 @@ module.exports = {
|
|
|
18
18
|
APM_TRACING_CUSTOM_TAGS: 1n << 15n,
|
|
19
19
|
APM_TRACING_ENABLED: 1n << 19n,
|
|
20
20
|
ASM_RASP_SQLI: 1n << 21n,
|
|
21
|
+
ASM_RASP_LFI: 1n << 22n,
|
|
21
22
|
ASM_RASP_SSRF: 1n << 23n,
|
|
22
|
-
APM_TRACING_SAMPLE_RULES: 1n << 29n
|
|
23
|
+
APM_TRACING_SAMPLE_RULES: 1n << 29n,
|
|
24
|
+
ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
|
|
25
|
+
ASM_NETWORK_FINGERPRINT: 1n << 34n,
|
|
26
|
+
ASM_HEADER_FINGERPRINT: 1n << 35n
|
|
23
27
|
}
|
|
@@ -75,10 +75,14 @@ function enableWafUpdate (appsecConfig) {
|
|
|
75
75
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
|
|
76
76
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
|
|
77
77
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
|
|
78
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, true)
|
|
79
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, true)
|
|
80
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, true)
|
|
78
81
|
|
|
79
82
|
if (appsecConfig.rasp?.enabled) {
|
|
80
83
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, true)
|
|
81
84
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, true)
|
|
85
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, true)
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
// TODO: delete noop handlers and kPreUpdate and replace with batched handlers
|
|
@@ -103,9 +107,13 @@ function disableWafUpdate () {
|
|
|
103
107
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
|
|
104
108
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
|
|
105
109
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
|
|
110
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, false)
|
|
111
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, false)
|
|
112
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, false)
|
|
106
113
|
|
|
107
114
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
|
|
108
115
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
|
|
116
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, false)
|
|
109
117
|
|
|
110
118
|
rc.removeProductHandler('ASM_DATA')
|
|
111
119
|
rc.removeProductHandler('ASM_DD')
|
|
@@ -153,7 +153,11 @@ function reportAttack (attackData) {
|
|
|
153
153
|
rootSpan.addTags(newTags)
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
function
|
|
156
|
+
function isFingerprintDerivative (derivative) {
|
|
157
|
+
return derivative.startsWith('_dd.appsec.fp')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function reportDerivatives (derivatives) {
|
|
157
161
|
if (!derivatives) return
|
|
158
162
|
|
|
159
163
|
const req = storage.getStore()?.req
|
|
@@ -162,9 +166,12 @@ function reportSchemas (derivatives) {
|
|
|
162
166
|
if (!rootSpan) return
|
|
163
167
|
|
|
164
168
|
const tags = {}
|
|
165
|
-
for (
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
for (let [tag, value] of Object.entries(derivatives)) {
|
|
170
|
+
if (!isFingerprintDerivative(tag)) {
|
|
171
|
+
const gzippedValue = zlib.gzipSync(JSON.stringify(value))
|
|
172
|
+
value = gzippedValue.toString('base64')
|
|
173
|
+
}
|
|
174
|
+
tags[tag] = value
|
|
168
175
|
}
|
|
169
176
|
|
|
170
177
|
rootSpan.addTags(tags)
|
|
@@ -248,7 +255,7 @@ module.exports = {
|
|
|
248
255
|
reportMetrics,
|
|
249
256
|
reportAttack,
|
|
250
257
|
reportWafUpdate: incrementWafUpdatesMetric,
|
|
251
|
-
|
|
258
|
+
reportDerivatives,
|
|
252
259
|
finishRequest,
|
|
253
260
|
setRateLimit,
|
|
254
261
|
mapHeaderAndTags
|
|
@@ -5,6 +5,7 @@ const { getRootSpan } = require('./utils')
|
|
|
5
5
|
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
6
6
|
const { setUserTags } = require('./set_user')
|
|
7
7
|
const standalone = require('../standalone')
|
|
8
|
+
const waf = require('../waf')
|
|
8
9
|
|
|
9
10
|
function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
10
11
|
// TODO: better user check here and in _setUser() ?
|
|
@@ -76,6 +77,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
|
76
77
|
rootSpan.addTags(tags)
|
|
77
78
|
|
|
78
79
|
standalone.sample(rootSpan)
|
|
80
|
+
|
|
81
|
+
if (['users.login.success', 'users.login.failure'].includes(eventName)) {
|
|
82
|
+
waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
|
|
83
|
+
}
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
module.exports = {
|
|
@@ -93,7 +93,7 @@ class WAFContextWrapper {
|
|
|
93
93
|
Reporter.reportAttack(JSON.stringify(result.events))
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
Reporter.
|
|
96
|
+
Reporter.reportDerivatives(result.derivatives)
|
|
97
97
|
|
|
98
98
|
if (wafRunFinished.hasSubscribers) {
|
|
99
99
|
wafRunFinished.publish({ payload })
|
|
@@ -12,19 +12,7 @@ const {
|
|
|
12
12
|
TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES
|
|
13
13
|
} = require('../../ci-visibility/telemetry')
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
let totalNumTests = 0
|
|
17
|
-
|
|
18
|
-
for (const testModule of Object.values(knownTests)) {
|
|
19
|
-
for (const testSuite of Object.values(testModule)) {
|
|
20
|
-
for (const testList of Object.values(testSuite)) {
|
|
21
|
-
totalNumTests += testList.length
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return totalNumTests
|
|
27
|
-
}
|
|
15
|
+
const { getNumFromKnownTests } = require('../../plugins/util/test')
|
|
28
16
|
|
|
29
17
|
function getKnownTests ({
|
|
30
18
|
url,
|
|
@@ -102,7 +90,7 @@ function getKnownTests ({
|
|
|
102
90
|
try {
|
|
103
91
|
const { data: { attributes: { tests: knownTests } } } = JSON.parse(res)
|
|
104
92
|
|
|
105
|
-
const numTests =
|
|
93
|
+
const numTests = getNumFromKnownTests(knownTests)
|
|
106
94
|
|
|
107
95
|
incrementCountMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, numTests)
|
|
108
96
|
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, res.length)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const Plugin = require('../../plugins/plugin')
|
|
2
|
+
const log = require('../../log')
|
|
3
|
+
|
|
4
|
+
function getWinstonLogSubmissionParameters (config) {
|
|
5
|
+
const { site, service } = config
|
|
6
|
+
|
|
7
|
+
const defaultParameters = {
|
|
8
|
+
host: `http-intake.logs.${site}`,
|
|
9
|
+
path: `/api/v2/logs?ddsource=winston&service=${service}`,
|
|
10
|
+
ssl: true,
|
|
11
|
+
headers: {
|
|
12
|
+
'DD-API-KEY': process.env.DD_API_KEY
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!process.env.DD_AGENTLESS_LOG_SUBMISSION_URL) {
|
|
17
|
+
return defaultParameters
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(process.env.DD_AGENTLESS_LOG_SUBMISSION_URL)
|
|
22
|
+
return {
|
|
23
|
+
host: url.hostname,
|
|
24
|
+
port: url.port,
|
|
25
|
+
ssl: url.protocol === 'https:',
|
|
26
|
+
path: defaultParameters.path,
|
|
27
|
+
headers: defaultParameters.headers
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
log.error('Could not parse DD_AGENTLESS_LOG_SUBMISSION_URL')
|
|
31
|
+
return defaultParameters
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class LogSubmissionPlugin extends Plugin {
|
|
36
|
+
static get id () {
|
|
37
|
+
return 'log-submission'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
constructor (...args) {
|
|
41
|
+
super(...args)
|
|
42
|
+
|
|
43
|
+
this.addSub('ci:log-submission:winston:configure', (httpClass) => {
|
|
44
|
+
this.HttpClass = httpClass
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
this.addSub('ci:log-submission:winston:add-transport', (logger) => {
|
|
48
|
+
logger.add(new this.HttpClass(getWinstonLogSubmissionParameters(this.config)))
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = LogSubmissionPlugin
|
|
@@ -464,6 +464,7 @@ class Config {
|
|
|
464
464
|
this._setValue(defaults, 'appsec.wafTimeout', 5e3) // µs
|
|
465
465
|
this._setValue(defaults, 'clientIpEnabled', false)
|
|
466
466
|
this._setValue(defaults, 'clientIpHeader', null)
|
|
467
|
+
this._setValue(defaults, 'codeOriginForSpans.enabled', false)
|
|
467
468
|
this._setValue(defaults, 'dbmPropagationMode', 'disabled')
|
|
468
469
|
this._setValue(defaults, 'dogstatsd.hostname', '127.0.0.1')
|
|
469
470
|
this._setValue(defaults, 'dogstatsd.port', '8125')
|
|
@@ -478,6 +479,7 @@ class Config {
|
|
|
478
479
|
this._setValue(defaults, 'gitMetadataEnabled', true)
|
|
479
480
|
this._setValue(defaults, 'headerTags', [])
|
|
480
481
|
this._setValue(defaults, 'hostname', '127.0.0.1')
|
|
482
|
+
this._setValue(defaults, 'iast.cookieFilterPattern', '.{32,}')
|
|
481
483
|
this._setValue(defaults, 'iast.deduplicationEnabled', true)
|
|
482
484
|
this._setValue(defaults, 'iast.enabled', false)
|
|
483
485
|
this._setValue(defaults, 'iast.maxConcurrentRequests', 2)
|
|
@@ -498,6 +500,7 @@ class Config {
|
|
|
498
500
|
this._setValue(defaults, 'isIntelligentTestRunnerEnabled', false)
|
|
499
501
|
this._setValue(defaults, 'isManualApiEnabled', false)
|
|
500
502
|
this._setValue(defaults, 'ciVisibilityTestSessionName', '')
|
|
503
|
+
this._setValue(defaults, 'ciVisAgentlessLogSubmissionEnabled', false)
|
|
501
504
|
this._setValue(defaults, 'logInjection', false)
|
|
502
505
|
this._setValue(defaults, 'lookup', undefined)
|
|
503
506
|
this._setValue(defaults, 'memcachedCommandEnabled', false)
|
|
@@ -571,6 +574,7 @@ class Config {
|
|
|
571
574
|
DD_APPSEC_RASP_ENABLED,
|
|
572
575
|
DD_APPSEC_TRACE_RATE_LIMIT,
|
|
573
576
|
DD_APPSEC_WAF_TIMEOUT,
|
|
577
|
+
DD_CODE_ORIGIN_FOR_SPANS_ENABLED,
|
|
574
578
|
DD_DATA_STREAMS_ENABLED,
|
|
575
579
|
DD_DBM_PROPAGATION_MODE,
|
|
576
580
|
DD_DOGSTATSD_HOSTNAME,
|
|
@@ -581,6 +585,7 @@ class Config {
|
|
|
581
585
|
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
|
|
582
586
|
DD_EXPERIMENTAL_PROFILING_ENABLED,
|
|
583
587
|
JEST_WORKER_ID,
|
|
588
|
+
DD_IAST_COOKIE_FILTER_PATTERN,
|
|
584
589
|
DD_IAST_DEDUPLICATION_ENABLED,
|
|
585
590
|
DD_IAST_ENABLED,
|
|
586
591
|
DD_IAST_MAX_CONCURRENT_REQUESTS,
|
|
@@ -701,6 +706,7 @@ class Config {
|
|
|
701
706
|
this._envUnprocessed['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT
|
|
702
707
|
this._setBoolean(env, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
|
|
703
708
|
this._setString(env, 'clientIpHeader', DD_TRACE_CLIENT_IP_HEADER)
|
|
709
|
+
this._setBoolean(env, 'codeOriginForSpans.enabled', DD_CODE_ORIGIN_FOR_SPANS_ENABLED)
|
|
704
710
|
this._setString(env, 'dbmPropagationMode', DD_DBM_PROPAGATION_MODE)
|
|
705
711
|
this._setString(env, 'dogstatsd.hostname', DD_DOGSTATSD_HOSTNAME)
|
|
706
712
|
this._setString(env, 'dogstatsd.port', DD_DOGSTATSD_PORT)
|
|
@@ -716,6 +722,7 @@ class Config {
|
|
|
716
722
|
this._setBoolean(env, 'gitMetadataEnabled', DD_TRACE_GIT_METADATA_ENABLED)
|
|
717
723
|
this._setArray(env, 'headerTags', DD_TRACE_HEADER_TAGS)
|
|
718
724
|
this._setString(env, 'hostname', coalesce(DD_AGENT_HOST, DD_TRACE_AGENT_HOSTNAME))
|
|
725
|
+
this._setString(env, 'iast.cookieFilterPattern', DD_IAST_COOKIE_FILTER_PATTERN)
|
|
719
726
|
this._setBoolean(env, 'iast.deduplicationEnabled', DD_IAST_DEDUPLICATION_ENABLED)
|
|
720
727
|
this._setBoolean(env, 'iast.enabled', DD_IAST_ENABLED)
|
|
721
728
|
this._setValue(env, 'iast.maxConcurrentRequests', maybeInt(DD_IAST_MAX_CONCURRENT_REQUESTS))
|
|
@@ -867,6 +874,7 @@ class Config {
|
|
|
867
874
|
this._optsUnprocessed['appsec.wafTimeout'] = options.appsec.wafTimeout
|
|
868
875
|
this._setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled)
|
|
869
876
|
this._setString(opts, 'clientIpHeader', options.clientIpHeader)
|
|
877
|
+
this._setBoolean(opts, 'codeOriginForSpans.enabled', options.codeOriginForSpans?.enabled)
|
|
870
878
|
this._setString(opts, 'dbmPropagationMode', options.dbmPropagationMode)
|
|
871
879
|
if (options.dogstatsd) {
|
|
872
880
|
this._setString(opts, 'dogstatsd.hostname', options.dogstatsd.hostname)
|
|
@@ -884,6 +892,7 @@ class Config {
|
|
|
884
892
|
this._optsUnprocessed.flushMinSpans = options.flushMinSpans
|
|
885
893
|
this._setArray(opts, 'headerTags', options.headerTags)
|
|
886
894
|
this._setString(opts, 'hostname', options.hostname)
|
|
895
|
+
this._setString(opts, 'iast.cookieFilterPattern', options.iast?.cookieFilterPattern)
|
|
887
896
|
this._setBoolean(opts, 'iast.deduplicationEnabled', options.iast && options.iast.deduplicationEnabled)
|
|
888
897
|
this._setBoolean(opts, 'iast.enabled',
|
|
889
898
|
options.iast && (options.iast === true || options.iast.enabled === true))
|
|
@@ -1035,7 +1044,8 @@ class Config {
|
|
|
1035
1044
|
DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED,
|
|
1036
1045
|
DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
|
|
1037
1046
|
DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
|
|
1038
|
-
DD_TEST_SESSION_NAME
|
|
1047
|
+
DD_TEST_SESSION_NAME,
|
|
1048
|
+
DD_AGENTLESS_LOG_SUBMISSION_ENABLED
|
|
1039
1049
|
} = process.env
|
|
1040
1050
|
|
|
1041
1051
|
if (DD_CIVISIBILITY_AGENTLESS_URL) {
|
|
@@ -1052,6 +1062,7 @@ class Config {
|
|
|
1052
1062
|
this._setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(this._isCiVisibilityItrEnabled()))
|
|
1053
1063
|
this._setBoolean(calc, 'isManualApiEnabled', !isFalse(this._isCiVisibilityManualApiEnabled()))
|
|
1054
1064
|
this._setString(calc, 'ciVisibilityTestSessionName', DD_TEST_SESSION_NAME)
|
|
1065
|
+
this._setBoolean(calc, 'ciVisAgentlessLogSubmissionEnabled', isTrue(DD_AGENTLESS_LOG_SUBMISSION_ENABLED))
|
|
1055
1066
|
}
|
|
1056
1067
|
this._setString(calc, 'dogstatsd.hostname', this._getHostname())
|
|
1057
1068
|
this._setBoolean(calc, 'isGitUploadEnabled',
|
|
@@ -4,13 +4,36 @@ const { Schema } = require('./schema')
|
|
|
4
4
|
|
|
5
5
|
const maxDepth = 10
|
|
6
6
|
const maxProperties = 1000
|
|
7
|
-
const CACHE = new LRUCache({ max:
|
|
7
|
+
const CACHE = new LRUCache({ max: 256 })
|
|
8
8
|
|
|
9
9
|
class SchemaBuilder {
|
|
10
10
|
constructor (iterator) {
|
|
11
11
|
this.schema = new OpenApiSchema()
|
|
12
12
|
this.iterator = iterator
|
|
13
|
-
this.
|
|
13
|
+
this.properties = 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static getCache () {
|
|
17
|
+
return CACHE
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static getSchemaDefinition (schema) {
|
|
21
|
+
const noNones = convertToJsonCompatible(schema)
|
|
22
|
+
const definition = jsonStringify(noNones)
|
|
23
|
+
const id = fnv64(Buffer.from(definition, 'utf-8')).toString()
|
|
24
|
+
return new Schema(definition, id)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static getSchema (schemaName, iterator, builder) {
|
|
28
|
+
if (!CACHE.has(schemaName)) {
|
|
29
|
+
CACHE.set(schemaName, (builder ?? new SchemaBuilder(iterator)).build())
|
|
30
|
+
}
|
|
31
|
+
return CACHE.get(schemaName)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
build () {
|
|
35
|
+
this.iterator.iterateOverSchema(this)
|
|
36
|
+
return this.schema
|
|
14
37
|
}
|
|
15
38
|
|
|
16
39
|
addProperty (schemaName, fieldName, isArray, type, description, ref, format, enumValues) {
|
|
@@ -26,14 +49,6 @@ class SchemaBuilder {
|
|
|
26
49
|
return true
|
|
27
50
|
}
|
|
28
51
|
|
|
29
|
-
build () {
|
|
30
|
-
this.iterator.iterateOverSchema(this)
|
|
31
|
-
const noNones = convertToJsonCompatible(this.schema)
|
|
32
|
-
const definition = jsonStringify(noNones)
|
|
33
|
-
const id = fnv64(Buffer.from(definition, 'utf-8')).toString()
|
|
34
|
-
return new Schema(definition, id)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
52
|
shouldExtractSchema (schemaName, depth) {
|
|
38
53
|
if (depth > maxDepth) {
|
|
39
54
|
return false
|
|
@@ -44,13 +59,6 @@ class SchemaBuilder {
|
|
|
44
59
|
this.schema.components.schemas[schemaName] = new OpenApiSchema.SCHEMA()
|
|
45
60
|
return true
|
|
46
61
|
}
|
|
47
|
-
|
|
48
|
-
static getSchema (schemaName, iterator) {
|
|
49
|
-
if (!CACHE.has(schemaName)) {
|
|
50
|
-
CACHE.set(schemaName, new SchemaBuilder(iterator).build())
|
|
51
|
-
}
|
|
52
|
-
return CACHE.get(schemaName)
|
|
53
|
-
}
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
class OpenApiSchema {
|