dd-trace 3.3.1 → 3.12.1
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 +8 -0
- package/README.md +108 -43
- package/ci/init.js +6 -1
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +129 -36
- package/package.json +18 -9
- package/packages/datadog-instrumentations/src/body-parser.js +26 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
- package/packages/datadog-instrumentations/src/child-process.js +30 -0
- package/packages/datadog-instrumentations/src/connect.js +15 -15
- package/packages/datadog-instrumentations/src/cucumber.js +1 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +51 -47
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +5 -0
- package/packages/datadog-instrumentations/src/http/server.js +20 -12
- package/packages/datadog-instrumentations/src/http2/server.js +67 -1
- package/packages/datadog-instrumentations/src/jest.js +182 -25
- package/packages/datadog-instrumentations/src/koa.js +32 -32
- package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
- package/packages/datadog-instrumentations/src/mariadb.js +63 -0
- package/packages/datadog-instrumentations/src/memcached.js +1 -4
- package/packages/datadog-instrumentations/src/mocha.js +135 -24
- package/packages/datadog-instrumentations/src/next.js +10 -2
- package/packages/datadog-instrumentations/src/opensearch.js +10 -0
- package/packages/datadog-instrumentations/src/oracledb.js +8 -8
- package/packages/datadog-instrumentations/src/pg.js +7 -3
- package/packages/datadog-instrumentations/src/qs.js +24 -0
- package/packages/datadog-instrumentations/src/redis.js +12 -3
- package/packages/datadog-instrumentations/src/restify.js +5 -1
- package/packages/datadog-instrumentations/src/rhea.js +29 -15
- package/packages/datadog-instrumentations/src/router.js +23 -23
- package/packages/datadog-plugin-amqp10/src/consumer.js +32 -0
- package/packages/datadog-plugin-amqp10/src/index.js +11 -101
- package/packages/datadog-plugin-amqp10/src/producer.js +34 -0
- package/packages/datadog-plugin-amqp10/src/util.js +15 -0
- package/packages/datadog-plugin-amqplib/src/client.js +38 -0
- package/packages/datadog-plugin-amqplib/src/consumer.js +40 -0
- package/packages/datadog-plugin-amqplib/src/index.js +14 -102
- package/packages/datadog-plugin-amqplib/src/producer.js +37 -0
- package/packages/datadog-plugin-amqplib/src/util.js +14 -0
- package/packages/datadog-plugin-cassandra-driver/src/index.js +22 -60
- package/packages/datadog-plugin-cucumber/src/index.js +14 -33
- package/packages/datadog-plugin-cypress/src/plugin.js +2 -1
- package/packages/datadog-plugin-dns/src/index.js +16 -91
- package/packages/datadog-plugin-dns/src/lookup.js +40 -0
- package/packages/datadog-plugin-dns/src/lookup_service.js +24 -0
- package/packages/datadog-plugin-dns/src/resolve.js +24 -0
- package/packages/datadog-plugin-dns/src/reverse.js +21 -0
- package/packages/datadog-plugin-elasticsearch/src/index.js +24 -60
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +24 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +41 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +14 -99
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +33 -0
- package/packages/datadog-plugin-graphql/src/execute.js +73 -0
- package/packages/datadog-plugin-graphql/src/index.js +14 -176
- package/packages/datadog-plugin-graphql/src/parse.js +32 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +70 -76
- package/packages/datadog-plugin-graphql/src/validate.js +28 -0
- package/packages/datadog-plugin-grpc/src/client.js +46 -55
- package/packages/datadog-plugin-grpc/src/index.js +7 -24
- package/packages/datadog-plugin-grpc/src/server.js +50 -52
- package/packages/datadog-plugin-grpc/src/util.js +15 -14
- package/packages/datadog-plugin-http/src/client.js +15 -5
- package/packages/datadog-plugin-http/src/index.js +7 -22
- package/packages/datadog-plugin-http/src/server.js +19 -3
- package/packages/datadog-plugin-http2/src/client.js +20 -1
- package/packages/datadog-plugin-http2/src/index.js +8 -26
- package/packages/datadog-plugin-http2/src/server.js +44 -0
- package/packages/datadog-plugin-jest/src/index.js +41 -65
- package/packages/datadog-plugin-kafkajs/src/consumer.js +42 -0
- package/packages/datadog-plugin-kafkajs/src/index.js +11 -87
- package/packages/datadog-plugin-kafkajs/src/producer.js +31 -0
- package/packages/datadog-plugin-mariadb/src/index.js +10 -0
- package/packages/datadog-plugin-memcached/src/index.js +17 -52
- package/packages/datadog-plugin-mocha/src/index.js +40 -60
- package/packages/datadog-plugin-moleculer/src/client.js +22 -36
- package/packages/datadog-plugin-moleculer/src/index.js +8 -26
- package/packages/datadog-plugin-moleculer/src/server.js +18 -30
- package/packages/datadog-plugin-mongodb-core/src/index.js +23 -51
- package/packages/datadog-plugin-mysql/src/index.js +23 -52
- package/packages/datadog-plugin-mysql2/src/index.js +1 -3
- package/packages/datadog-plugin-net/src/index.js +4 -0
- package/packages/datadog-plugin-net/src/ipc.js +21 -0
- package/packages/datadog-plugin-net/src/tcp.js +46 -0
- package/packages/datadog-plugin-next/src/index.js +3 -0
- package/packages/datadog-plugin-opensearch/src/index.js +11 -0
- package/packages/datadog-plugin-oracledb/src/index.js +29 -55
- package/packages/datadog-plugin-pg/src/index.js +27 -51
- package/packages/datadog-plugin-redis/src/index.js +29 -60
- package/packages/datadog-plugin-rhea/src/consumer.js +55 -0
- package/packages/datadog-plugin-rhea/src/index.js +11 -96
- package/packages/datadog-plugin-rhea/src/producer.js +45 -0
- package/packages/datadog-plugin-router/src/index.js +13 -2
- package/packages/datadog-plugin-sharedb/src/index.js +22 -39
- package/packages/datadog-plugin-tedious/src/index.js +20 -41
- 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 +8 -6
- package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/index.js +7 -2
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +4 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +11 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +19 -0
- 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 +13 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +43 -5
- package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
- package/packages/dd-trace/src/appsec/iast/index.js +24 -8
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +17 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +17 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +16 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +18 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +98 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +4 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +38 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +67 -0
- 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 +98 -30
- package/packages/dd-trace/src/appsec/index.js +79 -25
- 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 +134 -53
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +56 -0
- package/packages/dd-trace/src/appsec/remote_config/manager.js +264 -0
- package/packages/dd-trace/src/{exporters → appsec/remote_config}/scheduler.js +9 -9
- package/packages/dd-trace/src/appsec/rule_manager.js +61 -1
- 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 +66 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +19 -51
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +10 -5
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +202 -0
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +51 -62
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +89 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +82 -0
- package/packages/dd-trace/src/config.js +119 -35
- package/packages/dd-trace/src/constants.js +9 -1
- package/packages/dd-trace/src/dogstatsd.js +42 -10
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +10 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/exporter.js +3 -0
- package/packages/dd-trace/src/exporters/agent/index.js +10 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +2 -9
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +82 -0
- package/packages/dd-trace/src/exporters/common/request.js +51 -1
- package/packages/dd-trace/src/exporters/span-stats/index.js +6 -2
- package/packages/dd-trace/src/format.js +29 -10
- 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 +138 -0
- package/packages/dd-trace/src/metrics.js +15 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -5
- package/packages/dd-trace/src/opentracing/span.js +2 -1
- package/packages/dd-trace/src/opentracing/span_context.js +9 -0
- package/packages/dd-trace/src/plugin_manager.js +11 -17
- package/packages/dd-trace/src/plugins/cache.js +9 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +97 -0
- package/packages/dd-trace/src/plugins/client.js +9 -0
- package/packages/dd-trace/src/plugins/composite.js +26 -0
- package/packages/dd-trace/src/plugins/consumer.js +9 -0
- package/packages/dd-trace/src/plugins/database.js +55 -0
- package/packages/dd-trace/src/plugins/incoming.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
- package/packages/dd-trace/src/plugins/outgoing.js +31 -0
- package/packages/dd-trace/src/plugins/plugin.js +3 -0
- package/packages/dd-trace/src/plugins/producer.js +9 -0
- package/packages/dd-trace/src/plugins/server.js +9 -0
- package/packages/dd-trace/src/plugins/storage.js +21 -0
- package/packages/dd-trace/src/plugins/tracing.js +94 -0
- package/packages/dd-trace/src/plugins/util/ci.js +40 -4
- package/packages/dd-trace/src/plugins/util/git.js +58 -18
- package/packages/dd-trace/src/plugins/util/test.js +71 -8
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
- package/packages/dd-trace/src/plugins/util/web.js +11 -114
- package/packages/dd-trace/src/priority_sampler.js +6 -2
- package/packages/dd-trace/src/profiling/config.js +11 -6
- package/packages/dd-trace/src/profiling/exporters/agent.js +4 -0
- package/packages/dd-trace/src/profiling/index.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +43 -7
- package/packages/dd-trace/src/proxy.js +8 -16
- package/packages/dd-trace/src/ritm.js +25 -14
- package/packages/dd-trace/src/span_processor.js +17 -0
- package/packages/dd-trace/src/span_sampler.js +77 -0
- package/packages/dd-trace/src/span_stats.js +2 -2
- package/packages/dd-trace/src/telemetry/dependencies.js +21 -7
- package/packages/dd-trace/src/telemetry/index.js +7 -1
- package/packages/dd-trace/src/telemetry/send-data.js +3 -1
- package/packages/dd-trace/src/tracer.js +10 -5
- package/packages/dd-trace/src/util.js +43 -1
|
@@ -29,9 +29,7 @@ class WAFCallback {
|
|
|
29
29
|
|
|
30
30
|
this.wafTimeout = wafTimeout
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Reporter.metricsQueue.set('_dd.appsec.waf.version', `${version.major}.${version.minor}.${version.patch}`)
|
|
32
|
+
Reporter.metricsQueue.set('_dd.appsec.waf.version', this.ddwaf.constructor.version())
|
|
35
33
|
|
|
36
34
|
const { loaded, failed, errors } = this.ddwaf.rulesInfo
|
|
37
35
|
|
|
@@ -63,7 +61,7 @@ class WAFCallback {
|
|
|
63
61
|
|
|
64
62
|
subscribedAddresses.add(address)
|
|
65
63
|
|
|
66
|
-
Gateway.manager.addSubscription({ addresses: [
|
|
64
|
+
Gateway.manager.addSubscription({ addresses: [address], callback })
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
67
|
}
|
|
@@ -121,14 +119,18 @@ class WAFCallback {
|
|
|
121
119
|
if (result.data && result.data !== '[]') {
|
|
122
120
|
Reporter.reportAttack(result.data, store)
|
|
123
121
|
}
|
|
122
|
+
|
|
123
|
+
return result.actions
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
updateRuleData (ruleData) {
|
|
127
|
+
this.ddwaf.updateRuleData(ruleData)
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
clear () {
|
|
127
131
|
this.ddwaf.dispose()
|
|
128
132
|
|
|
129
133
|
this.wafContextCache = new WeakMap()
|
|
130
|
-
|
|
131
|
-
Gateway.manager.clear()
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
@@ -28,7 +28,7 @@ class SubscriptionManager {
|
|
|
28
28
|
const list = this.addressToSubscriptions.get(address)
|
|
29
29
|
|
|
30
30
|
if (list === undefined) {
|
|
31
|
-
this.addressToSubscriptions.set(address, [
|
|
31
|
+
this.addressToSubscriptions.set(address, [subscription])
|
|
32
32
|
} else {
|
|
33
33
|
list.push(subscription)
|
|
34
34
|
}
|
|
@@ -22,6 +22,10 @@ function getContext () {
|
|
|
22
22
|
return store && store.get('context')
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function needsAddress (address) {
|
|
26
|
+
return manager.addresses.has(address)
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
function propagate (data, context = getContext()) {
|
|
26
30
|
if (!context) return
|
|
27
31
|
|
|
@@ -30,17 +34,18 @@ function propagate (data, context = getContext()) {
|
|
|
30
34
|
for (let i = 0; i < keys.length; ++i) {
|
|
31
35
|
const key = keys[i]
|
|
32
36
|
|
|
33
|
-
if (
|
|
37
|
+
if (needsAddress(key)) {
|
|
34
38
|
context.setValue(key, data[key])
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
context.dispatch()
|
|
42
|
+
return context.dispatch()
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
module.exports = {
|
|
42
46
|
manager,
|
|
43
47
|
startContext,
|
|
44
48
|
getContext,
|
|
49
|
+
needsAddress,
|
|
45
50
|
propagate
|
|
46
51
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
|
|
3
|
-
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer')
|
|
3
|
+
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
|
|
4
|
+
'SQL_INJECTION_ANALYZER': require('./sql-injection-analyzer'),
|
|
5
|
+
'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
|
|
6
|
+
'LDAP_ANALYZER': require('./ldap-injection-analyzer')
|
|
4
7
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
|
|
4
|
+
class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
|
+
constructor () {
|
|
6
|
+
super('COMMAND_INJECTION')
|
|
7
|
+
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = new CommandInjectionAnalyzer()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
+
const { isTainted, getRanges } = require('../taint-tracking/operations')
|
|
4
|
+
|
|
5
|
+
class InjectionAnalyzer extends Analyzer {
|
|
6
|
+
_isVulnerable (value, iastContext) {
|
|
7
|
+
if (value) {
|
|
8
|
+
return isTainted(iastContext, value)
|
|
9
|
+
}
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_getEvidence (value, iastContext) {
|
|
14
|
+
const ranges = getRanges(iastContext, value)
|
|
15
|
+
return { value, ranges }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = InjectionAnalyzer
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
|
|
4
|
+
class LdapInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
|
+
constructor () {
|
|
6
|
+
super('LDAP_INJECTION')
|
|
7
|
+
this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = new LdapInjectionAnalyzer()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
|
|
4
|
+
class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
|
+
constructor () {
|
|
6
|
+
super('SQL_INJECTION')
|
|
7
|
+
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql))
|
|
8
|
+
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql))
|
|
9
|
+
this.addSub('apm:pg:query:start', ({ originalQuery }) => this.analyze(originalQuery))
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = new SqlInjectionAnalyzer()
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const Plugin = require('../../../../src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../../../../datadog-core')
|
|
5
|
-
const
|
|
5
|
+
const log = require('../../../log')
|
|
6
|
+
const { getFirstNonDDPathAndLine } = require('../path-line')
|
|
6
7
|
const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
|
|
7
8
|
const { getIastContext } = require('../iast-context')
|
|
8
9
|
const overheadController = require('../overhead-controller')
|
|
@@ -13,18 +14,40 @@ 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
|
}
|
|
19
34
|
|
|
20
35
|
_report (value, context) {
|
|
21
|
-
const evidence = this._getEvidence(value)
|
|
36
|
+
const evidence = this._getEvidence(value, context)
|
|
22
37
|
const location = this._getLocation()
|
|
23
38
|
const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
|
|
24
39
|
const vulnerability = createVulnerability(this._type, evidence, spanId, location)
|
|
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,25 +1,33 @@
|
|
|
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')
|
|
5
5
|
const overheadController = require('./overhead-controller')
|
|
6
6
|
const dc = require('diagnostics_channel')
|
|
7
|
-
const
|
|
7
|
+
const iastContextFunctions = require('./iast-context')
|
|
8
|
+
const { enableTaintTracking, disableTaintTracking, createTransaction, removeTransaction } = require('./taint-tracking')
|
|
9
|
+
|
|
10
|
+
const IAST_ENABLED_TAG_KEY = '_dd.iast.enabled'
|
|
8
11
|
|
|
9
12
|
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
|
|
10
13
|
// order of the callbacks can be enforce
|
|
11
14
|
const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
|
|
12
15
|
const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
|
|
13
16
|
|
|
14
|
-
function enable (config) {
|
|
17
|
+
function enable (config, _tracer) {
|
|
15
18
|
enableAllAnalyzers()
|
|
19
|
+
enableTaintTracking()
|
|
16
20
|
requestStart.subscribe(onIncomingHttpRequestStart)
|
|
17
21
|
requestClose.subscribe(onIncomingHttpRequestEnd)
|
|
18
22
|
overheadController.configure(config.iast)
|
|
23
|
+
overheadController.startGlobalContext()
|
|
24
|
+
setTracer(_tracer)
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
function disable () {
|
|
22
28
|
disableAllAnalyzers()
|
|
29
|
+
disableTaintTracking()
|
|
30
|
+
overheadController.finishGlobalContext()
|
|
23
31
|
if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
|
|
24
32
|
if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
|
|
25
33
|
}
|
|
@@ -33,9 +41,15 @@ function onIncomingHttpRequestStart (data) {
|
|
|
33
41
|
const rootSpan = topContext.span
|
|
34
42
|
const isRequestAcquired = overheadController.acquireRequest(rootSpan)
|
|
35
43
|
if (isRequestAcquired) {
|
|
36
|
-
const iastContext = saveIastContext(store, topContext, { rootSpan, req: data.req })
|
|
44
|
+
const iastContext = iastContextFunctions.saveIastContext(store, topContext, { rootSpan, req: data.req })
|
|
45
|
+
createTransaction(rootSpan.context().toSpanId(), iastContext)
|
|
37
46
|
overheadController.initializeRequestContext(iastContext)
|
|
38
47
|
}
|
|
48
|
+
if (rootSpan.addTags) {
|
|
49
|
+
rootSpan.addTags({
|
|
50
|
+
[IAST_ENABLED_TAG_KEY]: isRequestAcquired ? '1' : '0'
|
|
51
|
+
})
|
|
52
|
+
}
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
55
|
}
|
|
@@ -44,13 +58,15 @@ function onIncomingHttpRequestStart (data) {
|
|
|
44
58
|
function onIncomingHttpRequestEnd (data) {
|
|
45
59
|
if (data && data.req) {
|
|
46
60
|
const store = storage.getStore()
|
|
47
|
-
const iastContext = getIastContext(storage.getStore())
|
|
61
|
+
const iastContext = iastContextFunctions.getIastContext(storage.getStore())
|
|
48
62
|
if (iastContext && iastContext.rootSpan) {
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
const vulnerabilities = iastContext.vulnerabilities
|
|
64
|
+
const rootSpan = iastContext.rootSpan
|
|
65
|
+
sendVulnerabilities(vulnerabilities, rootSpan)
|
|
66
|
+
removeTransaction(iastContext)
|
|
51
67
|
}
|
|
52
68
|
// TODO web.getContext(data.req) is required when the request is aborted
|
|
53
|
-
if (cleanIastContext(store, web.getContext(data.req), iastContext)) {
|
|
69
|
+
if (iastContextFunctions.cleanIastContext(store, web.getContext(data.req), iastContext)) {
|
|
54
70
|
overheadController.releaseRequest()
|
|
55
71
|
}
|
|
56
72
|
}
|
|
@@ -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,28 +8,35 @@ 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',
|
|
14
|
-
'diagnostics_channel'
|
|
15
|
+
'diagnostics_channel',
|
|
16
|
+
'node:child_process',
|
|
17
|
+
'child_process',
|
|
18
|
+
'node:async_hooks',
|
|
19
|
+
'async_hooks'
|
|
15
20
|
]
|
|
16
21
|
|
|
17
22
|
function calculateDDBasePath (dirname) {
|
|
18
23
|
const dirSteps = dirname.split(path.sep)
|
|
19
|
-
const packagesIndex = dirSteps.
|
|
24
|
+
const packagesIndex = dirSteps.lastIndexOf('packages')
|
|
20
25
|
return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
function getCallSiteInfo () {
|
|
24
29
|
const previousPrepareStackTrace = Error.prepareStackTrace
|
|
30
|
+
const previousStackTraceLimit = Error.stackTraceLimit
|
|
25
31
|
let callsiteList
|
|
32
|
+
Error.stackTraceLimit = 100
|
|
26
33
|
Error.prepareStackTrace = function (_, callsites) {
|
|
27
34
|
callsiteList = callsites
|
|
28
35
|
}
|
|
29
36
|
const e = new Error()
|
|
30
37
|
e.stack
|
|
31
38
|
Error.prepareStackTrace = previousPrepareStackTrace
|
|
39
|
+
Error.stackTraceLimit = previousStackTraceLimit
|
|
32
40
|
return callsiteList
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -36,10 +44,10 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
|
|
|
36
44
|
if (callsites) {
|
|
37
45
|
for (let i = 0; i < callsites.length; i++) {
|
|
38
46
|
const callsite = callsites[i]
|
|
39
|
-
const
|
|
40
|
-
if (!isExcluded(callsite) &&
|
|
47
|
+
const filepath = callsite.getFileName()
|
|
48
|
+
if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
|
|
41
49
|
return {
|
|
42
|
-
path,
|
|
50
|
+
path: path.relative(process.cwd(), filepath),
|
|
43
51
|
line: callsite.getLineNumber()
|
|
44
52
|
}
|
|
45
53
|
}
|
|
@@ -51,6 +59,9 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
|
|
|
51
59
|
function isExcluded (callsite) {
|
|
52
60
|
if (callsite.isNative()) return true
|
|
53
61
|
const filename = callsite.getFileName()
|
|
62
|
+
if (!filename) {
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
54
65
|
for (let i = 0; i < EXCLUDED_PATHS.length; i++) {
|
|
55
66
|
if (filename.indexOf(EXCLUDED_PATHS[i]) > -1) {
|
|
56
67
|
return true
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const NODE_MODULES = 'node_modules'
|
|
4
|
+
|
|
5
|
+
const isPrivateModule = function (file) {
|
|
6
|
+
return file && file.indexOf(NODE_MODULES) === -1
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const isNotLibraryFile = function (file) {
|
|
10
|
+
return file && file.indexOf('dd-trace-js') === -1 && file.indexOf('dd-trace') === -1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
isPrivateModule,
|
|
15
|
+
isNotLibraryFile
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { enableRewriter, disableRewriter } = require('./rewriter')
|
|
2
|
+
const { createTransaction, removeTransaction, enableTaintOperations, disableTaintOperations } = require('./operations')
|
|
3
|
+
const taintTrackingPlugin = require('./plugin')
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
enableTaintTracking () {
|
|
7
|
+
enableRewriter()
|
|
8
|
+
enableTaintOperations()
|
|
9
|
+
taintTrackingPlugin.enable()
|
|
10
|
+
},
|
|
11
|
+
disableTaintTracking () {
|
|
12
|
+
disableRewriter()
|
|
13
|
+
disableTaintOperations()
|
|
14
|
+
taintTrackingPlugin.disable()
|
|
15
|
+
},
|
|
16
|
+
createTransaction: createTransaction,
|
|
17
|
+
removeTransaction: removeTransaction
|
|
18
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
2
|
+
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
3
|
+
const { TaintTracking, TaintTrackingDummy } = require('./taint-tracking-impl')
|
|
4
|
+
|
|
5
|
+
function createTransaction (id, iastContext) {
|
|
6
|
+
if (id && iastContext) {
|
|
7
|
+
iastContext[IAST_TRANSACTION_ID] = TaintedUtils.createTransaction(id)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function removeTransaction (iastContext) {
|
|
12
|
+
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
13
|
+
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
14
|
+
TaintedUtils.removeTransaction(transactionId)
|
|
15
|
+
delete iastContext[IAST_TRANSACTION_ID]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function newTaintedString (iastContext, string, name, type) {
|
|
20
|
+
let result = string
|
|
21
|
+
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
22
|
+
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
23
|
+
result = TaintedUtils.newTaintedString(transactionId, string, name, type)
|
|
24
|
+
} else {
|
|
25
|
+
result = string
|
|
26
|
+
}
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function taintObject (iastContext, object, type) {
|
|
31
|
+
let result = object
|
|
32
|
+
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
33
|
+
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
34
|
+
const queue = [{ parent: null, property: null, value: object }]
|
|
35
|
+
const visited = new WeakSet()
|
|
36
|
+
while (queue.length > 0) {
|
|
37
|
+
const { parent, property, value } = queue.pop()
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
const tainted = TaintedUtils.newTaintedString(transactionId, value, property, type)
|
|
40
|
+
if (!parent) {
|
|
41
|
+
result = tainted
|
|
42
|
+
} else {
|
|
43
|
+
parent[property] = tainted
|
|
44
|
+
}
|
|
45
|
+
} else if (typeof value === 'object' && !visited.has(value)) {
|
|
46
|
+
visited.add(value)
|
|
47
|
+
const keys = Object.keys(value)
|
|
48
|
+
for (let i = 0; i < keys.length; i++) {
|
|
49
|
+
const key = keys[i]
|
|
50
|
+
queue.push({ parent: value, property: property ? `${property}.${key}` : key, value: value[key] })
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isTainted (iastContext, string) {
|
|
59
|
+
let result = false
|
|
60
|
+
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
61
|
+
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
62
|
+
result = TaintedUtils.isTainted(transactionId, string)
|
|
63
|
+
} else {
|
|
64
|
+
result = false
|
|
65
|
+
}
|
|
66
|
+
return result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getRanges (iastContext, string) {
|
|
70
|
+
let result = []
|
|
71
|
+
if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
|
|
72
|
+
const transactionId = iastContext[IAST_TRANSACTION_ID]
|
|
73
|
+
result = TaintedUtils.getRanges(transactionId, string)
|
|
74
|
+
} else {
|
|
75
|
+
result = []
|
|
76
|
+
}
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function enableTaintOperations () {
|
|
81
|
+
global._ddiast = TaintTracking
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function disableTaintOperations () {
|
|
85
|
+
global._ddiast = TaintTrackingDummy
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
createTransaction,
|
|
90
|
+
removeTransaction,
|
|
91
|
+
newTaintedString,
|
|
92
|
+
taintObject,
|
|
93
|
+
isTainted,
|
|
94
|
+
getRanges,
|
|
95
|
+
enableTaintOperations,
|
|
96
|
+
disableTaintOperations,
|
|
97
|
+
IAST_TRANSACTION_ID
|
|
98
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const Plugin = require('../../../plugins/plugin')
|
|
2
|
+
const { getIastContext } = require('../iast-context')
|
|
3
|
+
const { storage } = require('../../../../../datadog-core')
|
|
4
|
+
const { HTTP_REQUEST_PARAMETER, HTTP_REQUEST_BODY } = require('./origin-types')
|
|
5
|
+
const { taintObject } = require('./operations')
|
|
6
|
+
|
|
7
|
+
class TaintTrackingPlugin extends Plugin {
|
|
8
|
+
constructor () {
|
|
9
|
+
super()
|
|
10
|
+
this._type = 'taint-tracking'
|
|
11
|
+
this.addSub(
|
|
12
|
+
'datadog:body-parser:read:finish',
|
|
13
|
+
({ request }) => this._taintTrackingHandler(HTTP_REQUEST_BODY, request, 'body')
|
|
14
|
+
)
|
|
15
|
+
this.addSub(
|
|
16
|
+
'datadog:qs:parse:finish',
|
|
17
|
+
({ qs }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, qs))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_taintTrackingHandler (type, target, property) {
|
|
21
|
+
const iastContext = getIastContext(storage.getStore())
|
|
22
|
+
if (!property) {
|
|
23
|
+
target = taintObject(iastContext, target, type)
|
|
24
|
+
} else {
|
|
25
|
+
target[property] = taintObject(iastContext, target[property], type)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
enable () {
|
|
30
|
+
this.configure(true)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
disable () {
|
|
34
|
+
this.configure(false)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = new TaintTrackingPlugin()
|