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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Module = require('module')
|
|
4
|
+
const shimmer = require('../../../../../datadog-shimmer')
|
|
5
|
+
const log = require('../../../log')
|
|
6
|
+
const { isPrivateModule, isNotLibraryFile } = require('./filter')
|
|
7
|
+
const { csiMethods } = require('./csi-methods')
|
|
8
|
+
|
|
9
|
+
let rewriter
|
|
10
|
+
let getPrepareStackTrace
|
|
11
|
+
function getRewriter () {
|
|
12
|
+
if (!rewriter) {
|
|
13
|
+
try {
|
|
14
|
+
const iastRewriter = require('@datadog/native-iast-rewriter')
|
|
15
|
+
const Rewriter = iastRewriter.Rewriter
|
|
16
|
+
getPrepareStackTrace = iastRewriter.getPrepareStackTrace
|
|
17
|
+
rewriter = new Rewriter({ csiMethods })
|
|
18
|
+
} catch (e) {
|
|
19
|
+
log.warn(`Unable to initialize TaintTracking Rewriter: ${e.message}`)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return rewriter
|
|
23
|
+
}
|
|
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
|
+
|
|
39
|
+
function getCompileMethodFn (compileMethod) {
|
|
40
|
+
return function (content, filename) {
|
|
41
|
+
try {
|
|
42
|
+
if (isPrivateModule(filename) && isNotLibraryFile(filename)) {
|
|
43
|
+
content = rewriter.rewrite(content, filename)
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
log.debug(e)
|
|
47
|
+
}
|
|
48
|
+
return compileMethod.apply(this, [content, filename])
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function enableRewriter () {
|
|
53
|
+
const rewriter = getRewriter()
|
|
54
|
+
if (rewriter) {
|
|
55
|
+
Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
|
|
56
|
+
shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function disableRewriter () {
|
|
61
|
+
shimmer.unwrap(Module.prototype, '_compile')
|
|
62
|
+
Error.prepareStackTrace = originalPrepareStackTrace
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
enableRewriter, disableRewriter
|
|
67
|
+
}
|
|
@@ -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
|
|
|
@@ -50,13 +57,44 @@ function isValidVulnerability (vulnerability) {
|
|
|
50
57
|
vulnerability.location && vulnerability.location.spanId
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
function
|
|
60
|
+
function formatEvidence (evidence, sourcesIndexes) {
|
|
61
|
+
if (!evidence.ranges) {
|
|
62
|
+
return { value: evidence.value }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const valueParts = []
|
|
66
|
+
let fromIndex = 0
|
|
67
|
+
evidence.ranges.forEach((range, rangeIndex) => {
|
|
68
|
+
if (fromIndex < range.start) {
|
|
69
|
+
valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
|
|
70
|
+
}
|
|
71
|
+
valueParts.push({ value: evidence.value.substring(range.start, range.end), source: sourcesIndexes[rangeIndex] })
|
|
72
|
+
fromIndex = range.end
|
|
73
|
+
})
|
|
74
|
+
if (fromIndex < evidence.value.length) {
|
|
75
|
+
valueParts.push({ value: evidence.value.substring(fromIndex) })
|
|
76
|
+
}
|
|
77
|
+
return { valueParts }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function extractSourcesFromVulnerability (vulnerability) {
|
|
81
|
+
if (!vulnerability.evidence.ranges) {
|
|
82
|
+
return []
|
|
83
|
+
}
|
|
84
|
+
return vulnerability.evidence.ranges.map(range => (
|
|
85
|
+
{
|
|
86
|
+
origin: range.iinfo.type,
|
|
87
|
+
name: range.iinfo.parameterName,
|
|
88
|
+
value: range.iinfo.parameterValue
|
|
89
|
+
}
|
|
90
|
+
))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function jsonVulnerabilityFromVulnerability (vulnerability, sourcesIndexes) {
|
|
54
94
|
const jsonVulnerability = {
|
|
55
95
|
type: vulnerability.type,
|
|
56
96
|
hash: vulnerability.hash,
|
|
57
|
-
evidence:
|
|
58
|
-
value: vulnerability.evidence.value
|
|
59
|
-
},
|
|
97
|
+
evidence: formatEvidence(vulnerability.evidence, sourcesIndexes),
|
|
60
98
|
location: {
|
|
61
99
|
spanId: vulnerability.location.spanId
|
|
62
100
|
}
|
|
@@ -70,28 +108,53 @@ function jsonVulnerabilityFromVulnerability (vulnerability) {
|
|
|
70
108
|
return jsonVulnerability
|
|
71
109
|
}
|
|
72
110
|
|
|
73
|
-
function sendVulnerabilities (
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
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
|
+
})
|
|
81
121
|
}
|
|
82
122
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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()
|
|
86
157
|
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
if (jsonToSend.vulnerabilities.length > 0) {
|
|
90
|
-
const tags = {}
|
|
91
|
-
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
92
|
-
tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
|
|
93
|
-
tags[MANUAL_KEEP] = 'true'
|
|
94
|
-
span.addTags(tags)
|
|
95
158
|
}
|
|
96
159
|
}
|
|
97
160
|
return IAST_JSON_TAG_KEY
|
|
@@ -113,9 +176,14 @@ function deduplicateVulnerabilities (vulnerabilities) {
|
|
|
113
176
|
return deduplicated
|
|
114
177
|
}
|
|
115
178
|
|
|
179
|
+
function setTracer (_tracer) {
|
|
180
|
+
tracer = _tracer
|
|
181
|
+
}
|
|
182
|
+
|
|
116
183
|
module.exports = {
|
|
117
184
|
createVulnerability,
|
|
118
185
|
addVulnerability,
|
|
119
186
|
sendVulnerabilities,
|
|
120
|
-
clearCache
|
|
187
|
+
clearCache,
|
|
188
|
+
setTracer
|
|
121
189
|
}
|
|
@@ -1,32 +1,51 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
4
5
|
const log = require('../log')
|
|
5
6
|
const RuleManager = require('./rule_manager')
|
|
7
|
+
const remoteConfig = require('./remote_config')
|
|
6
8
|
const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('./gateway/channels')
|
|
7
9
|
const Gateway = require('./gateway/engine')
|
|
8
10
|
const addresses = require('./addresses')
|
|
9
11
|
const Reporter = require('./reporter')
|
|
10
12
|
const web = require('../plugins/util/web')
|
|
13
|
+
const { extractIp } = require('./ip_extractor')
|
|
14
|
+
const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
|
|
15
|
+
const { block, loadTemplates, loadTemplatesAsync } = require('./blocking')
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// TODO: enable dc_blocking: config.appsec.blocking === true
|
|
17
|
+
let isEnabled = false
|
|
18
|
+
let config
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
function enable (_config) {
|
|
21
|
+
if (isEnabled) return
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
try {
|
|
24
|
+
loadTemplates(_config)
|
|
25
|
+
const rules = fs.readFileSync(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
|
|
26
|
+
enableFromRules(_config, JSON.parse(rules))
|
|
20
27
|
} catch (err) {
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
abortEnable(err)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
async function enableAsync (_config) {
|
|
33
|
+
if (isEnabled) return
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await loadTemplatesAsync(_config)
|
|
37
|
+
const rules = await fs.promises.readFile(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
|
|
38
|
+
enableFromRules(_config, JSON.parse(rules))
|
|
39
|
+
} catch (err) {
|
|
40
|
+
abortEnable(err)
|
|
27
41
|
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function enableFromRules (_config, rules) {
|
|
45
|
+
RuleManager.applyRules(rules, _config.appsec)
|
|
46
|
+
remoteConfig.enableAsmData(_config.appsec)
|
|
28
47
|
|
|
29
|
-
Reporter.setRateLimit(
|
|
48
|
+
Reporter.setRateLimit(_config.appsec.rateLimit)
|
|
30
49
|
|
|
31
50
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
32
51
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
@@ -36,27 +55,57 @@ function enable (config) {
|
|
|
36
55
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_ENDPOINT)
|
|
37
56
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS)
|
|
38
57
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
|
|
58
|
+
|
|
59
|
+
isEnabled = true
|
|
60
|
+
config = _config
|
|
39
61
|
}
|
|
40
62
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})
|
|
49
|
-
}
|
|
63
|
+
function abortEnable (err) {
|
|
64
|
+
log.error('Unable to start AppSec')
|
|
65
|
+
log.error(err)
|
|
66
|
+
|
|
67
|
+
// abort AppSec start
|
|
68
|
+
RuleManager.clearAllRules()
|
|
69
|
+
remoteConfig.disableAsmData()
|
|
50
70
|
}
|
|
51
71
|
|
|
52
|
-
function
|
|
72
|
+
function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
73
|
+
const topSpan = web.root(req)
|
|
74
|
+
if (!topSpan) return
|
|
75
|
+
|
|
76
|
+
const clientIp = extractIp(config, req)
|
|
77
|
+
|
|
78
|
+
topSpan.addTags({
|
|
79
|
+
'_dd.appsec.enabled': 1,
|
|
80
|
+
'_dd.runtime_family': 'nodejs',
|
|
81
|
+
[HTTP_CLIENT_IP]: clientIp
|
|
82
|
+
})
|
|
83
|
+
|
|
53
84
|
const store = Gateway.startContext()
|
|
54
85
|
|
|
55
|
-
store.set('req',
|
|
56
|
-
store.set('res',
|
|
86
|
+
store.set('req', req)
|
|
87
|
+
store.set('res', res)
|
|
57
88
|
|
|
58
89
|
const context = store.get('context')
|
|
59
90
|
|
|
91
|
+
if (clientIp) {
|
|
92
|
+
const results = Gateway.propagate({
|
|
93
|
+
[addresses.HTTP_CLIENT_IP]: clientIp
|
|
94
|
+
}, context)
|
|
95
|
+
|
|
96
|
+
if (!results || !abortController) return
|
|
97
|
+
|
|
98
|
+
for (const entry of results) {
|
|
99
|
+
if (entry && entry.includes('block')) {
|
|
100
|
+
block(req, res, topSpan, abortController)
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function incomingHttpEndTranslator (data) {
|
|
108
|
+
const context = Gateway.getContext()
|
|
60
109
|
if (!context) return
|
|
61
110
|
|
|
62
111
|
const requestHeaders = Object.assign({}, data.req.headers)
|
|
@@ -97,7 +146,7 @@ function incomingHttpEndTranslator (data) {
|
|
|
97
146
|
payload[addresses.HTTP_INCOMING_COOKIES] = {}
|
|
98
147
|
|
|
99
148
|
for (const k of Object.keys(data.req.cookies)) {
|
|
100
|
-
payload[addresses.HTTP_INCOMING_COOKIES][k] = [
|
|
149
|
+
payload[addresses.HTTP_INCOMING_COOKIES][k] = [data.req.cookies[k]]
|
|
101
150
|
}
|
|
102
151
|
}
|
|
103
152
|
|
|
@@ -107,7 +156,11 @@ function incomingHttpEndTranslator (data) {
|
|
|
107
156
|
}
|
|
108
157
|
|
|
109
158
|
function disable () {
|
|
159
|
+
isEnabled = false
|
|
160
|
+
config = null
|
|
161
|
+
|
|
110
162
|
RuleManager.clearAllRules()
|
|
163
|
+
remoteConfig.disableAsmData()
|
|
111
164
|
|
|
112
165
|
// Channel#unsubscribe() is undefined for non active channels
|
|
113
166
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
|
|
@@ -116,6 +169,7 @@ function disable () {
|
|
|
116
169
|
|
|
117
170
|
module.exports = {
|
|
118
171
|
enable,
|
|
172
|
+
enableAsync,
|
|
119
173
|
disable,
|
|
120
174
|
incomingHttpStartTranslator,
|
|
121
175
|
incomingHttpEndTranslator
|
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const BlockList = require('./ip_blocklist')
|
|
4
|
+
const net = require('net')
|
|
5
|
+
const log = require('../log')
|
|
6
|
+
|
|
7
|
+
const ipHeaderList = [
|
|
8
|
+
'x-forwarded-for',
|
|
9
|
+
'x-real-ip',
|
|
10
|
+
'client-ip',
|
|
11
|
+
'x-forwarded',
|
|
12
|
+
'x-cluster-client-ip',
|
|
13
|
+
'forwarded-for',
|
|
14
|
+
'forwarded',
|
|
15
|
+
'via',
|
|
16
|
+
'true-client-ip'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
const privateCIDRs = [
|
|
20
|
+
'127.0.0.0/8',
|
|
21
|
+
'10.0.0.0/8',
|
|
22
|
+
'172.16.0.0/12',
|
|
23
|
+
'192.168.0.0/16',
|
|
24
|
+
'169.254.0.0/16',
|
|
25
|
+
'::1/128',
|
|
26
|
+
'fec0::/10',
|
|
27
|
+
'fe80::/10',
|
|
28
|
+
'fc00::/7',
|
|
29
|
+
'fd00::/8'
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
const privateIPMatcher = new BlockList()
|
|
33
|
+
|
|
34
|
+
for (const cidr of privateCIDRs) {
|
|
35
|
+
const [address, prefix] = cidr.split('/')
|
|
36
|
+
|
|
37
|
+
privateIPMatcher.addSubnet(address, parseInt(prefix), net.isIPv6(address) ? 'ipv6' : 'ipv4')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function extractIp (config, req) {
|
|
41
|
+
const headers = req.headers
|
|
42
|
+
if (config.clientIpHeader) {
|
|
43
|
+
if (!headers) return
|
|
44
|
+
const header = headers[config.clientIpHeader]
|
|
45
|
+
if (!header) return
|
|
46
|
+
|
|
47
|
+
return findFirstIp(header)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const foundHeaders = []
|
|
51
|
+
if (headers) {
|
|
52
|
+
for (let i = 0; i < ipHeaderList.length; i++) {
|
|
53
|
+
if (headers[ipHeaderList[i]]) {
|
|
54
|
+
foundHeaders.push(ipHeaderList[i])
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (foundHeaders.length === 1) {
|
|
60
|
+
const header = headers[foundHeaders[0]]
|
|
61
|
+
const firstIp = findFirstIp(header)
|
|
62
|
+
|
|
63
|
+
if (firstIp) return firstIp
|
|
64
|
+
} else if (foundHeaders.length > 1) {
|
|
65
|
+
log.error(`Cannot find client IP: multiple IP headers detected ${foundHeaders}`)
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return req.socket && req.socket.remoteAddress
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function findFirstIp (str) {
|
|
73
|
+
let firstPrivateIp
|
|
74
|
+
const splitted = str.split(',')
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i < splitted.length; i++) {
|
|
77
|
+
const chunk = splitted[i].trim()
|
|
78
|
+
|
|
79
|
+
// TODO: strip port and interface data ?
|
|
80
|
+
|
|
81
|
+
const type = net.isIP(chunk)
|
|
82
|
+
if (!type) continue
|
|
83
|
+
|
|
84
|
+
if (!privateIPMatcher.check(chunk, type === 6 ? 'ipv6' : 'ipv4')) {
|
|
85
|
+
// it's public, return it immediately
|
|
86
|
+
return chunk
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// it's private, only save the first one found
|
|
90
|
+
if (!firstPrivateIp) firstPrivateIp = chunk
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return firstPrivateIp
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
extractIp
|
|
98
|
+
}
|