dd-trace 2.23.0 → 2.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 +2 -0
- package/index.d.ts +33 -1
- package/package.json +10 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +7 -0
- package/packages/datadog-instrumentations/src/http/server.js +7 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
- package/packages/datadog-instrumentations/src/mocha.js +33 -8
- package/packages/datadog-instrumentations/src/pg.js +6 -2
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +7 -3
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +6 -3
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/blocking.js +44 -0
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/index.js +6 -1
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +11 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -3
- package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
- package/packages/dd-trace/src/appsec/iast/index.js +15 -3
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +6 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +17 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -29
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +16 -15
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +103 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +63 -41
- package/packages/dd-trace/src/appsec/index.js +68 -27
- package/packages/dd-trace/src/{plugins/util → appsec}/ip_blocklist.js +0 -0
- package/packages/dd-trace/src/appsec/ip_extractor.js +98 -0
- package/packages/dd-trace/src/appsec/recommended.json +75 -8
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/manager.js +2 -2
- package/packages/dd-trace/src/appsec/templates/blocked.html +99 -0
- package/packages/dd-trace/src/appsec/templates/blocked.json +8 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +18 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +12 -6
- package/packages/dd-trace/src/config.js +49 -8
- package/packages/dd-trace/src/exporters/common/request.js +33 -1
- package/packages/dd-trace/src/format.js +5 -1
- package/packages/dd-trace/src/lambda/handler.js +72 -0
- package/packages/dd-trace/src/lambda/index.js +5 -0
- package/packages/dd-trace/src/lambda/runtime/errors.js +20 -0
- package/packages/dd-trace/src/lambda/runtime/patch.js +74 -0
- package/packages/dd-trace/src/lambda/runtime/ritm.js +143 -0
- package/packages/dd-trace/src/plugin_manager.js +5 -11
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -0
- package/packages/dd-trace/src/plugins/database.js +4 -4
- package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
- package/packages/dd-trace/src/plugins/util/ci.js +5 -2
- package/packages/dd-trace/src/plugins/util/test.js +2 -2
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
- package/packages/dd-trace/src/plugins/util/web.js +1 -104
- package/packages/dd-trace/src/priority_sampler.js +6 -2
- package/packages/dd-trace/src/proxy.js +4 -3
- package/packages/dd-trace/src/ritm.js +7 -1
- package/packages/dd-trace/src/span_processor.js +13 -0
- package/packages/dd-trace/src/span_sampler.js +1 -4
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Plugin = require('../../../../src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../../../../datadog-core')
|
|
5
|
+
const log = require('../../../log')
|
|
5
6
|
const { getFirstNonDDPathAndLine } = require('../path-line')
|
|
6
7
|
const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
|
|
7
8
|
const { getIastContext } = require('../iast-context')
|
|
@@ -13,6 +14,20 @@ class Analyzer extends Plugin {
|
|
|
13
14
|
this._type = type
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
_wrapHandler (handler) {
|
|
18
|
+
return (message, name) => {
|
|
19
|
+
try {
|
|
20
|
+
handler(message, name)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
log.debug(e)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
addSub (channelName, handler) {
|
|
28
|
+
super.addSub(channelName, this._wrapHandler(handler))
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
_isVulnerable (value, context) {
|
|
17
32
|
return false
|
|
18
33
|
}
|
|
@@ -25,6 +40,14 @@ class Analyzer extends Plugin {
|
|
|
25
40
|
addVulnerability(context, vulnerability)
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
_reportIfVulnerable (value, context) {
|
|
44
|
+
if (this._isVulnerable(value, context) && this._checkOCE(context)) {
|
|
45
|
+
this._report(value, context)
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
_getEvidence (value) {
|
|
29
52
|
return { value }
|
|
30
53
|
}
|
|
@@ -34,9 +57,24 @@ class Analyzer extends Plugin {
|
|
|
34
57
|
}
|
|
35
58
|
|
|
36
59
|
analyze (value) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
const store = storage.getStore()
|
|
61
|
+
const iastContext = getIastContext(store)
|
|
62
|
+
if (store && !iastContext) return
|
|
63
|
+
this._reportIfVulnerable(value, iastContext)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
analyzeAll (...values) {
|
|
67
|
+
const store = storage.getStore()
|
|
68
|
+
const iastContext = getIastContext(store)
|
|
69
|
+
if (store && !iastContext) return
|
|
70
|
+
for (let i = 0; i < values.length; i++) {
|
|
71
|
+
const value = values[i]
|
|
72
|
+
if (this._isVulnerable(value, iastContext)) {
|
|
73
|
+
if (this._checkOCE(iastContext)) {
|
|
74
|
+
this._report(value, iastContext)
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
}
|
|
40
78
|
}
|
|
41
79
|
}
|
|
42
80
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const IAST_CONTEXT_KEY = Symbol('_dd.iast.context')
|
|
2
|
+
const IAST_TRANSACTION_ID = Symbol('_dd.iast.transactionId')
|
|
2
3
|
|
|
3
4
|
function getIastContext (store) {
|
|
4
5
|
return store && store[IAST_CONTEXT_KEY]
|
|
@@ -46,5 +47,6 @@ module.exports = {
|
|
|
46
47
|
getIastContext,
|
|
47
48
|
saveIastContext,
|
|
48
49
|
cleanIastContext,
|
|
49
|
-
IAST_CONTEXT_KEY
|
|
50
|
+
IAST_CONTEXT_KEY,
|
|
51
|
+
IAST_TRANSACTION_ID
|
|
50
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { sendVulnerabilities } = require('./vulnerability-reporter')
|
|
1
|
+
const { sendVulnerabilities, setTracer } = require('./vulnerability-reporter')
|
|
2
2
|
const { enableAllAnalyzers, disableAllAnalyzers } = require('./analyzers')
|
|
3
3
|
const web = require('../../plugins/util/web')
|
|
4
4
|
const { storage } = require('../../../../datadog-core')
|
|
@@ -7,22 +7,27 @@ const dc = require('diagnostics_channel')
|
|
|
7
7
|
const iastContextFunctions = require('./iast-context')
|
|
8
8
|
const { enableTaintTracking, disableTaintTracking, createTransaction, removeTransaction } = require('./taint-tracking')
|
|
9
9
|
|
|
10
|
+
const IAST_ENABLED_TAG_KEY = '_dd.iast.enabled'
|
|
11
|
+
|
|
10
12
|
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
|
|
11
13
|
// order of the callbacks can be enforce
|
|
12
14
|
const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
|
|
13
15
|
const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
|
|
14
16
|
|
|
15
|
-
function enable (config) {
|
|
17
|
+
function enable (config, _tracer) {
|
|
16
18
|
enableAllAnalyzers()
|
|
17
19
|
enableTaintTracking()
|
|
18
20
|
requestStart.subscribe(onIncomingHttpRequestStart)
|
|
19
21
|
requestClose.subscribe(onIncomingHttpRequestEnd)
|
|
20
22
|
overheadController.configure(config.iast)
|
|
23
|
+
overheadController.startGlobalContext()
|
|
24
|
+
setTracer(_tracer)
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function disable () {
|
|
24
28
|
disableAllAnalyzers()
|
|
25
29
|
disableTaintTracking()
|
|
30
|
+
overheadController.finishGlobalContext()
|
|
26
31
|
if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
|
|
27
32
|
if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
|
|
28
33
|
}
|
|
@@ -40,6 +45,11 @@ function onIncomingHttpRequestStart (data) {
|
|
|
40
45
|
createTransaction(rootSpan.context().toSpanId(), iastContext)
|
|
41
46
|
overheadController.initializeRequestContext(iastContext)
|
|
42
47
|
}
|
|
48
|
+
if (rootSpan.addTags) {
|
|
49
|
+
rootSpan.addTags({
|
|
50
|
+
[IAST_ENABLED_TAG_KEY]: isRequestAcquired ? '1' : '0'
|
|
51
|
+
})
|
|
52
|
+
}
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
}
|
|
@@ -50,7 +60,9 @@ function onIncomingHttpRequestEnd (data) {
|
|
|
50
60
|
const store = storage.getStore()
|
|
51
61
|
const iastContext = iastContextFunctions.getIastContext(storage.getStore())
|
|
52
62
|
if (iastContext && iastContext.rootSpan) {
|
|
53
|
-
|
|
63
|
+
const vulnerabilities = iastContext.vulnerabilities
|
|
64
|
+
const rootSpan = iastContext.rootSpan
|
|
65
|
+
sendVulnerabilities(vulnerabilities, rootSpan)
|
|
54
66
|
removeTransaction(iastContext)
|
|
55
67
|
}
|
|
56
68
|
// TODO web.getContext(data.req) is required when the request is aborted
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const OVERHEAD_CONTROLLER_CONTEXT_KEY = 'oce'
|
|
4
4
|
const REPORT_VULNERABILITY = 'REPORT_VULNERABILITY'
|
|
5
|
+
const INTERVAL_RESET_GLOBAL_CONTEXT = 60 * 1000
|
|
5
6
|
|
|
6
7
|
const GLOBAL_OCE_CONTEXT = {}
|
|
8
|
+
|
|
9
|
+
let resetGlobalContextInterval
|
|
7
10
|
let config = {}
|
|
8
11
|
let availableRequest = 0
|
|
9
12
|
const OPERATIONS = {
|
|
@@ -80,11 +83,27 @@ function configure (cfg) {
|
|
|
80
83
|
availableRequest = config.maxConcurrentRequests
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
function startGlobalContext () {
|
|
87
|
+
if (resetGlobalContextInterval) return
|
|
88
|
+
_resetGlobalContext()
|
|
89
|
+
resetGlobalContextInterval = setInterval(() => {
|
|
90
|
+
_resetGlobalContext()
|
|
91
|
+
}, INTERVAL_RESET_GLOBAL_CONTEXT)
|
|
92
|
+
resetGlobalContextInterval.unref && resetGlobalContextInterval.unref()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function finishGlobalContext () {
|
|
96
|
+
if (resetGlobalContextInterval) {
|
|
97
|
+
clearInterval(resetGlobalContextInterval)
|
|
98
|
+
resetGlobalContextInterval = null
|
|
99
|
+
}
|
|
100
|
+
}
|
|
84
101
|
|
|
85
102
|
module.exports = {
|
|
86
103
|
OVERHEAD_CONTROLLER_CONTEXT_KEY,
|
|
87
104
|
OPERATIONS,
|
|
105
|
+
startGlobalContext,
|
|
106
|
+
finishGlobalContext,
|
|
88
107
|
_resetGlobalContext,
|
|
89
108
|
initializeRequestContext,
|
|
90
109
|
hasQuota,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const process = require('process')
|
|
2
3
|
const pathLine = {
|
|
3
4
|
getFirstNonDDPathAndLine,
|
|
4
5
|
getFirstNonDDPathAndLineFromCallsites, // Exported only for test purposes
|
|
@@ -7,7 +8,7 @@ const pathLine = {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const EXCLUDED_PATHS = [
|
|
10
|
-
'
|
|
11
|
+
path.join(path.sep, 'node_modules', 'diagnostics_channel')
|
|
11
12
|
]
|
|
12
13
|
const EXCLUDED_PATH_PREFIXES = [
|
|
13
14
|
'node:diagnostics_channel',
|
|
@@ -20,7 +21,7 @@ const EXCLUDED_PATH_PREFIXES = [
|
|
|
20
21
|
|
|
21
22
|
function calculateDDBasePath (dirname) {
|
|
22
23
|
const dirSteps = dirname.split(path.sep)
|
|
23
|
-
const packagesIndex = dirSteps.
|
|
24
|
+
const packagesIndex = dirSteps.lastIndexOf('packages')
|
|
24
25
|
return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -43,10 +44,10 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
|
|
|
43
44
|
if (callsites) {
|
|
44
45
|
for (let i = 0; i < callsites.length; i++) {
|
|
45
46
|
const callsite = callsites[i]
|
|
46
|
-
const
|
|
47
|
-
if (!isExcluded(callsite) &&
|
|
47
|
+
const filepath = callsite.getFileName()
|
|
48
|
+
if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
|
|
48
49
|
return {
|
|
49
|
-
path,
|
|
50
|
+
path: path.relative(process.cwd(), filepath),
|
|
50
51
|
line: callsite.getLineNumber()
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const csiMethods = [
|
|
4
|
+
{ src: 'plusOperator', operator: true },
|
|
5
|
+
{ src: 'trim' },
|
|
6
|
+
{ src: 'trimStart', dst: 'trim' },
|
|
7
|
+
{ src: 'trimEnd' },
|
|
8
|
+
{ src: 'concat' },
|
|
9
|
+
{ src: 'substring' },
|
|
10
|
+
{ src: 'substr' },
|
|
11
|
+
{ src: 'slice' },
|
|
12
|
+
{ src: 'replace' }
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
csiMethods
|
|
17
|
+
}
|
|
@@ -1,33 +1,6 @@
|
|
|
1
|
-
const { storage } = require('../../../../../datadog-core')
|
|
2
|
-
const iastContextFunctions = require('../iast-context')
|
|
3
|
-
const log = require('../../../log')
|
|
4
1
|
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
function noop (res) { return res }
|
|
9
|
-
const TaintTrackingDummy = {
|
|
10
|
-
plusOperator: noop
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const TaintTracking = {
|
|
14
|
-
plusOperator: function (res, op1, op2) {
|
|
15
|
-
try {
|
|
16
|
-
if (typeof res !== 'string' ||
|
|
17
|
-
(typeof op1 !== 'string' && typeof op2 !== 'string')) { return res }
|
|
18
|
-
|
|
19
|
-
const store = storage.getStore()
|
|
20
|
-
const iastContext = iastContextFunctions.getIastContext(store)
|
|
21
|
-
const transactionId = iastContext && iastContext[IAST_TRANSACTION_ID]
|
|
22
|
-
if (transactionId) {
|
|
23
|
-
return TaintedUtils.concat(transactionId, res, op1, op2)
|
|
24
|
-
}
|
|
25
|
-
} catch (e) {
|
|
26
|
-
log.debug(e)
|
|
27
|
-
}
|
|
28
|
-
return res
|
|
29
|
-
}
|
|
30
|
-
}
|
|
2
|
+
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
3
|
+
const { TaintTracking, TaintTrackingDummy } = require('./taint-tracking-impl')
|
|
31
4
|
|
|
32
5
|
function createTransaction (id, iastContext) {
|
|
33
6
|
if (id && iastContext) {
|
|
@@ -4,20 +4,7 @@ const Module = require('module')
|
|
|
4
4
|
const shimmer = require('../../../../../datadog-shimmer')
|
|
5
5
|
const log = require('../../../log')
|
|
6
6
|
const { isPrivateModule, isNotLibraryFile } = require('./filter')
|
|
7
|
-
|
|
8
|
-
let originalPrepareStackTrace = Error.prepareStackTrace
|
|
9
|
-
function getPrepareStackTraceAccessor () {
|
|
10
|
-
let actual = getPrepareStackTrace(originalPrepareStackTrace)
|
|
11
|
-
return {
|
|
12
|
-
get () {
|
|
13
|
-
return actual
|
|
14
|
-
},
|
|
15
|
-
set (value) {
|
|
16
|
-
actual = getPrepareStackTrace(value)
|
|
17
|
-
originalPrepareStackTrace = value
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
7
|
+
const { csiMethods } = require('./csi-methods')
|
|
21
8
|
|
|
22
9
|
let rewriter
|
|
23
10
|
let getPrepareStackTrace
|
|
@@ -27,7 +14,7 @@ function getRewriter () {
|
|
|
27
14
|
const iastRewriter = require('@datadog/native-iast-rewriter')
|
|
28
15
|
const Rewriter = iastRewriter.Rewriter
|
|
29
16
|
getPrepareStackTrace = iastRewriter.getPrepareStackTrace
|
|
30
|
-
rewriter = new Rewriter()
|
|
17
|
+
rewriter = new Rewriter({ csiMethods })
|
|
31
18
|
} catch (e) {
|
|
32
19
|
log.warn(`Unable to initialize TaintTracking Rewriter: ${e.message}`)
|
|
33
20
|
}
|
|
@@ -35,6 +22,20 @@ function getRewriter () {
|
|
|
35
22
|
return rewriter
|
|
36
23
|
}
|
|
37
24
|
|
|
25
|
+
let originalPrepareStackTrace = Error.prepareStackTrace
|
|
26
|
+
function getPrepareStackTraceAccessor () {
|
|
27
|
+
let actual = getPrepareStackTrace(originalPrepareStackTrace)
|
|
28
|
+
return {
|
|
29
|
+
get () {
|
|
30
|
+
return actual
|
|
31
|
+
},
|
|
32
|
+
set (value) {
|
|
33
|
+
actual = getPrepareStackTrace(value)
|
|
34
|
+
originalPrepareStackTrace = value
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
38
39
|
function getCompileMethodFn (compileMethod) {
|
|
39
40
|
return function (content, filename) {
|
|
40
41
|
try {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
4
|
+
const { storage } = require('../../../../../datadog-core')
|
|
5
|
+
const iastContextFunctions = require('../iast-context')
|
|
6
|
+
const log = require('../../../log')
|
|
7
|
+
|
|
8
|
+
function noop (res) { return res }
|
|
9
|
+
const TaintTrackingDummy = {
|
|
10
|
+
plusOperator: noop,
|
|
11
|
+
trim: noop,
|
|
12
|
+
trimEnd: noop,
|
|
13
|
+
concat: noop,
|
|
14
|
+
substring: noop,
|
|
15
|
+
substr: noop,
|
|
16
|
+
slice: noop,
|
|
17
|
+
replace: noop
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getTransactionId () {
|
|
21
|
+
const store = storage.getStore()
|
|
22
|
+
const iastContext = iastContextFunctions.getIastContext(store)
|
|
23
|
+
return iastContext && iastContext[iastContextFunctions.IAST_TRANSACTION_ID]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getFilteredCsiFn (cb, filter) {
|
|
27
|
+
return function csiCall (res, fn, target, ...rest) {
|
|
28
|
+
try {
|
|
29
|
+
if (filter(res, fn, target)) { return res }
|
|
30
|
+
const transactionId = getTransactionId()
|
|
31
|
+
if (transactionId) {
|
|
32
|
+
return cb(transactionId, res, target, ...rest)
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
log.debug(e)
|
|
36
|
+
}
|
|
37
|
+
return res
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function notString () {
|
|
42
|
+
return Array.prototype.some.call(arguments, (p) => typeof p !== 'string')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isValidCsiMethod (fn, protos) {
|
|
46
|
+
return protos.some(proto => fn === proto)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getCsiFn (cb, ...protos) {
|
|
50
|
+
let filter
|
|
51
|
+
if (!protos || protos.length === 0) {
|
|
52
|
+
filter = (res, fn, target) => notString(res, target)
|
|
53
|
+
} else if (protos.length === 1) {
|
|
54
|
+
const protoFn = protos[0]
|
|
55
|
+
filter = (res, fn, target) => notString(res, target) || fn !== protoFn
|
|
56
|
+
} else {
|
|
57
|
+
filter = (res, fn, target) => notString(res, target) || !isValidCsiMethod(fn, protos)
|
|
58
|
+
}
|
|
59
|
+
return getFilteredCsiFn(cb, filter)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function csiMethodsDefaults (names, excluded) {
|
|
63
|
+
const impl = {}
|
|
64
|
+
names.forEach(name => {
|
|
65
|
+
if (excluded.indexOf(name) !== -1) return
|
|
66
|
+
impl[name] = getCsiFn(
|
|
67
|
+
(transactionId, res, target, ...rest) => TaintedUtils[name](transactionId, res, target, ...rest),
|
|
68
|
+
String.prototype[name]
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
return impl
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const csiMethodsOverrides = {
|
|
75
|
+
plusOperator: function (res, op1, op2) {
|
|
76
|
+
try {
|
|
77
|
+
if (notString(res) || (notString(op1) && notString(op2))) { return res }
|
|
78
|
+
const transactionId = getTransactionId()
|
|
79
|
+
if (transactionId) {
|
|
80
|
+
return TaintedUtils.concat(transactionId, res, op1, op2)
|
|
81
|
+
}
|
|
82
|
+
} catch (e) {
|
|
83
|
+
log.debug(e)
|
|
84
|
+
}
|
|
85
|
+
return res
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
trim: getCsiFn(
|
|
89
|
+
(transactionId, res, target) => TaintedUtils.trim(transactionId, res, target),
|
|
90
|
+
String.prototype.trim,
|
|
91
|
+
String.prototype.trimStart
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const TaintTracking = {
|
|
96
|
+
...csiMethodsDefaults(Object.keys(TaintTrackingDummy), Object.keys(csiMethodsOverrides)),
|
|
97
|
+
...csiMethodsOverrides
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
TaintTracking,
|
|
102
|
+
TaintTrackingDummy
|
|
103
|
+
}
|
|
@@ -5,13 +5,16 @@ const IAST_JSON_TAG_KEY = '_dd.iast.json'
|
|
|
5
5
|
const VULNERABILITY_HASHES_MAX_SIZE = 1000
|
|
6
6
|
const VULNERABILITY_HASHES = new LRU({ max: VULNERABILITY_HASHES_MAX_SIZE })
|
|
7
7
|
|
|
8
|
+
let tracer
|
|
9
|
+
|
|
8
10
|
function createVulnerability (type, evidence, spanId, location) {
|
|
9
|
-
if (type && evidence
|
|
11
|
+
if (type && evidence) {
|
|
12
|
+
const _spanId = spanId || 0
|
|
10
13
|
return {
|
|
11
14
|
type,
|
|
12
15
|
evidence,
|
|
13
16
|
location: {
|
|
14
|
-
spanId,
|
|
17
|
+
spanId: _spanId,
|
|
15
18
|
...location
|
|
16
19
|
},
|
|
17
20
|
hash: createHash(type, location)
|
|
@@ -37,10 +40,14 @@ function createHash (type, location) {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
function addVulnerability (iastContext, vulnerability) {
|
|
40
|
-
if (
|
|
41
|
-
vulnerability.location
|
|
42
|
-
iastContext
|
|
43
|
-
|
|
43
|
+
if (vulnerability && vulnerability.evidence && vulnerability.type &&
|
|
44
|
+
vulnerability.location) {
|
|
45
|
+
if (iastContext && iastContext.rootSpan) {
|
|
46
|
+
iastContext[VULNERABILITIES_KEY] = iastContext[VULNERABILITIES_KEY] || []
|
|
47
|
+
iastContext[VULNERABILITIES_KEY].push(vulnerability)
|
|
48
|
+
} else {
|
|
49
|
+
sendVulnerabilities([vulnerability])
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -101,43 +108,53 @@ function jsonVulnerabilityFromVulnerability (vulnerability, sourcesIndexes) {
|
|
|
101
108
|
return jsonVulnerability
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
function sendVulnerabilities (
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
vulnerabilities
|
|
111
|
+
function sendVulnerabilities (vulnerabilities, rootSpan) {
|
|
112
|
+
if (vulnerabilities && vulnerabilities.length) {
|
|
113
|
+
let span = rootSpan
|
|
114
|
+
if (!span && tracer) {
|
|
115
|
+
span = tracer.startSpan('vulnerability', {
|
|
116
|
+
type: 'vulnerability'
|
|
117
|
+
})
|
|
118
|
+
vulnerabilities.forEach((vulnerability) => {
|
|
119
|
+
vulnerability.location.spanId = span.context().toSpanId()
|
|
120
|
+
})
|
|
112
121
|
}
|
|
113
122
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
123
|
+
if (span && span.addTags) {
|
|
124
|
+
const jsonToSend = {
|
|
125
|
+
sources: [],
|
|
126
|
+
vulnerabilities: []
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
deduplicateVulnerabilities(vulnerabilities).forEach((vulnerability) => {
|
|
130
|
+
if (isValidVulnerability(vulnerability)) {
|
|
131
|
+
const sourcesIndexes = []
|
|
132
|
+
const vulnerabilitySources = extractSourcesFromVulnerability(vulnerability)
|
|
133
|
+
vulnerabilitySources.forEach((source) => {
|
|
134
|
+
let sourceIndex = jsonToSend.sources.findIndex(
|
|
135
|
+
existingSource =>
|
|
136
|
+
existingSource.origin === source.origin &&
|
|
137
|
+
existingSource.name === source.name &&
|
|
138
|
+
existingSource.value === source.value
|
|
139
|
+
)
|
|
140
|
+
if (sourceIndex === -1) {
|
|
141
|
+
sourceIndex = jsonToSend.sources.length
|
|
142
|
+
jsonToSend.sources.push(source)
|
|
143
|
+
}
|
|
144
|
+
sourcesIndexes.push(sourceIndex)
|
|
145
|
+
})
|
|
146
|
+
jsonToSend.vulnerabilities.push(jsonVulnerabilityFromVulnerability(vulnerability, sourcesIndexes))
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (jsonToSend.vulnerabilities.length > 0) {
|
|
151
|
+
const tags = {}
|
|
152
|
+
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
153
|
+
tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
|
|
154
|
+
tags[MANUAL_KEEP] = 'true'
|
|
155
|
+
span.addTags(tags)
|
|
156
|
+
if (!rootSpan) span.finish()
|
|
132
157
|
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
if (jsonToSend.vulnerabilities.length > 0) {
|
|
136
|
-
const tags = {}
|
|
137
|
-
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
138
|
-
tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
|
|
139
|
-
tags[MANUAL_KEEP] = 'true'
|
|
140
|
-
span.addTags(tags)
|
|
141
158
|
}
|
|
142
159
|
}
|
|
143
160
|
return IAST_JSON_TAG_KEY
|
|
@@ -159,9 +176,14 @@ function deduplicateVulnerabilities (vulnerabilities) {
|
|
|
159
176
|
return deduplicated
|
|
160
177
|
}
|
|
161
178
|
|
|
179
|
+
function setTracer (_tracer) {
|
|
180
|
+
tracer = _tracer
|
|
181
|
+
}
|
|
182
|
+
|
|
162
183
|
module.exports = {
|
|
163
184
|
createVulnerability,
|
|
164
185
|
addVulnerability,
|
|
165
186
|
sendVulnerabilities,
|
|
166
|
-
clearCache
|
|
187
|
+
clearCache,
|
|
188
|
+
setTracer
|
|
167
189
|
}
|