dd-trace 5.24.0 → 5.25.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 -0
- package/index.d.ts +335 -0
- package/package.json +13 -7
- package/packages/datadog-code-origin/index.js +4 -4
- 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/child_process.js +135 -27
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
- package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
- package/packages/datadog-instrumentations/src/multer.js +37 -0
- package/packages/datadog-instrumentations/src/openai.js +2 -2
- 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-plugin-amqplib/src/consumer.js +4 -4
- 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-fastify/src/code_origin.js +2 -2
- 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/dd-trace/src/appsec/addresses.js +2 -0
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +55 -7
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
- package/packages/dd-trace/src/appsec/index.js +3 -0
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
- package/packages/dd-trace/src/appsec/rasp/index.js +3 -0
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +4 -3
- package/packages/dd-trace/src/appsec/rasp/utils.js +3 -2
- package/packages/dd-trace/src/appsec/recommended.json +2 -4
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
- package/packages/dd-trace/src/appsec/reporter.js +5 -4
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -3
- 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/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/config.js +75 -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/debugger/devtools_client/index.js +9 -13
- package/packages/dd-trace/src/debugger/devtools_client/send.js +15 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +57 -23
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +12 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +31 -20
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +11 -2
- package/packages/dd-trace/src/debugger/index.js +10 -3
- 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/plugins/outbound.js +9 -0
- 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/profilers/wall.js +2 -1
- package/packages/dd-trace/src/proxy.js +8 -1
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/telemetry/index.js +11 -1
|
@@ -28,6 +28,8 @@ module.exports = {
|
|
|
28
28
|
DB_STATEMENT: 'server.db.statement',
|
|
29
29
|
DB_SYSTEM: 'server.db.system',
|
|
30
30
|
|
|
31
|
+
SHELL_COMMAND: 'server.sys.shell.cmd',
|
|
32
|
+
|
|
31
33
|
LOGIN_SUCCESS: 'server.business_logic.users.login.success',
|
|
32
34
|
LOGIN_FAILURE: 'server.business_logic.users.login.failure'
|
|
33
35
|
}
|
|
@@ -6,6 +6,7 @@ const dc = require('dc-polyfill')
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
bodyParser: dc.channel('datadog:body-parser:read:finish'),
|
|
8
8
|
cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
|
|
9
|
+
multerParser: dc.channel('datadog:multer:read:finish'),
|
|
9
10
|
startGraphqlResolve: dc.channel('datadog:graphql:resolver:start'),
|
|
10
11
|
graphqlMiddlewareChannel: dc.tracingChannel('datadog:apollo:middleware'),
|
|
11
12
|
apolloChannel: dc.tracingChannel('datadog:apollo:request'),
|
|
@@ -28,5 +29,6 @@ module.exports = {
|
|
|
28
29
|
mysql2OuterQueryStart: dc.channel('datadog:mysql2:outerquery:start'),
|
|
29
30
|
wafRunFinished: dc.channel('datadog:waf:run:finish'),
|
|
30
31
|
fsOperationStart: dc.channel('apm:fs:operation:start'),
|
|
31
|
-
expressMiddlewareError: dc.channel('apm:express:middleware:error')
|
|
32
|
+
expressMiddlewareError: dc.channel('apm:express:middleware:error'),
|
|
33
|
+
childProcessExecutionTracingChannel: dc.tracingChannel('datadog:child_process:execution')
|
|
32
34
|
}
|
|
@@ -23,18 +23,26 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
23
23
|
constructor () {
|
|
24
24
|
super()
|
|
25
25
|
this._type = 'taint-tracking'
|
|
26
|
+
this._taintedURLs = new WeakMap()
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
onConfigure () {
|
|
30
|
+
const onRequestBody = ({ req }) => {
|
|
31
|
+
const iastContext = getIastContext(storage.getStore())
|
|
32
|
+
if (iastContext && iastContext.body !== req.body) {
|
|
33
|
+
this._taintTrackingHandler(HTTP_REQUEST_BODY, req, 'body', iastContext)
|
|
34
|
+
iastContext.body = req.body
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
this.addSub(
|
|
30
39
|
{ channelName: 'datadog:body-parser:read:finish', tag: HTTP_REQUEST_BODY },
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
40
|
+
onRequestBody
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
this.addSub(
|
|
44
|
+
{ channelName: 'datadog:multer:read:finish', tag: HTTP_REQUEST_BODY },
|
|
45
|
+
onRequestBody
|
|
38
46
|
)
|
|
39
47
|
|
|
40
48
|
this.addSub(
|
|
@@ -81,6 +89,46 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
81
89
|
}
|
|
82
90
|
)
|
|
83
91
|
|
|
92
|
+
const urlResultTaintedProperties = ['host', 'origin', 'hostname']
|
|
93
|
+
this.addSub(
|
|
94
|
+
{ channelName: 'datadog:url:parse:finish' },
|
|
95
|
+
({ input, base, parsed, isURL }) => {
|
|
96
|
+
const iastContext = getIastContext(storage.getStore())
|
|
97
|
+
let ranges
|
|
98
|
+
|
|
99
|
+
if (base) {
|
|
100
|
+
ranges = getRanges(iastContext, base)
|
|
101
|
+
} else {
|
|
102
|
+
ranges = getRanges(iastContext, input)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (ranges?.length) {
|
|
106
|
+
if (isURL) {
|
|
107
|
+
this._taintedURLs.set(parsed, ranges[0])
|
|
108
|
+
} else {
|
|
109
|
+
urlResultTaintedProperties.forEach(param => {
|
|
110
|
+
this._taintTrackingHandler(ranges[0].iinfo.type, parsed, param, iastContext)
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
this.addSub(
|
|
118
|
+
{ channelName: 'datadog:url:getter:finish' },
|
|
119
|
+
(context) => {
|
|
120
|
+
if (!urlResultTaintedProperties.includes(context.property)) return
|
|
121
|
+
|
|
122
|
+
const origRange = this._taintedURLs.get(context.urlObject)
|
|
123
|
+
if (!origRange) return
|
|
124
|
+
|
|
125
|
+
const iastContext = getIastContext(storage.getStore())
|
|
126
|
+
if (!iastContext) return
|
|
127
|
+
|
|
128
|
+
context.result =
|
|
129
|
+
newTaintedString(iastContext, context.result, origRange.iinfo.parameterName, origRange.iinfo.type)
|
|
130
|
+
})
|
|
131
|
+
|
|
84
132
|
// this is a special case to increment INSTRUMENTED_SOURCE metric for header
|
|
85
133
|
this.addInstrumentedSource('http', [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME])
|
|
86
134
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
4
3
|
const LRU = require('lru-cache')
|
|
5
4
|
const vulnerabilitiesFormatter = require('./vulnerabilities-formatter')
|
|
6
5
|
const { IAST_ENABLED_TAG_KEY, IAST_JSON_TAG_KEY } = require('./tags')
|
|
7
6
|
const standalone = require('../standalone')
|
|
7
|
+
const { SAMPLING_MECHANISM_APPSEC } = require('../../constants')
|
|
8
|
+
const { keepTrace } = require('../../priority_sampler')
|
|
8
9
|
|
|
9
10
|
const VULNERABILITIES_KEY = 'vulnerabilities'
|
|
10
11
|
const VULNERABILITY_HASHES_MAX_SIZE = 1000
|
|
@@ -56,9 +57,10 @@ function sendVulnerabilities (vulnerabilities, rootSpan) {
|
|
|
56
57
|
const tags = {}
|
|
57
58
|
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
58
59
|
tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
|
|
59
|
-
tags[MANUAL_KEEP] = 'true'
|
|
60
60
|
span.addTags(tags)
|
|
61
61
|
|
|
62
|
+
keepTrace(span, SAMPLING_MECHANISM_APPSEC)
|
|
63
|
+
|
|
62
64
|
standalone.sample(span)
|
|
63
65
|
|
|
64
66
|
if (!rootSpan) span.finish()
|
|
@@ -6,6 +6,7 @@ const remoteConfig = require('./remote_config')
|
|
|
6
6
|
const {
|
|
7
7
|
bodyParser,
|
|
8
8
|
cookieParser,
|
|
9
|
+
multerParser,
|
|
9
10
|
incomingHttpRequestStart,
|
|
10
11
|
incomingHttpRequestEnd,
|
|
11
12
|
passportVerify,
|
|
@@ -58,6 +59,7 @@ function enable (_config) {
|
|
|
58
59
|
apiSecuritySampler.configure(_config.appsec)
|
|
59
60
|
|
|
60
61
|
bodyParser.subscribe(onRequestBodyParsed)
|
|
62
|
+
multerParser.subscribe(onRequestBodyParsed)
|
|
61
63
|
cookieParser.subscribe(onRequestCookieParser)
|
|
62
64
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
63
65
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
@@ -299,6 +301,7 @@ function disable () {
|
|
|
299
301
|
|
|
300
302
|
// Channel#unsubscribe() is undefined for non active channels
|
|
301
303
|
if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed)
|
|
304
|
+
if (multerParser.hasSubscribers) multerParser.unsubscribe(onRequestBodyParsed)
|
|
302
305
|
if (cookieParser.hasSubscribers) cookieParser.unsubscribe(onRequestCookieParser)
|
|
303
306
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
|
|
304
307
|
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
|
|
@@ -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
|
+
}
|
|
@@ -6,6 +6,7 @@ const { block, isBlocked } = require('../blocking')
|
|
|
6
6
|
const ssrf = require('./ssrf')
|
|
7
7
|
const sqli = require('./sql_injection')
|
|
8
8
|
const lfi = require('./lfi')
|
|
9
|
+
const cmdi = require('./command_injection')
|
|
9
10
|
|
|
10
11
|
const { DatadogRaspAbortError } = require('./utils')
|
|
11
12
|
|
|
@@ -95,6 +96,7 @@ function enable (config) {
|
|
|
95
96
|
ssrf.enable(config)
|
|
96
97
|
sqli.enable(config)
|
|
97
98
|
lfi.enable(config)
|
|
99
|
+
cmdi.enable(config)
|
|
98
100
|
|
|
99
101
|
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
100
102
|
expressMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
|
|
@@ -104,6 +106,7 @@ function disable () {
|
|
|
104
106
|
ssrf.disable()
|
|
105
107
|
sqli.disable()
|
|
106
108
|
lfi.disable()
|
|
109
|
+
cmdi.disable()
|
|
107
110
|
|
|
108
111
|
process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
109
112
|
if (expressMiddlewareError.hasSubscribers) expressMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
|
|
@@ -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,9 +12,10 @@ if (abortOnUncaughtException) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const RULE_TYPES = {
|
|
15
|
-
|
|
15
|
+
COMMAND_INJECTION: 'command_injection',
|
|
16
|
+
LFI: 'lfi',
|
|
16
17
|
SQL_INJECTION: 'sql_injection',
|
|
17
|
-
|
|
18
|
+
SSRF: 'ssrf'
|
|
18
19
|
}
|
|
19
20
|
|
|
20
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
|
{
|
|
@@ -6335,7 +6335,6 @@
|
|
|
6335
6335
|
{
|
|
6336
6336
|
"id": "rasp-934-100",
|
|
6337
6337
|
"name": "Server-side request forgery exploit",
|
|
6338
|
-
"enabled": false,
|
|
6339
6338
|
"tags": {
|
|
6340
6339
|
"type": "ssrf",
|
|
6341
6340
|
"category": "vulnerability_trigger",
|
|
@@ -6384,7 +6383,6 @@
|
|
|
6384
6383
|
{
|
|
6385
6384
|
"id": "rasp-942-100",
|
|
6386
6385
|
"name": "SQL injection exploit",
|
|
6387
|
-
"enabled": false,
|
|
6388
6386
|
"tags": {
|
|
6389
6387
|
"type": "sql_injection",
|
|
6390
6388
|
"category": "vulnerability_trigger",
|
|
@@ -6424,7 +6422,7 @@
|
|
|
6424
6422
|
}
|
|
6425
6423
|
]
|
|
6426
6424
|
},
|
|
6427
|
-
"operator": "sqli_detector"
|
|
6425
|
+
"operator": "sqli_detector@v2"
|
|
6428
6426
|
}
|
|
6429
6427
|
],
|
|
6430
6428
|
"transformers": [],
|
|
@@ -83,6 +83,7 @@ function enableWafUpdate (appsecConfig) {
|
|
|
83
83
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, true)
|
|
84
84
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, true)
|
|
85
85
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, true)
|
|
86
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, true)
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// TODO: delete noop handlers and kPreUpdate and replace with batched handlers
|
|
@@ -114,6 +115,7 @@ function disableWafUpdate () {
|
|
|
114
115
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
|
|
115
116
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
|
|
116
117
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, false)
|
|
118
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, false)
|
|
117
119
|
|
|
118
120
|
rc.removeProductHandler('ASM_DATA')
|
|
119
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
|
}
|
|
@@ -184,6 +183,8 @@ function finishRequest (req, res) {
|
|
|
184
183
|
if (metricsQueue.size) {
|
|
185
184
|
rootSpan.addTags(Object.fromEntries(metricsQueue))
|
|
186
185
|
|
|
186
|
+
keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
|
|
187
|
+
|
|
187
188
|
standalone.sample(rootSpan)
|
|
188
189
|
|
|
189
190
|
metricsQueue.clear()
|
|
@@ -2,10 +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')
|
|
8
7
|
const waf = require('../waf')
|
|
8
|
+
const { SAMPLING_MECHANISM_APPSEC } = require('../../constants')
|
|
9
|
+
const { keepTrace } = require('../../priority_sampler')
|
|
9
10
|
|
|
10
11
|
function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
11
12
|
// TODO: better user check here and in _setUser() ?
|
|
@@ -55,9 +56,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
|
55
56
|
return
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
|
|
60
|
+
|
|
58
61
|
const tags = {
|
|
59
|
-
[`appsec.events.${eventName}.track`]: 'true'
|
|
60
|
-
[MANUAL_KEEP]: 'true'
|
|
62
|
+
[`appsec.events.${eventName}.track`]: 'true'
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
if (mode === 'sdk') {
|
|
@@ -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
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line max-len
|
|
4
|
+
// Modeled after https://github.com/DataDog/libdatadog/blob/f3994857a59bb5679a65967138c5a3aec418a65f/ddcommon/src/azure_app_services.rs
|
|
5
|
+
|
|
6
|
+
const os = require('os')
|
|
7
|
+
const { getIsAzureFunction } = require('./serverless')
|
|
8
|
+
|
|
9
|
+
function extractSubscriptionID (ownerName) {
|
|
10
|
+
if (ownerName !== undefined) {
|
|
11
|
+
const subId = ownerName.split('+')[0].trim()
|
|
12
|
+
if (subId.length > 0) {
|
|
13
|
+
return subId
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function extractResourceGroup (ownerName) {
|
|
20
|
+
return /.+\+(.+)-.+webspace(-Linux)?/.exec(ownerName)?.[1]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildResourceID (subscriptionID, siteName, resourceGroup) {
|
|
24
|
+
if (subscriptionID === undefined || siteName === undefined || resourceGroup === undefined) {
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
return `/subscriptions/${subscriptionID}/resourcegroups/${resourceGroup}/providers/microsoft.web/sites/${siteName}`
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function trimObject (obj) {
|
|
32
|
+
Object.entries(obj)
|
|
33
|
+
.filter(([_, value]) => value === undefined)
|
|
34
|
+
.forEach(([key, _]) => { delete obj[key] })
|
|
35
|
+
return obj
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildMetadata () {
|
|
39
|
+
const {
|
|
40
|
+
COMPUTERNAME,
|
|
41
|
+
DD_AAS_DOTNET_EXTENSION_VERSION,
|
|
42
|
+
FUNCTIONS_EXTENSION_VERSION,
|
|
43
|
+
FUNCTIONS_WORKER_RUNTIME,
|
|
44
|
+
FUNCTIONS_WORKER_RUNTIME_VERSION,
|
|
45
|
+
WEBSITE_INSTANCE_ID,
|
|
46
|
+
WEBSITE_OWNER_NAME,
|
|
47
|
+
WEBSITE_OS,
|
|
48
|
+
WEBSITE_RESOURCE_GROUP,
|
|
49
|
+
WEBSITE_SITE_NAME
|
|
50
|
+
} = process.env
|
|
51
|
+
|
|
52
|
+
const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
|
|
53
|
+
|
|
54
|
+
const siteName = WEBSITE_SITE_NAME
|
|
55
|
+
|
|
56
|
+
const [siteKind, siteType] = getIsAzureFunction()
|
|
57
|
+
? ['functionapp', 'function']
|
|
58
|
+
: ['app', 'app']
|
|
59
|
+
|
|
60
|
+
const resourceGroup = WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME)
|
|
61
|
+
|
|
62
|
+
return trimObject({
|
|
63
|
+
extensionVersion: DD_AAS_DOTNET_EXTENSION_VERSION,
|
|
64
|
+
functionRuntimeVersion: FUNCTIONS_EXTENSION_VERSION,
|
|
65
|
+
instanceID: WEBSITE_INSTANCE_ID,
|
|
66
|
+
instanceName: COMPUTERNAME,
|
|
67
|
+
operatingSystem: WEBSITE_OS ?? os.platform(),
|
|
68
|
+
resourceGroup,
|
|
69
|
+
resourceID: buildResourceID(subscriptionID, siteName, resourceGroup),
|
|
70
|
+
runtime: FUNCTIONS_WORKER_RUNTIME,
|
|
71
|
+
runtimeVersion: FUNCTIONS_WORKER_RUNTIME_VERSION,
|
|
72
|
+
siteKind,
|
|
73
|
+
siteName,
|
|
74
|
+
siteType,
|
|
75
|
+
subscriptionID
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getAzureAppMetadata () {
|
|
80
|
+
// DD_AZURE_APP_SERVICES is an environment variable introduced by the .NET APM team and is set automatically for
|
|
81
|
+
// anyone using the Datadog APM Extensions (.NET, Java, or Node) for Windows Azure App Services
|
|
82
|
+
// eslint-disable-next-line max-len
|
|
83
|
+
// See: https://github.com/DataDog/datadog-aas-extension/blob/01f94b5c28b7fa7a9ab264ca28bd4e03be603900/node/src/applicationHost.xdt#L20-L21
|
|
84
|
+
return process.env.DD_AZURE_APP_SERVICES !== undefined ? buildMetadata() : undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getAzureFunctionMetadata () {
|
|
88
|
+
return getIsAzureFunction() ? buildMetadata() : undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line max-len
|
|
92
|
+
// Modeled after https://github.com/DataDog/libdatadog/blob/92272e90a7919f07178f3246ef8f82295513cfed/profiling/src/exporter/mod.rs#L187
|
|
93
|
+
// eslint-disable-next-line max-len
|
|
94
|
+
// and https://github.com/DataDog/libdatadog/blob/f3994857a59bb5679a65967138c5a3aec418a65f/trace-utils/src/trace_utils.rs#L533
|
|
95
|
+
function getAzureTagsFromMetadata (metadata) {
|
|
96
|
+
if (metadata === undefined) {
|
|
97
|
+
return {}
|
|
98
|
+
}
|
|
99
|
+
return trimObject({
|
|
100
|
+
'aas.environment.extension_version': metadata.extensionVersion,
|
|
101
|
+
'aas.environment.function_runtime': metadata.functionRuntimeVersion,
|
|
102
|
+
'aas.environment.instance_id': metadata.instanceID,
|
|
103
|
+
'aas.environment.instance_name': metadata.instanceName,
|
|
104
|
+
'aas.environment.os': metadata.operatingSystem,
|
|
105
|
+
'aas.environment.runtime': metadata.runtime,
|
|
106
|
+
'aas.environment.runtime_version': metadata.runtimeVersion,
|
|
107
|
+
'aas.resource.group': metadata.resourceGroup,
|
|
108
|
+
'aas.resource.id': metadata.resourceID,
|
|
109
|
+
'aas.site.kind': metadata.siteKind,
|
|
110
|
+
'aas.site.name': metadata.siteName,
|
|
111
|
+
'aas.site.type': metadata.siteType,
|
|
112
|
+
'aas.subscription.id': metadata.subscriptionID
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
getAzureAppMetadata,
|
|
118
|
+
getAzureFunctionMetadata,
|
|
119
|
+
getAzureTagsFromMetadata
|
|
120
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join } = require('path')
|
|
4
|
+
const { Worker } = require('worker_threads')
|
|
5
|
+
const { randomUUID } = require('crypto')
|
|
6
|
+
const log = require('../../log')
|
|
7
|
+
|
|
8
|
+
const probeIdToResolveBreakpointSet = new Map()
|
|
9
|
+
const probeIdToResolveBreakpointHit = new Map()
|
|
10
|
+
|
|
11
|
+
class TestVisDynamicInstrumentation {
|
|
12
|
+
constructor () {
|
|
13
|
+
this.worker = null
|
|
14
|
+
this._readyPromise = new Promise(resolve => {
|
|
15
|
+
this._onReady = resolve
|
|
16
|
+
})
|
|
17
|
+
this.breakpointSetChannel = new MessageChannel()
|
|
18
|
+
this.breakpointHitChannel = new MessageChannel()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Return 3 elements:
|
|
22
|
+
// 1. Snapshot ID
|
|
23
|
+
// 2. Promise that's resolved when the breakpoint is set
|
|
24
|
+
// 3. Promise that's resolved when the breakpoint is hit
|
|
25
|
+
addLineProbe ({ file, line }) {
|
|
26
|
+
const snapshotId = randomUUID()
|
|
27
|
+
const probeId = randomUUID()
|
|
28
|
+
|
|
29
|
+
this.breakpointSetChannel.port2.postMessage({
|
|
30
|
+
snapshotId,
|
|
31
|
+
probe: { id: probeId, file, line }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
snapshotId,
|
|
36
|
+
new Promise(resolve => {
|
|
37
|
+
probeIdToResolveBreakpointSet.set(probeId, resolve)
|
|
38
|
+
}),
|
|
39
|
+
new Promise(resolve => {
|
|
40
|
+
probeIdToResolveBreakpointHit.set(probeId, resolve)
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
isReady () {
|
|
46
|
+
return this._readyPromise
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
start () {
|
|
50
|
+
if (this.worker) return
|
|
51
|
+
|
|
52
|
+
const { NODE_OPTIONS, ...envWithoutNodeOptions } = process.env
|
|
53
|
+
|
|
54
|
+
log.debug('Starting Test Visibility - Dynamic Instrumentation client...')
|
|
55
|
+
|
|
56
|
+
this.worker = new Worker(
|
|
57
|
+
join(__dirname, 'worker', 'index.js'),
|
|
58
|
+
{
|
|
59
|
+
execArgv: [],
|
|
60
|
+
env: envWithoutNodeOptions,
|
|
61
|
+
workerData: {
|
|
62
|
+
breakpointSetChannel: this.breakpointSetChannel.port1,
|
|
63
|
+
breakpointHitChannel: this.breakpointHitChannel.port1
|
|
64
|
+
},
|
|
65
|
+
transferList: [this.breakpointSetChannel.port1, this.breakpointHitChannel.port1]
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
this.worker.on('online', () => {
|
|
69
|
+
log.debug('Test Visibility - Dynamic Instrumentation client is ready')
|
|
70
|
+
this._onReady()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Allow the parent to exit even if the worker is still running
|
|
74
|
+
this.worker.unref()
|
|
75
|
+
|
|
76
|
+
this.breakpointSetChannel.port2.on('message', (message) => {
|
|
77
|
+
const { probeId } = message
|
|
78
|
+
const resolve = probeIdToResolveBreakpointSet.get(probeId)
|
|
79
|
+
if (resolve) {
|
|
80
|
+
resolve()
|
|
81
|
+
probeIdToResolveBreakpointSet.delete(probeId)
|
|
82
|
+
}
|
|
83
|
+
}).unref()
|
|
84
|
+
|
|
85
|
+
this.breakpointHitChannel.port2.on('message', (message) => {
|
|
86
|
+
const { snapshot } = message
|
|
87
|
+
const { probe: { id: probeId } } = snapshot
|
|
88
|
+
const resolve = probeIdToResolveBreakpointHit.get(probeId)
|
|
89
|
+
if (resolve) {
|
|
90
|
+
resolve({ snapshot })
|
|
91
|
+
probeIdToResolveBreakpointHit.delete(probeId)
|
|
92
|
+
}
|
|
93
|
+
}).unref()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = new TestVisDynamicInstrumentation()
|