dd-trace 2.40.0 → 2.41.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/package.json +3 -3
- package/packages/datadog-instrumentations/src/cucumber.js +5 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +44 -42
- package/packages/datadog-instrumentations/src/grpc/server.js +69 -60
- package/packages/datadog-instrumentations/src/http2/client.js +25 -26
- package/packages/datadog-instrumentations/src/jest.js +3 -1
- package/packages/datadog-instrumentations/src/mocha.js +5 -3
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
- package/packages/datadog-plugin-grpc/src/client.js +29 -11
- package/packages/datadog-plugin-grpc/src/server.js +22 -6
- package/packages/datadog-plugin-http2/src/client.js +46 -29
- package/packages/datadog-plugin-router/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +7 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +19 -15
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +18 -19
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +205 -0
- package/packages/dd-trace/src/appsec/iast/index.js +6 -5
- package/packages/dd-trace/src/appsec/iast/tags.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +32 -16
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +33 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +23 -16
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +76 -37
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +101 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +45 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/{logs.js → log/index.js} +5 -5
- package/packages/dd-trace/src/appsec/iast/telemetry/{log_collector.js → log/log-collector.js} +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +76 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +53 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +42 -0
- package/packages/dd-trace/src/config.js +21 -2
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/opentracing/tracer.js +1 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/outbound.js +29 -12
- package/packages/dd-trace/src/plugins/plugin.js +28 -0
- package/packages/dd-trace/src/plugins/tracing.js +33 -16
- package/packages/dd-trace/src/plugins/util/ci.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +55 -11
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
- package/packages/dd-trace/src/profiling/config.js +0 -3
- package/packages/dd-trace/src/profiling/index.js +0 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +23 -11
- package/packages/diagnostics_channel/src/index.js +64 -0
- package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
- /package/packages/dd-trace/src/appsec/iast/taint-tracking/{origin-types.js → source-types.js} +0 -0
|
@@ -13,8 +13,7 @@ const {
|
|
|
13
13
|
taintTrackingPlugin
|
|
14
14
|
} = require('./taint-tracking')
|
|
15
15
|
const { IAST_ENABLED_TAG_KEY } = require('./tags')
|
|
16
|
-
|
|
17
|
-
const telemetryLogs = require('./telemetry/logs')
|
|
16
|
+
const iastTelemetry = require('./telemetry')
|
|
18
17
|
|
|
19
18
|
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
|
|
20
19
|
// order of the callbacks can be enforce
|
|
@@ -22,24 +21,24 @@ const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
|
|
|
22
21
|
const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
|
|
23
22
|
|
|
24
23
|
function enable (config, _tracer) {
|
|
24
|
+
iastTelemetry.configure(config, config.iast && config.iast.telemetryVerbosity)
|
|
25
25
|
enableAllAnalyzers()
|
|
26
|
-
enableTaintTracking(config.iast)
|
|
26
|
+
enableTaintTracking(config.iast, iastTelemetry.verbosity)
|
|
27
27
|
requestStart.subscribe(onIncomingHttpRequestStart)
|
|
28
28
|
requestClose.subscribe(onIncomingHttpRequestEnd)
|
|
29
29
|
overheadController.configure(config.iast)
|
|
30
30
|
overheadController.startGlobalContext()
|
|
31
31
|
vulnerabilityReporter.start(config, _tracer)
|
|
32
|
-
telemetryLogs.start()
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
function disable () {
|
|
35
|
+
iastTelemetry.stop()
|
|
36
36
|
disableAllAnalyzers()
|
|
37
37
|
disableTaintTracking()
|
|
38
38
|
overheadController.finishGlobalContext()
|
|
39
39
|
if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
|
|
40
40
|
if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
|
|
41
41
|
vulnerabilityReporter.stop()
|
|
42
|
-
telemetryLogs.stop()
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
function onIncomingHttpRequestStart (data) {
|
|
@@ -54,6 +53,7 @@ function onIncomingHttpRequestStart (data) {
|
|
|
54
53
|
const iastContext = iastContextFunctions.saveIastContext(store, topContext, { rootSpan, req: data.req })
|
|
55
54
|
createTransaction(rootSpan.context().toSpanId(), iastContext)
|
|
56
55
|
overheadController.initializeRequestContext(iastContext)
|
|
56
|
+
iastTelemetry.onRequestStart(iastContext)
|
|
57
57
|
taintTrackingPlugin.taintHeaders(data.req.headers, iastContext)
|
|
58
58
|
}
|
|
59
59
|
if (rootSpan.addTags) {
|
|
@@ -76,6 +76,7 @@ function onIncomingHttpRequestEnd (data) {
|
|
|
76
76
|
const rootSpan = iastContext.rootSpan
|
|
77
77
|
vulnerabilityReporter.sendVulnerabilities(vulnerabilities, rootSpan)
|
|
78
78
|
removeTransaction(iastContext)
|
|
79
|
+
iastTelemetry.onRequestEnd(iastContext, iastContext.rootSpan)
|
|
79
80
|
}
|
|
80
81
|
// TODO web.getContext(data.req) is required when the request is aborted
|
|
81
82
|
if (iastContextFunctions.cleanIastContext(store, topContext, iastContext)) {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const csiMethods = [
|
|
4
|
+
{ src: 'concat' },
|
|
4
5
|
{ src: 'plusOperator', operator: true },
|
|
6
|
+
{ src: 'replace' },
|
|
7
|
+
{ src: 'slice' },
|
|
8
|
+
{ src: 'substr' },
|
|
9
|
+
{ src: 'substring' },
|
|
5
10
|
{ src: 'trim' },
|
|
6
|
-
{ src: 'trimStart', dst: 'trim' },
|
|
7
11
|
{ src: 'trimEnd' },
|
|
8
|
-
{ src: '
|
|
9
|
-
{ src: 'substring' },
|
|
10
|
-
{ src: 'substr' },
|
|
11
|
-
{ src: 'slice' },
|
|
12
|
-
{ src: 'replace' }
|
|
12
|
+
{ src: 'trimStart', dst: 'trim' }
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
module.exports = {
|
|
@@ -10,9 +10,9 @@ const { createTransaction,
|
|
|
10
10
|
const taintTrackingPlugin = require('./plugin')
|
|
11
11
|
|
|
12
12
|
module.exports = {
|
|
13
|
-
enableTaintTracking (config) {
|
|
14
|
-
enableRewriter()
|
|
15
|
-
enableTaintOperations()
|
|
13
|
+
enableTaintTracking (config, telemetryVerbosity) {
|
|
14
|
+
enableRewriter(telemetryVerbosity)
|
|
15
|
+
enableTaintOperations(telemetryVerbosity)
|
|
16
16
|
taintTrackingPlugin.enable()
|
|
17
17
|
setMaxTransactions(config.maxConcurrentRequests)
|
|
18
18
|
},
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
4
4
|
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
5
5
|
const iastLog = require('../iast-log')
|
|
6
|
-
const
|
|
6
|
+
const iastTelemetry = require('../telemetry')
|
|
7
|
+
const { REQUEST_TAINTED } = require('../telemetry/iast-metric')
|
|
8
|
+
const { isInfoAllowed } = require('../telemetry/verbosity')
|
|
9
|
+
const { getTaintTrackingImpl, getTaintTrackingNoop } = require('./taint-tracking-impl')
|
|
7
10
|
|
|
8
11
|
function createTransaction (id, iastContext) {
|
|
9
12
|
if (id && iastContext) {
|
|
@@ -11,9 +14,21 @@ function createTransaction (id, iastContext) {
|
|
|
11
14
|
}
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
let onRemoveTransaction = (transactionId, iastContext) => {}
|
|
18
|
+
|
|
19
|
+
function onRemoveTransactionInformationTelemetry (transactionId, iastContext) {
|
|
20
|
+
const metrics = TaintedUtils.getMetrics(transactionId, iastTelemetry.verbosity)
|
|
21
|
+
if (metrics && metrics.requestCount) {
|
|
22
|
+
REQUEST_TAINTED.add(metrics.requestCount, null, iastContext)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
function removeTransaction (iastContext) {
|
|
15
27
|
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
16
28
|
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
29
|
+
|
|
30
|
+
onRemoveTransaction(transactionId, iastContext)
|
|
31
|
+
|
|
17
32
|
TaintedUtils.removeTransaction(transactionId)
|
|
18
33
|
delete iastContext[IAST_TRANSACTION_ID]
|
|
19
34
|
}
|
|
@@ -96,12 +111,16 @@ function getRanges (iastContext, string) {
|
|
|
96
111
|
return result
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
function enableTaintOperations () {
|
|
100
|
-
|
|
114
|
+
function enableTaintOperations (telemetryVerbosity) {
|
|
115
|
+
if (isInfoAllowed(telemetryVerbosity)) {
|
|
116
|
+
onRemoveTransaction = onRemoveTransactionInformationTelemetry
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
global._ddiast = getTaintTrackingImpl(telemetryVerbosity)
|
|
101
120
|
}
|
|
102
121
|
|
|
103
122
|
function disableTaintOperations () {
|
|
104
|
-
global._ddiast =
|
|
123
|
+
global._ddiast = getTaintTrackingNoop()
|
|
105
124
|
}
|
|
106
125
|
|
|
107
126
|
function setMaxTransactions (transactions) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { SourceIastPlugin } = require('../iast-plugin')
|
|
4
4
|
const { getIastContext } = require('../iast-context')
|
|
5
5
|
const { storage } = require('../../../../../datadog-core')
|
|
6
6
|
const { taintObject } = require('./operations')
|
|
@@ -12,15 +12,17 @@ const {
|
|
|
12
12
|
HTTP_REQUEST_HEADER_NAME,
|
|
13
13
|
HTTP_REQUEST_PARAMETER,
|
|
14
14
|
HTTP_REQUEST_PATH_PARAM
|
|
15
|
+
} = require('./source-types')
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class TaintTrackingPlugin extends Plugin {
|
|
17
|
+
class TaintTrackingPlugin extends SourceIastPlugin {
|
|
19
18
|
constructor () {
|
|
20
19
|
super()
|
|
21
20
|
this._type = 'taint-tracking'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onConfigure () {
|
|
22
24
|
this.addSub(
|
|
23
|
-
'datadog:body-parser:read:finish',
|
|
25
|
+
{ channelName: 'datadog:body-parser:read:finish', tag: HTTP_REQUEST_BODY },
|
|
24
26
|
({ req }) => {
|
|
25
27
|
const iastContext = getIastContext(storage.getStore())
|
|
26
28
|
if (iastContext && iastContext['body'] !== req.body) {
|
|
@@ -29,31 +31,41 @@ class TaintTrackingPlugin extends Plugin {
|
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
)
|
|
34
|
+
|
|
32
35
|
this.addSub(
|
|
33
|
-
'datadog:qs:parse:finish',
|
|
36
|
+
{ channelName: 'datadog:qs:parse:finish', tag: HTTP_REQUEST_PARAMETER },
|
|
34
37
|
({ qs }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, qs)
|
|
35
38
|
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
iastContext
|
|
39
|
+
|
|
40
|
+
this.addSub(
|
|
41
|
+
{ channelName: 'apm:express:middleware:next', tag: HTTP_REQUEST_BODY },
|
|
42
|
+
({ req }) => {
|
|
43
|
+
if (req && req.body && typeof req.body === 'object') {
|
|
44
|
+
const iastContext = getIastContext(storage.getStore())
|
|
45
|
+
if (iastContext && iastContext['body'] !== req.body) {
|
|
46
|
+
this._taintTrackingHandler(HTTP_REQUEST_BODY, req, 'body', iastContext)
|
|
47
|
+
iastContext['body'] = req.body
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
|
-
|
|
51
|
+
)
|
|
52
|
+
|
|
45
53
|
this.addSub(
|
|
46
|
-
'datadog:cookie:parse:finish',
|
|
54
|
+
{ channelName: 'datadog:cookie:parse:finish', tag: [HTTP_REQUEST_COOKIE_VALUE, HTTP_REQUEST_COOKIE_NAME] },
|
|
47
55
|
({ cookies }) => this._cookiesTaintTrackingHandler(cookies)
|
|
48
56
|
)
|
|
57
|
+
|
|
49
58
|
this.addSub(
|
|
50
|
-
'datadog:express:process_params:start',
|
|
59
|
+
{ channelName: 'datadog:express:process_params:start', tag: HTTP_REQUEST_PATH_PARAM },
|
|
51
60
|
({ req }) => {
|
|
52
61
|
if (req && req.params && typeof req.params === 'object') {
|
|
53
62
|
this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
)
|
|
66
|
+
|
|
67
|
+
// this is a special case to increment INSTRUMENTED_SOURCE metric for header
|
|
68
|
+
this.addInstrumentedSource('http', [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME])
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
_taintTrackingHandler (type, target, property, iastContext = getIastContext(storage.getStore())) {
|
|
@@ -70,7 +82,11 @@ class TaintTrackingPlugin extends Plugin {
|
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
taintHeaders (headers, iastContext) {
|
|
73
|
-
|
|
85
|
+
this.execSource({
|
|
86
|
+
handler: () => taintObject(iastContext, headers, HTTP_REQUEST_HEADER_VALUE, true, HTTP_REQUEST_HEADER_NAME),
|
|
87
|
+
tag: [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME],
|
|
88
|
+
iastContext
|
|
89
|
+
})
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
enable () {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const iastTelemetry = require('../telemetry')
|
|
4
|
+
const { Verbosity } = require('../telemetry/verbosity')
|
|
5
|
+
const { INSTRUMENTED_PROPAGATION } = require('../telemetry/iast-metric')
|
|
6
|
+
|
|
7
|
+
const telemetryRewriter = {
|
|
8
|
+
off (content, filename, rewriter) {
|
|
9
|
+
return rewriter.rewrite(content, filename)
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
information (content, filename, rewriter) {
|
|
13
|
+
const response = this.off(content, filename, rewriter)
|
|
14
|
+
|
|
15
|
+
const metrics = response.metrics
|
|
16
|
+
if (metrics && metrics.instrumentedPropagation) {
|
|
17
|
+
INSTRUMENTED_PROPAGATION.add(metrics.instrumentedPropagation)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return response
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getRewriteFunction (rewriter) {
|
|
25
|
+
switch (iastTelemetry.verbosity) {
|
|
26
|
+
case Verbosity.OFF:
|
|
27
|
+
return (content, filename) => telemetryRewriter.off(content, filename, rewriter)
|
|
28
|
+
default:
|
|
29
|
+
return (content, filename) => telemetryRewriter.information(content, filename, rewriter)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { getRewriteFunction }
|
|
@@ -5,20 +5,17 @@ const shimmer = require('../../../../../datadog-shimmer')
|
|
|
5
5
|
const iastLog = require('../iast-log')
|
|
6
6
|
const { isPrivateModule, isNotLibraryFile } = require('./filter')
|
|
7
7
|
const { csiMethods } = require('./csi-methods')
|
|
8
|
+
const { getName } = require('../telemetry/verbosity')
|
|
9
|
+
const { getRewriteFunction } = require('./rewriter-telemetry')
|
|
8
10
|
|
|
9
11
|
let rewriter
|
|
10
12
|
let getPrepareStackTrace
|
|
11
|
-
function getRewriter () {
|
|
13
|
+
function getRewriter (telemetryVerbosity) {
|
|
12
14
|
if (!rewriter) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
rewriter = new Rewriter({ csiMethods })
|
|
18
|
-
} catch (e) {
|
|
19
|
-
iastLog.error('Unable to initialize TaintTracking Rewriter')
|
|
20
|
-
.errorAndPublish(e)
|
|
21
|
-
}
|
|
15
|
+
const iastRewriter = require('@datadog/native-iast-rewriter')
|
|
16
|
+
const Rewriter = iastRewriter.Rewriter
|
|
17
|
+
getPrepareStackTrace = iastRewriter.getPrepareStackTrace
|
|
18
|
+
rewriter = new Rewriter({ csiMethods, telemetryVerbosity: getName(telemetryVerbosity) })
|
|
22
19
|
}
|
|
23
20
|
return rewriter
|
|
24
21
|
}
|
|
@@ -27,6 +24,7 @@ let originalPrepareStackTrace = Error.prepareStackTrace
|
|
|
27
24
|
function getPrepareStackTraceAccessor () {
|
|
28
25
|
let actual = getPrepareStackTrace(originalPrepareStackTrace)
|
|
29
26
|
return {
|
|
27
|
+
configurable: true,
|
|
30
28
|
get () {
|
|
31
29
|
return actual
|
|
32
30
|
},
|
|
@@ -38,10 +36,11 @@ function getPrepareStackTraceAccessor () {
|
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
function getCompileMethodFn (compileMethod) {
|
|
39
|
+
const rewriteFn = getRewriteFunction(rewriter)
|
|
41
40
|
return function (content, filename) {
|
|
42
41
|
try {
|
|
43
42
|
if (isPrivateModule(filename) && isNotLibraryFile(filename)) {
|
|
44
|
-
const rewritten =
|
|
43
|
+
const rewritten = rewriteFn(content, filename)
|
|
45
44
|
if (rewritten && rewritten.content) {
|
|
46
45
|
return compileMethod.apply(this, [rewritten.content, filename])
|
|
47
46
|
}
|
|
@@ -54,11 +53,19 @@ function getCompileMethodFn (compileMethod) {
|
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
function enableRewriter () {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
function enableRewriter (telemetryVerbosity) {
|
|
57
|
+
try {
|
|
58
|
+
const rewriter = getRewriter(telemetryVerbosity)
|
|
59
|
+
if (rewriter) {
|
|
60
|
+
const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
|
|
61
|
+
if (!pstDescriptor || pstDescriptor.configurable) {
|
|
62
|
+
Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
|
|
63
|
+
}
|
|
64
|
+
shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
iastLog.error('Error enabling TaintTracking Rewriter')
|
|
68
|
+
.errorAndPublish(e)
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -4,30 +4,45 @@ const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
|
4
4
|
const { storage } = require('../../../../../datadog-core')
|
|
5
5
|
const iastContextFunctions = require('../iast-context')
|
|
6
6
|
const iastLog = require('../iast-log')
|
|
7
|
+
const { EXECUTED_PROPAGATION } = require('../telemetry/iast-metric')
|
|
8
|
+
const { isDebugAllowed } = require('../telemetry/verbosity')
|
|
7
9
|
|
|
8
10
|
function noop (res) { return res }
|
|
9
|
-
|
|
11
|
+
// NOTE: methods of this object must be synchronized with csi-methods.js file definitions!
|
|
12
|
+
// Otherwise you may end up rewriting a method and not providing its rewritten implementation
|
|
13
|
+
const TaintTrackingNoop = {
|
|
10
14
|
plusOperator: noop,
|
|
11
|
-
trim: noop,
|
|
12
|
-
trimEnd: noop,
|
|
13
15
|
concat: noop,
|
|
14
|
-
|
|
15
|
-
substr: noop,
|
|
16
|
+
replace: noop,
|
|
16
17
|
slice: noop,
|
|
17
|
-
|
|
18
|
+
substr: noop,
|
|
19
|
+
substring: noop,
|
|
20
|
+
trim: noop,
|
|
21
|
+
trimEnd: noop
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
function getTransactionId () {
|
|
21
|
-
const store = storage.getStore()
|
|
22
|
-
const iastContext = iastContextFunctions.getIastContext(store)
|
|
24
|
+
function getTransactionId (iastContext) {
|
|
23
25
|
return iastContext && iastContext[iastContextFunctions.IAST_TRANSACTION_ID]
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
function
|
|
28
|
+
function getContextDefault () {
|
|
29
|
+
const store = storage.getStore()
|
|
30
|
+
return iastContextFunctions.getIastContext(store)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getContextDebug () {
|
|
34
|
+
const iastContext = getContextDefault()
|
|
35
|
+
EXECUTED_PROPAGATION.inc(null, iastContext)
|
|
36
|
+
return iastContext
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getFilteredCsiFn (cb, filter, getContext) {
|
|
27
40
|
return function csiCall (res, fn, target, ...rest) {
|
|
28
41
|
try {
|
|
29
42
|
if (filter(res, fn, target)) { return res }
|
|
30
|
-
|
|
43
|
+
|
|
44
|
+
const context = getContext()
|
|
45
|
+
const transactionId = getTransactionId(context)
|
|
31
46
|
if (transactionId) {
|
|
32
47
|
return cb(transactionId, res, target, ...rest)
|
|
33
48
|
}
|
|
@@ -47,7 +62,7 @@ function isValidCsiMethod (fn, protos) {
|
|
|
47
62
|
return protos.some(proto => fn === proto)
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
function getCsiFn (cb, ...protos) {
|
|
65
|
+
function getCsiFn (cb, getContext, ...protos) {
|
|
51
66
|
let filter
|
|
52
67
|
if (!protos || protos.length === 0) {
|
|
53
68
|
filter = (res, fn, target) => notString(res, target)
|
|
@@ -57,49 +72,73 @@ function getCsiFn (cb, ...protos) {
|
|
|
57
72
|
} else {
|
|
58
73
|
filter = (res, fn, target) => notString(res, target) || !isValidCsiMethod(fn, protos)
|
|
59
74
|
}
|
|
60
|
-
return getFilteredCsiFn(cb, filter)
|
|
75
|
+
return getFilteredCsiFn(cb, filter, getContext)
|
|
61
76
|
}
|
|
62
77
|
|
|
63
|
-
function csiMethodsDefaults (names, excluded) {
|
|
78
|
+
function csiMethodsDefaults (names, excluded, getContext) {
|
|
64
79
|
const impl = {}
|
|
65
80
|
names.forEach(name => {
|
|
66
81
|
if (excluded.indexOf(name) !== -1) return
|
|
67
82
|
impl[name] = getCsiFn(
|
|
68
83
|
(transactionId, res, target, ...rest) => TaintedUtils[name](transactionId, res, target, ...rest),
|
|
84
|
+
getContext,
|
|
69
85
|
String.prototype[name]
|
|
70
86
|
)
|
|
71
87
|
})
|
|
72
88
|
return impl
|
|
73
89
|
}
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
function csiMethodsOverrides (getContext) {
|
|
92
|
+
return {
|
|
93
|
+
plusOperator: function (res, op1, op2) {
|
|
94
|
+
try {
|
|
95
|
+
if (notString(res) || (notString(op1) && notString(op2))) { return res }
|
|
96
|
+
const iastContext = getContext()
|
|
97
|
+
const transactionId = getTransactionId(iastContext)
|
|
98
|
+
if (transactionId) {
|
|
99
|
+
return TaintedUtils.concat(transactionId, res, op1, op2)
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {
|
|
102
|
+
iastLog.error(`Error invoking CSI plusOperator`)
|
|
103
|
+
.errorAndPublish(e)
|
|
82
104
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
return res
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
trim: getCsiFn(
|
|
109
|
+
(transactionId, res, target) => TaintedUtils.trim(transactionId, res, target),
|
|
110
|
+
getContext,
|
|
111
|
+
String.prototype.trim,
|
|
112
|
+
String.prototype.trimStart
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createImplWith (getContext) {
|
|
118
|
+
const methodNames = Object.keys(TaintTrackingNoop)
|
|
119
|
+
const overrides = csiMethodsOverrides(getContext)
|
|
120
|
+
|
|
121
|
+
// impls could be cached but at the moment there is only one invocation to getTaintTrackingImpl
|
|
122
|
+
return {
|
|
123
|
+
...csiMethodsDefaults(methodNames, Object.keys(overrides), getContext),
|
|
124
|
+
...overrides
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getTaintTrackingImpl (telemetryVerbosity, dummy = false) {
|
|
129
|
+
if (dummy) return TaintTrackingNoop
|
|
89
130
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
131
|
+
// with Verbosity.DEBUG every invocation of a TaintedUtils method increases the EXECUTED_PROPAGATION metric
|
|
132
|
+
return isDebugAllowed(telemetryVerbosity)
|
|
133
|
+
? createImplWith(getContextDebug)
|
|
134
|
+
: createImplWith(getContextDefault)
|
|
95
135
|
}
|
|
96
136
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
...csiMethodsOverrides
|
|
137
|
+
function getTaintTrackingNoop () {
|
|
138
|
+
return getTaintTrackingImpl(null, true)
|
|
100
139
|
}
|
|
101
140
|
|
|
102
141
|
module.exports = {
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
getTaintTrackingImpl,
|
|
143
|
+
getTaintTrackingNoop
|
|
105
144
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { getNamespaceFromContext, globalNamespace } = require('./namespaces')
|
|
4
|
+
|
|
5
|
+
const Scope = {
|
|
6
|
+
GLOBAL: 'GLOBAL',
|
|
7
|
+
REQUEST: 'REQUEST'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const PropagationType = {
|
|
11
|
+
STRING: 'STRING',
|
|
12
|
+
JSON: 'JSON',
|
|
13
|
+
URL: 'URL'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TagKey = {
|
|
17
|
+
VULNERABILITY_TYPE: 'vulnerability_type',
|
|
18
|
+
SOURCE_TYPE: 'source_type',
|
|
19
|
+
PROPAGATION_TYPE: 'propagation_type'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class IastMetric {
|
|
23
|
+
constructor (name, scope, tagKey) {
|
|
24
|
+
this.name = name
|
|
25
|
+
this.scope = scope
|
|
26
|
+
this.tagKey = tagKey
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getNamespace (context) {
|
|
30
|
+
return getNamespaceFromContext(context) || globalNamespace
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getTag (tagValue) {
|
|
34
|
+
return tagValue ? { [this.tagKey]: tagValue } : undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
addValue (value, tagValue, context) {
|
|
38
|
+
this.getNamespace(context)
|
|
39
|
+
.count(this.name, this.getTag(tagValue))
|
|
40
|
+
.inc(value)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
add (value, tagValue, context) {
|
|
44
|
+
if (Array.isArray(tagValue)) {
|
|
45
|
+
tagValue.forEach(tag => this.addValue(value, tag, context))
|
|
46
|
+
} else {
|
|
47
|
+
this.addValue(value, tagValue, context)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
inc (tagValue, context) {
|
|
52
|
+
this.add(1, tagValue, context)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getExecutedMetric (tagKey) {
|
|
57
|
+
return tagKey === TagKey.VULNERABILITY_TYPE ? EXECUTED_SINK : EXECUTED_SOURCE
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getInstrumentedMetric (tagKey) {
|
|
61
|
+
return tagKey === TagKey.VULNERABILITY_TYPE ? INSTRUMENTED_SINK : INSTRUMENTED_SOURCE
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const INSTRUMENTED_PROPAGATION = new IastMetric('instrumented.propagation', Scope.GLOBAL)
|
|
65
|
+
const INSTRUMENTED_SOURCE = new IastMetric('instrumented.source', Scope.GLOBAL, TagKey.SOURCE_TYPE)
|
|
66
|
+
const INSTRUMENTED_SINK = new IastMetric('instrumented.sink', Scope.GLOBAL, TagKey.VULNERABILITY_TYPE)
|
|
67
|
+
|
|
68
|
+
const EXECUTED_SOURCE = new IastMetric('executed.source', Scope.REQUEST, TagKey.SOURCE_TYPE)
|
|
69
|
+
const EXECUTED_SINK = new IastMetric('executed.sink', Scope.REQUEST, TagKey.VULNERABILITY_TYPE)
|
|
70
|
+
|
|
71
|
+
const REQUEST_TAINTED = new IastMetric('request.tainted', Scope.REQUEST)
|
|
72
|
+
|
|
73
|
+
// DEBUG using metrics
|
|
74
|
+
const EXECUTED_PROPAGATION = new IastMetric('executed.propagation', Scope.REQUEST)
|
|
75
|
+
const EXECUTED_TAINTED = new IastMetric('executed.tainted', Scope.REQUEST)
|
|
76
|
+
|
|
77
|
+
// DEBUG using distribution endpoint
|
|
78
|
+
const INSTRUMENTATION_TIME = new IastMetric('instrumentation.time', Scope.GLOBAL)
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
INSTRUMENTED_PROPAGATION,
|
|
82
|
+
INSTRUMENTED_SOURCE,
|
|
83
|
+
INSTRUMENTED_SINK,
|
|
84
|
+
|
|
85
|
+
EXECUTED_PROPAGATION,
|
|
86
|
+
EXECUTED_SOURCE,
|
|
87
|
+
EXECUTED_SINK,
|
|
88
|
+
EXECUTED_TAINTED,
|
|
89
|
+
|
|
90
|
+
REQUEST_TAINTED,
|
|
91
|
+
|
|
92
|
+
INSTRUMENTATION_TIME,
|
|
93
|
+
|
|
94
|
+
PropagationType,
|
|
95
|
+
TagKey,
|
|
96
|
+
|
|
97
|
+
IastMetric,
|
|
98
|
+
|
|
99
|
+
getExecutedMetric,
|
|
100
|
+
getInstrumentedMetric
|
|
101
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const telemetryMetrics = require('../../../telemetry/metrics')
|
|
4
|
+
const telemetryLogs = require('./log')
|
|
5
|
+
const { Verbosity, getVerbosity } = require('./verbosity')
|
|
6
|
+
const { initRequestNamespace, finalizeRequestNamespace, globalNamespace } = require('./namespaces')
|
|
7
|
+
|
|
8
|
+
class Telemetry {
|
|
9
|
+
configure (config, verbosity) {
|
|
10
|
+
// in order to telemetry be enabled, tracer telemetry and metrics collection have to be enabled
|
|
11
|
+
this.enabled = config && config.telemetry && config.telemetry.enabled && config.telemetry.metrics
|
|
12
|
+
this.verbosity = this.enabled ? getVerbosity(verbosity) : Verbosity.OFF
|
|
13
|
+
|
|
14
|
+
if (this.enabled) {
|
|
15
|
+
telemetryMetrics.manager.set('iast', globalNamespace)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
telemetryLogs.start()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
stop () {
|
|
22
|
+
this.enabled = false
|
|
23
|
+
telemetryMetrics.manager.delete('iast')
|
|
24
|
+
|
|
25
|
+
telemetryLogs.stop()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
isEnabled () {
|
|
29
|
+
return this.enabled
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onRequestStart (context) {
|
|
33
|
+
if (this.isEnabled() && this.verbosity !== Verbosity.OFF) {
|
|
34
|
+
initRequestNamespace(context)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onRequestEnd (context, rootSpan) {
|
|
39
|
+
if (this.isEnabled() && this.verbosity !== Verbosity.OFF) {
|
|
40
|
+
finalizeRequestNamespace(context, rootSpan)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = new Telemetry()
|