dd-trace 4.47.1 → 4.49.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 +1 -1
- package/ext/types.d.ts +1 -0
- package/ext/types.js +1 -0
- package/index.d.ts +361 -0
- package/package.json +18 -13
- package/packages/datadog-code-origin/index.js +38 -0
- package/packages/datadog-core/index.js +2 -2
- package/packages/datadog-core/src/utils/src/parse-tags.js +33 -0
- package/packages/datadog-esbuild/index.js +4 -2
- package/packages/datadog-instrumentations/src/amqplib.js +65 -5
- 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 +144 -27
- 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 +6 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
- package/packages/datadog-instrumentations/src/jest.js +2 -1
- package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
- package/packages/datadog-instrumentations/src/multer.js +37 -0
- package/packages/datadog-instrumentations/src/mysql2.js +220 -1
- package/packages/datadog-instrumentations/src/openai.js +2 -2
- package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
- package/packages/datadog-instrumentations/src/url.js +84 -0
- package/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js +7 -4
- package/packages/datadog-instrumentations/src/winston.js +22 -0
- package/packages/datadog-plugin-amqplib/src/consumer.js +4 -4
- 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-aws-sdk/src/services/eventbridge.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -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-google-cloud-pubsub/src/consumer.js +8 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +8 -0
- package/packages/datadog-plugin-grpc/src/client.js +3 -0
- package/packages/datadog-plugin-grpc/src/server.js +3 -0
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +6 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -4
- package/packages/datadog-plugin-kafkajs/src/producer.js +10 -4
- package/packages/datadog-plugin-mocha/src/index.js +4 -1
- package/packages/datadog-plugin-openai/src/index.js +9 -1015
- package/packages/datadog-plugin-openai/src/tracing.js +1023 -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 +8 -1
- package/packages/dd-trace/src/appsec/channels.js +7 -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/plugin.js +55 -7
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
- package/packages/dd-trace/src/appsec/index.js +61 -43
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
- package/packages/dd-trace/src/appsec/rasp/index.js +27 -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/ssrf.js +4 -3
- package/packages/dd-trace/src/appsec/rasp/utils.js +4 -2
- package/packages/dd-trace/src/appsec/recommended.json +3 -7
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +10 -0
- package/packages/dd-trace/src/appsec/reporter.js +17 -9
- package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -3
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +4 -0
- package/packages/dd-trace/src/azure_metadata.js +120 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +97 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +90 -0
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +19 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +53 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +8 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +43 -0
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
- package/packages/dd-trace/src/config.js +86 -6
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/datastreams/pathway.js +1 -0
- 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 +52 -5
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +29 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +187 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +40 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +252 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +19 -4
- package/packages/dd-trace/src/debugger/index.js +10 -3
- 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/llmobs/constants/tags.js +34 -0
- package/packages/dd-trace/src/llmobs/constants/text.js +6 -0
- package/packages/dd-trace/src/llmobs/constants/writers.js +13 -0
- package/packages/dd-trace/src/llmobs/index.js +103 -0
- package/packages/dd-trace/src/llmobs/noop.js +82 -0
- package/packages/dd-trace/src/llmobs/plugins/base.js +65 -0
- package/packages/dd-trace/src/llmobs/plugins/openai.js +205 -0
- package/packages/dd-trace/src/llmobs/sdk.js +377 -0
- package/packages/dd-trace/src/llmobs/span_processor.js +195 -0
- package/packages/dd-trace/src/llmobs/storage.js +7 -0
- package/packages/dd-trace/src/llmobs/tagger.js +322 -0
- package/packages/dd-trace/src/llmobs/util.js +176 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +111 -0
- package/packages/dd-trace/src/llmobs/writers/evaluations.js +29 -0
- package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +23 -0
- package/packages/dd-trace/src/llmobs/writers/spans/agentless.js +17 -0
- package/packages/dd-trace/src/llmobs/writers/spans/base.js +49 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/noop/span.js +3 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +1 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +73 -12
- package/packages/dd-trace/src/opentracing/span.js +12 -0
- package/packages/dd-trace/src/opentracing/tracer.js +8 -1
- package/packages/dd-trace/src/payload-tagging/config/aws.json +71 -3
- 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/outbound.js +9 -0
- 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/priority_sampler.js +16 -0
- package/packages/dd-trace/src/profiling/config.js +3 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +7 -5
- 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 +95 -66
- package/packages/dd-trace/src/proxy.js +20 -1
- 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/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/telemetry/index.js +11 -1
- package/packages/datadog-core/src/storage/async_resource.js +0 -108
- package/packages/datadog-core/src/storage/index.js +0 -5
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { childProcessExecutionTracingChannel } = require('../channels')
|
|
4
|
+
const { RULE_TYPES, handleResult } = require('./utils')
|
|
5
|
+
const { storage } = require('../../../../datadog-core')
|
|
6
|
+
const addresses = require('../addresses')
|
|
7
|
+
const waf = require('../waf')
|
|
8
|
+
|
|
9
|
+
let config
|
|
10
|
+
|
|
11
|
+
function enable (_config) {
|
|
12
|
+
config = _config
|
|
13
|
+
|
|
14
|
+
childProcessExecutionTracingChannel.subscribe({
|
|
15
|
+
start: analyzeCommandInjection
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function disable () {
|
|
20
|
+
if (childProcessExecutionTracingChannel.start.hasSubscribers) {
|
|
21
|
+
childProcessExecutionTracingChannel.unsubscribe({
|
|
22
|
+
start: analyzeCommandInjection
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
|
|
28
|
+
if (!file || !shell) return
|
|
29
|
+
|
|
30
|
+
const store = storage.getStore()
|
|
31
|
+
const req = store?.req
|
|
32
|
+
if (!req) return
|
|
33
|
+
|
|
34
|
+
const commandParams = fileArgs ? [file, ...fileArgs] : file
|
|
35
|
+
|
|
36
|
+
const persistent = {
|
|
37
|
+
[addresses.SHELL_COMMAND]: commandParams
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = waf.run({ persistent }, req, RULE_TYPES.COMMAND_INJECTION)
|
|
41
|
+
|
|
42
|
+
const res = store?.res
|
|
43
|
+
handleResult(result, req, res, abortController, config)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
enable,
|
|
48
|
+
disable
|
|
49
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Plugin = require('../../plugins/plugin')
|
|
4
|
+
const { storage } = require('../../../../datadog-core')
|
|
5
|
+
const log = require('../../log')
|
|
6
|
+
|
|
7
|
+
const RASP_MODULE = 'rasp'
|
|
8
|
+
const IAST_MODULE = 'iast'
|
|
9
|
+
|
|
10
|
+
const enabledFor = {
|
|
11
|
+
[RASP_MODULE]: false,
|
|
12
|
+
[IAST_MODULE]: false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let fsPlugin
|
|
16
|
+
|
|
17
|
+
function enterWith (fsProps, store = storage.getStore()) {
|
|
18
|
+
if (store && !store.fs?.opExcluded) {
|
|
19
|
+
storage.enterWith({
|
|
20
|
+
...store,
|
|
21
|
+
fs: {
|
|
22
|
+
...store.fs,
|
|
23
|
+
...fsProps,
|
|
24
|
+
parentStore: store
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class AppsecFsPlugin extends Plugin {
|
|
31
|
+
enable () {
|
|
32
|
+
this.addSub('apm:fs:operation:start', this._onFsOperationStart)
|
|
33
|
+
this.addSub('apm:fs:operation:finish', this._onFsOperationFinishOrRenderEnd)
|
|
34
|
+
this.addSub('tracing:datadog:express:response:render:start', this._onResponseRenderStart)
|
|
35
|
+
this.addSub('tracing:datadog:express:response:render:end', this._onFsOperationFinishOrRenderEnd)
|
|
36
|
+
|
|
37
|
+
super.configure(true)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
disable () {
|
|
41
|
+
super.configure(false)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_onFsOperationStart () {
|
|
45
|
+
const store = storage.getStore()
|
|
46
|
+
if (store) {
|
|
47
|
+
enterWith({ root: store.fs?.root === undefined }, store)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_onResponseRenderStart () {
|
|
52
|
+
enterWith({ opExcluded: true })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_onFsOperationFinishOrRenderEnd () {
|
|
56
|
+
const store = storage.getStore()
|
|
57
|
+
if (store?.fs?.parentStore) {
|
|
58
|
+
storage.enterWith(store.fs.parentStore)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function enable (mod) {
|
|
64
|
+
if (enabledFor[mod] !== false) return
|
|
65
|
+
|
|
66
|
+
enabledFor[mod] = true
|
|
67
|
+
|
|
68
|
+
if (!fsPlugin) {
|
|
69
|
+
fsPlugin = new AppsecFsPlugin()
|
|
70
|
+
fsPlugin.enable()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
log.info(`Enabled AppsecFsPlugin for ${mod}`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function disable (mod) {
|
|
77
|
+
if (!mod || !enabledFor[mod]) return
|
|
78
|
+
|
|
79
|
+
enabledFor[mod] = false
|
|
80
|
+
|
|
81
|
+
const allDisabled = Object.values(enabledFor).every(val => val === false)
|
|
82
|
+
if (allDisabled) {
|
|
83
|
+
fsPlugin?.disable()
|
|
84
|
+
|
|
85
|
+
fsPlugin = undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
log.info(`Disabled AppsecFsPlugin for ${mod}`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
enable,
|
|
93
|
+
disable,
|
|
94
|
+
|
|
95
|
+
AppsecFsPlugin,
|
|
96
|
+
|
|
97
|
+
RASP_MODULE,
|
|
98
|
+
IAST_MODULE
|
|
99
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
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')
|
|
9
|
+
const cmdi = require('./command_injection')
|
|
8
10
|
|
|
9
11
|
const { DatadogRaspAbortError } = require('./utils')
|
|
10
12
|
|
|
@@ -30,17 +32,13 @@ function findDatadogRaspAbortError (err, deep = 10) {
|
|
|
30
32
|
return err
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
if (err
|
|
35
|
+
if (err?.cause && deep > 0) {
|
|
34
36
|
return findDatadogRaspAbortError(err.cause, deep - 1)
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
function handleUncaughtExceptionMonitor (
|
|
39
|
-
|
|
40
|
-
if (!abortError) return
|
|
41
|
-
|
|
42
|
-
const { req, res, blockingAction } = abortError
|
|
43
|
-
block(req, res, web.root(req), null, blockingAction)
|
|
40
|
+
function handleUncaughtExceptionMonitor (error) {
|
|
41
|
+
if (!blockOnDatadogRaspAbortError({ error })) return
|
|
44
42
|
|
|
45
43
|
if (!process.hasUncaughtExceptionCaptureCallback()) {
|
|
46
44
|
const cleanUp = removeAllListeners(process, 'uncaughtException')
|
|
@@ -82,22 +80,41 @@ function handleUncaughtExceptionMonitor (err) {
|
|
|
82
80
|
}
|
|
83
81
|
}
|
|
84
82
|
|
|
83
|
+
function blockOnDatadogRaspAbortError ({ error }) {
|
|
84
|
+
const abortError = findDatadogRaspAbortError(error)
|
|
85
|
+
if (!abortError) return false
|
|
86
|
+
|
|
87
|
+
const { req, res, blockingAction } = abortError
|
|
88
|
+
if (!isBlocked(res)) {
|
|
89
|
+
block(req, res, web.root(req), null, blockingAction)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
function enable (config) {
|
|
86
96
|
ssrf.enable(config)
|
|
87
97
|
sqli.enable(config)
|
|
98
|
+
lfi.enable(config)
|
|
99
|
+
cmdi.enable(config)
|
|
88
100
|
|
|
89
101
|
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
102
|
+
expressMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
function disable () {
|
|
93
106
|
ssrf.disable()
|
|
94
107
|
sqli.disable()
|
|
108
|
+
lfi.disable()
|
|
109
|
+
cmdi.disable()
|
|
95
110
|
|
|
96
111
|
process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
112
|
+
if (expressMiddlewareError.hasSubscribers) expressMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
module.exports = {
|
|
100
116
|
enable,
|
|
101
117
|
disable,
|
|
102
|
-
handleUncaughtExceptionMonitor // exported only for testing purpose
|
|
118
|
+
handleUncaughtExceptionMonitor, // exported only for testing purpose
|
|
119
|
+
blockOnDatadogRaspAbortError // exported only for testing purpose
|
|
103
120
|
}
|
|
@@ -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,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { format } = require('url')
|
|
3
4
|
const { httpClientRequestStart } = require('../channels')
|
|
4
5
|
const { storage } = require('../../../../datadog-core')
|
|
5
6
|
const addresses = require('../addresses')
|
|
@@ -20,12 +21,12 @@ function disable () {
|
|
|
20
21
|
function analyzeSsrf (ctx) {
|
|
21
22
|
const store = storage.getStore()
|
|
22
23
|
const req = store?.req
|
|
23
|
-
const
|
|
24
|
+
const outgoingUrl = (ctx.args.options?.uri && format(ctx.args.options.uri)) ?? ctx.args.uri
|
|
24
25
|
|
|
25
|
-
if (!req || !
|
|
26
|
+
if (!req || !outgoingUrl) return
|
|
26
27
|
|
|
27
28
|
const persistent = {
|
|
28
|
-
[addresses.HTTP_OUTGOING_URL]:
|
|
29
|
+
[addresses.HTTP_OUTGOING_URL]: outgoingUrl
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
|
|
@@ -12,8 +12,10 @@ if (abortOnUncaughtException) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const RULE_TYPES = {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
COMMAND_INJECTION: 'command_injection',
|
|
16
|
+
LFI: 'lfi',
|
|
17
|
+
SQL_INJECTION: 'sql_injection',
|
|
18
|
+
SSRF: 'ssrf'
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
class DatadogRaspAbortError extends Error {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "2.2",
|
|
3
3
|
"metadata": {
|
|
4
|
-
"rules_version": "1.13.
|
|
4
|
+
"rules_version": "1.13.2"
|
|
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",
|
|
@@ -6337,7 +6335,6 @@
|
|
|
6337
6335
|
{
|
|
6338
6336
|
"id": "rasp-934-100",
|
|
6339
6337
|
"name": "Server-side request forgery exploit",
|
|
6340
|
-
"enabled": false,
|
|
6341
6338
|
"tags": {
|
|
6342
6339
|
"type": "ssrf",
|
|
6343
6340
|
"category": "vulnerability_trigger",
|
|
@@ -6386,7 +6383,6 @@
|
|
|
6386
6383
|
{
|
|
6387
6384
|
"id": "rasp-942-100",
|
|
6388
6385
|
"name": "SQL injection exploit",
|
|
6389
|
-
"enabled": false,
|
|
6390
6386
|
"tags": {
|
|
6391
6387
|
"type": "sql_injection",
|
|
6392
6388
|
"category": "vulnerability_trigger",
|
|
@@ -6426,7 +6422,7 @@
|
|
|
6426
6422
|
}
|
|
6427
6423
|
]
|
|
6428
6424
|
},
|
|
6429
|
-
"operator": "sqli_detector"
|
|
6425
|
+
"operator": "sqli_detector@v2"
|
|
6430
6426
|
}
|
|
6431
6427
|
],
|
|
6432
6428
|
"transformers": [],
|
|
@@ -18,6 +18,11 @@ 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
|
-
|
|
23
|
+
ASM_RASP_SHI: 1n << 24n,
|
|
24
|
+
APM_TRACING_SAMPLE_RULES: 1n << 29n,
|
|
25
|
+
ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
|
|
26
|
+
ASM_NETWORK_FINGERPRINT: 1n << 34n,
|
|
27
|
+
ASM_HEADER_FINGERPRINT: 1n << 35n
|
|
23
28
|
}
|
|
@@ -75,10 +75,15 @@ 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)
|
|
86
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, true)
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
// TODO: delete noop handlers and kPreUpdate and replace with batched handlers
|
|
@@ -103,9 +108,14 @@ function disableWafUpdate () {
|
|
|
103
108
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
|
|
104
109
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
|
|
105
110
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
|
|
111
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, false)
|
|
112
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, false)
|
|
113
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, false)
|
|
106
114
|
|
|
107
115
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
|
|
108
116
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
|
|
117
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, false)
|
|
118
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, false)
|
|
109
119
|
|
|
110
120
|
rc.removeProductHandler('ASM_DATA')
|
|
111
121
|
rc.removeProductHandler('ASM_DD')
|
|
@@ -13,8 +13,9 @@ const {
|
|
|
13
13
|
getRequestMetrics
|
|
14
14
|
} = require('./telemetry')
|
|
15
15
|
const zlib = require('zlib')
|
|
16
|
-
const { MANUAL_KEEP } = require('../../../../ext/tags')
|
|
17
16
|
const standalone = require('./standalone')
|
|
17
|
+
const { SAMPLING_MECHANISM_APPSEC } = require('../constants')
|
|
18
|
+
const { keepTrace } = require('../priority_sampler')
|
|
18
19
|
|
|
19
20
|
// default limiter, configurable with setRateLimit()
|
|
20
21
|
let limiter = new Limiter(100)
|
|
@@ -96,8 +97,6 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
|
96
97
|
metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
metricsQueue.set(MANUAL_KEEP, 'true')
|
|
100
|
-
|
|
101
100
|
incrementWafInitMetric(wafVersion, rulesVersion)
|
|
102
101
|
}
|
|
103
102
|
|
|
@@ -129,7 +128,7 @@ function reportAttack (attackData) {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
if (limiter.isAllowed()) {
|
|
132
|
-
|
|
131
|
+
keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
|
|
133
132
|
|
|
134
133
|
standalone.sample(rootSpan)
|
|
135
134
|
}
|
|
@@ -153,7 +152,11 @@ function reportAttack (attackData) {
|
|
|
153
152
|
rootSpan.addTags(newTags)
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
function
|
|
155
|
+
function isFingerprintDerivative (derivative) {
|
|
156
|
+
return derivative.startsWith('_dd.appsec.fp')
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function reportDerivatives (derivatives) {
|
|
157
160
|
if (!derivatives) return
|
|
158
161
|
|
|
159
162
|
const req = storage.getStore()?.req
|
|
@@ -162,9 +165,12 @@ function reportSchemas (derivatives) {
|
|
|
162
165
|
if (!rootSpan) return
|
|
163
166
|
|
|
164
167
|
const tags = {}
|
|
165
|
-
for (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
for (let [tag, value] of Object.entries(derivatives)) {
|
|
169
|
+
if (!isFingerprintDerivative(tag)) {
|
|
170
|
+
const gzippedValue = zlib.gzipSync(JSON.stringify(value))
|
|
171
|
+
value = gzippedValue.toString('base64')
|
|
172
|
+
}
|
|
173
|
+
tags[tag] = value
|
|
168
174
|
}
|
|
169
175
|
|
|
170
176
|
rootSpan.addTags(tags)
|
|
@@ -177,6 +183,8 @@ function finishRequest (req, res) {
|
|
|
177
183
|
if (metricsQueue.size) {
|
|
178
184
|
rootSpan.addTags(Object.fromEntries(metricsQueue))
|
|
179
185
|
|
|
186
|
+
keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
|
|
187
|
+
|
|
180
188
|
standalone.sample(rootSpan)
|
|
181
189
|
|
|
182
190
|
metricsQueue.clear()
|
|
@@ -248,7 +256,7 @@ module.exports = {
|
|
|
248
256
|
reportMetrics,
|
|
249
257
|
reportAttack,
|
|
250
258
|
reportWafUpdate: incrementWafUpdatesMetric,
|
|
251
|
-
|
|
259
|
+
reportDerivatives,
|
|
252
260
|
finishRequest,
|
|
253
261
|
setRateLimit,
|
|
254
262
|
mapHeaderAndTags
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const { getRootSpan } = require('./utils')
|
|
5
|
-
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
6
5
|
const { setUserTags } = require('./set_user')
|
|
7
6
|
const standalone = require('../standalone')
|
|
7
|
+
const waf = require('../waf')
|
|
8
|
+
const { SAMPLING_MECHANISM_APPSEC } = require('../../constants')
|
|
9
|
+
const { keepTrace } = require('../../priority_sampler')
|
|
8
10
|
|
|
9
11
|
function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
10
12
|
// TODO: better user check here and in _setUser() ?
|
|
@@ -54,9 +56,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
|
54
56
|
return
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
|
|
60
|
+
|
|
57
61
|
const tags = {
|
|
58
|
-
[`appsec.events.${eventName}.track`]: 'true'
|
|
59
|
-
[MANUAL_KEEP]: 'true'
|
|
62
|
+
[`appsec.events.${eventName}.track`]: 'true'
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
if (mode === 'sdk') {
|
|
@@ -76,6 +79,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
|
76
79
|
rootSpan.addTags(tags)
|
|
77
80
|
|
|
78
81
|
standalone.sample(rootSpan)
|
|
82
|
+
|
|
83
|
+
if (['users.login.success', 'users.login.failure'].includes(eventName)) {
|
|
84
|
+
waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
|
|
85
|
+
}
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
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 })
|
|
@@ -51,6 +51,10 @@ class WAFManager {
|
|
|
51
51
|
update (newRules) {
|
|
52
52
|
this.ddwaf.update(newRules)
|
|
53
53
|
|
|
54
|
+
if (this.ddwaf.diagnostics.ruleset_version) {
|
|
55
|
+
this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
|
|
55
59
|
}
|
|
56
60
|
|