dd-trace 2.39.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/kafkajs.js +11 -2
- package/packages/datadog-instrumentations/src/mocha.js +5 -3
- package/packages/datadog-instrumentations/src/redis.js +48 -5
- 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-kafkajs/src/producer.js +6 -1
- package/packages/datadog-plugin-openai/src/services.js +14 -10
- 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/dogstatsd.js +14 -1
- package/packages/dd-trace/src/metrics.js +2 -2
- 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
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('../../../../diagnostics_channel')
|
|
4
|
+
|
|
5
|
+
const iastLog = require('./iast-log')
|
|
6
|
+
const Plugin = require('../../plugins/plugin')
|
|
7
|
+
const iastTelemetry = require('./telemetry')
|
|
8
|
+
const { getInstrumentedMetric, getExecutedMetric, TagKey, EXECUTED_SOURCE } = require('./telemetry/iast-metric')
|
|
9
|
+
const { storage } = require('../../../../datadog-core')
|
|
10
|
+
const { getIastContext } = require('./iast-context')
|
|
11
|
+
const instrumentations = require('../../../../datadog-instrumentations/src/helpers/instrumentations')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Used by vulnerability sources and sinks to subscribe diagnostic channel events
|
|
15
|
+
* and indicate what kind of metrics the subscription provides
|
|
16
|
+
* - moduleName is used identify when a module is loaded and
|
|
17
|
+
* to increment the INSTRUMENTED_[SINK|SOURCE] metric when it occurs
|
|
18
|
+
* - channelName is the channel used by the hook to publish execution events
|
|
19
|
+
* - tag indicates the name of the metric: taint-tracking/source-types for Sources and analyzers type for Sinks
|
|
20
|
+
* - tagKey can be only SOURCE_TYPE (Source) or VULNERABILITY_TYPE (Sink)
|
|
21
|
+
*/
|
|
22
|
+
class IastPluginSubscription {
|
|
23
|
+
constructor (moduleName, channelName, tag, tagKey = TagKey.VULNERABILITY_TYPE) {
|
|
24
|
+
this.moduleName = moduleName
|
|
25
|
+
this.channelName = channelName
|
|
26
|
+
this.tag = tag
|
|
27
|
+
this.tagKey = tagKey
|
|
28
|
+
this.executedMetric = getExecutedMetric(this.tagKey)
|
|
29
|
+
this.instrumentedMetric = getInstrumentedMetric(this.tagKey)
|
|
30
|
+
this.moduleInstrumented = false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
increaseInstrumented () {
|
|
34
|
+
if (this.moduleInstrumented) return
|
|
35
|
+
|
|
36
|
+
this.moduleInstrumented = true
|
|
37
|
+
this.instrumentedMetric.inc(this.tag)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
increaseExecuted (iastContext) {
|
|
41
|
+
this.executedMetric.inc(this.tag, iastContext)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
matchesModuleInstrumented (name) {
|
|
45
|
+
// https module is a special case because it's events are published as http
|
|
46
|
+
name = name === 'https' ? 'http' : name
|
|
47
|
+
return this.moduleName === name
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class IastPlugin extends Plugin {
|
|
52
|
+
constructor () {
|
|
53
|
+
super()
|
|
54
|
+
this.configured = false
|
|
55
|
+
this.pluginSubs = []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_wrapHandler (handler) {
|
|
59
|
+
return (message, name) => {
|
|
60
|
+
try {
|
|
61
|
+
handler(message, name)
|
|
62
|
+
} catch (e) {
|
|
63
|
+
iastLog.errorAndPublish(e)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_getTelemetryHandler (iastSub) {
|
|
69
|
+
return () => {
|
|
70
|
+
try {
|
|
71
|
+
const iastContext = getIastContext(storage.getStore())
|
|
72
|
+
iastSub.increaseExecuted(iastContext)
|
|
73
|
+
} catch (e) {
|
|
74
|
+
iastLog.errorAndPublish(e)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_execHandlerAndIncMetric ({ handler, metric, tag, iastContext = getIastContext(storage.getStore()) }) {
|
|
80
|
+
try {
|
|
81
|
+
const result = handler()
|
|
82
|
+
iastTelemetry.isEnabled() && metric.inc(tag, iastContext)
|
|
83
|
+
return result
|
|
84
|
+
} catch (e) {
|
|
85
|
+
iastLog.errorAndPublish(e)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
addSub (iastSub, handler) {
|
|
90
|
+
if (typeof iastSub === 'string') {
|
|
91
|
+
super.addSub(iastSub, this._wrapHandler(handler))
|
|
92
|
+
} else {
|
|
93
|
+
iastSub = this._getAndRegisterSubscription(iastSub)
|
|
94
|
+
if (iastSub) {
|
|
95
|
+
super.addSub(iastSub.channelName, this._wrapHandler(handler))
|
|
96
|
+
|
|
97
|
+
if (iastTelemetry.isEnabled()) {
|
|
98
|
+
super.addSub(iastSub.channelName, this._getTelemetryHandler(iastSub))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onConfigure () {}
|
|
105
|
+
|
|
106
|
+
configure (config) {
|
|
107
|
+
if (typeof config !== 'object') {
|
|
108
|
+
config = { enabled: config }
|
|
109
|
+
}
|
|
110
|
+
if (config.enabled && !this.configured) {
|
|
111
|
+
this.onConfigure()
|
|
112
|
+
this.configured = true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (iastTelemetry.isEnabled()) {
|
|
116
|
+
if (config.enabled) {
|
|
117
|
+
this.enableTelemetry()
|
|
118
|
+
} else {
|
|
119
|
+
this.disableTelemetry()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
super.configure(config)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_getAndRegisterSubscription ({ moduleName, channelName, tag, tagKey }) {
|
|
127
|
+
if (!channelName && !moduleName) return
|
|
128
|
+
|
|
129
|
+
if (!moduleName) {
|
|
130
|
+
const firstSep = channelName.indexOf(':')
|
|
131
|
+
if (firstSep === -1) {
|
|
132
|
+
moduleName = channelName
|
|
133
|
+
} else {
|
|
134
|
+
const lastSep = channelName.indexOf(':', firstSep + 1)
|
|
135
|
+
moduleName = channelName.substring(firstSep + 1, lastSep !== -1 ? lastSep : channelName.length)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const iastSub = new IastPluginSubscription(moduleName, channelName, tag, tagKey)
|
|
140
|
+
this.pluginSubs.push(iastSub)
|
|
141
|
+
return iastSub
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
enableTelemetry () {
|
|
145
|
+
if (this.onInstrumentationLoadedListener) return
|
|
146
|
+
|
|
147
|
+
this.onInstrumentationLoadedListener = ({ name }) => this._onInstrumentationLoaded(name)
|
|
148
|
+
const loadChannel = channel('dd-trace:instrumentation:load')
|
|
149
|
+
loadChannel.subscribe(this.onInstrumentationLoadedListener)
|
|
150
|
+
|
|
151
|
+
// check for already instrumented modules
|
|
152
|
+
for (const name in instrumentations) {
|
|
153
|
+
this._onInstrumentationLoaded(name)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
disableTelemetry () {
|
|
158
|
+
if (!this.onInstrumentationLoadedListener) return
|
|
159
|
+
|
|
160
|
+
const loadChannel = channel('dd-trace:instrumentation:load')
|
|
161
|
+
if (loadChannel.hasSubscribers) {
|
|
162
|
+
loadChannel.unsubscribe(this.onInstrumentationLoadedListener)
|
|
163
|
+
}
|
|
164
|
+
this.onInstrumentationLoadedListener = null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
_onInstrumentationLoaded (name) {
|
|
168
|
+
this.pluginSubs
|
|
169
|
+
.filter(sub => sub.matchesModuleInstrumented(name))
|
|
170
|
+
.forEach(sub => sub.increaseInstrumented())
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class SourceIastPlugin extends IastPlugin {
|
|
175
|
+
addSub (iastPluginSub, handler) {
|
|
176
|
+
return super.addSub({ tagKey: TagKey.SOURCE_TYPE, ...iastPluginSub }, handler)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
addInstrumentedSource (moduleName, tag) {
|
|
180
|
+
this._getAndRegisterSubscription({
|
|
181
|
+
moduleName,
|
|
182
|
+
tag,
|
|
183
|
+
tagKey: TagKey.SOURCE_TYPE
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
execSource (sourceHandlerInfo) {
|
|
188
|
+
this._execHandlerAndIncMetric({
|
|
189
|
+
metric: EXECUTED_SOURCE,
|
|
190
|
+
...sourceHandlerInfo
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
class SinkIastPlugin extends IastPlugin {
|
|
196
|
+
addSub (iastPluginSub, handler) {
|
|
197
|
+
return super.addSub({ tagKey: TagKey.VULNERABILITY_TYPE, ...iastPluginSub }, handler)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
SourceIastPlugin,
|
|
203
|
+
SinkIastPlugin,
|
|
204
|
+
IastPlugin
|
|
205
|
+
}
|
|
@@ -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
|
|