dd-trace 3.17.1 → 3.19.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 +1 -1
- package/esbuild.js +3 -0
- package/index.d.ts +10 -9
- package/package.json +12 -12
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +9 -2
- package/packages/datadog-instrumentations/src/body-parser.js +15 -9
- package/packages/datadog-instrumentations/src/cucumber.js +11 -1
- package/packages/datadog-instrumentations/src/express.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/http/server.js +2 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha.js +19 -2
- package/packages/datadog-instrumentations/src/playwright.js +5 -2
- package/packages/datadog-plugin-amqp10/src/consumer.js +1 -3
- package/packages/datadog-plugin-amqp10/src/producer.js +1 -3
- package/packages/datadog-plugin-amqplib/src/client.js +4 -3
- package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
- package/packages/datadog-plugin-amqplib/src/producer.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +6 -4
- package/packages/datadog-plugin-cypress/src/plugin.js +5 -1
- package/packages/datadog-plugin-cypress/src/support.js +4 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -3
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -3
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -3
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +2 -2
- package/packages/datadog-plugin-http2/src/server.js +0 -5
- package/packages/datadog-plugin-jest/src/index.js +10 -5
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -4
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -3
- package/packages/datadog-plugin-mocha/src/index.js +9 -4
- package/packages/datadog-plugin-playwright/src/index.js +6 -5
- package/packages/datadog-plugin-redis/src/index.js +16 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
- package/packages/datadog-plugin-rhea/src/producer.js +1 -5
- package/packages/dd-trace/src/appsec/addresses.js +0 -3
- package/packages/dd-trace/src/appsec/blocked_templates.js +2 -9
- package/packages/dd-trace/src/appsec/blocking.js +1 -1
- package/packages/dd-trace/src/appsec/{gateway/channels.js → channels.js} +4 -4
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +21 -13
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +87 -79
- package/packages/dd-trace/src/appsec/recommended.json +448 -121
- package/packages/dd-trace/src/appsec/remote_config/apply_states.js +7 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +30 -11
- package/packages/dd-trace/src/appsec/remote_config/manager.js +33 -12
- package/packages/dd-trace/src/appsec/reporter.js +27 -58
- package/packages/dd-trace/src/appsec/rule_manager.js +160 -32
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -12
- package/packages/dd-trace/src/appsec/waf/index.js +75 -0
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +57 -0
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +66 -0
- package/packages/dd-trace/src/config.js +18 -1
- package/packages/dd-trace/src/dcitm.js +2 -0
- package/packages/dd-trace/src/encode/0.4.js +12 -4
- package/packages/dd-trace/src/encode/tags-processors.js +40 -68
- package/packages/dd-trace/src/exporters/common/request.js +2 -2
- package/packages/dd-trace/src/format.js +2 -1
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +11 -12
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +3 -1
- package/packages/dd-trace/src/plugins/client.js +3 -2
- package/packages/dd-trace/src/plugins/consumer.js +17 -2
- package/packages/dd-trace/src/plugins/inbound.js +7 -0
- package/packages/dd-trace/src/plugins/{outgoing.js → outbound.js} +2 -2
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/producer.js +17 -2
- package/packages/dd-trace/src/plugins/server.js +2 -2
- package/packages/dd-trace/src/plugins/tracing.js +11 -0
- package/packages/dd-trace/src/plugins/util/test.js +19 -1
- package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/service-naming/index.js +41 -0
- package/packages/dd-trace/src/service-naming/schemas/definition.js +28 -0
- package/packages/dd-trace/src/service-naming/schemas/index.js +6 -0
- package/packages/dd-trace/src/service-naming/schemas/v0.js +66 -0
- package/packages/dd-trace/src/service-naming/schemas/v1.js +58 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +1 -1
- package/packages/diagnostics_channel/index.js +3 -0
- package/packages/diagnostics_channel/src/index.js +57 -0
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +0 -137
- package/packages/dd-trace/src/appsec/callbacks/index.js +0 -7
- package/packages/dd-trace/src/appsec/gateway/als.js +0 -6
- package/packages/dd-trace/src/appsec/gateway/engine/engine.js +0 -140
- package/packages/dd-trace/src/appsec/gateway/engine/index.js +0 -51
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -42
- package/packages/dd-trace/src/instrumenter.js +0 -203
- package/packages/dd-trace/src/loader.js +0 -131
- package/packages/dd-trace/src/plugins/incoming.js +0 -7
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
6
|
-
const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/
|
|
6
|
+
const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/channels')
|
|
7
7
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
8
8
|
|
|
9
9
|
class HttpServerPlugin extends Plugin {
|
|
@@ -33,7 +33,7 @@ class HttpServerPlugin extends Plugin {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (incomingHttpRequestStart.hasSubscribers) {
|
|
36
|
-
incomingHttpRequestStart.publish({ req, res, abortController })
|
|
36
|
+
incomingHttpRequestStart.publish({ req, res, abortController }) // TODO: no need to make a new object here
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
39
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
6
6
|
const { storage } = require('../../datadog-core')
|
|
7
7
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
8
|
-
const { incomingHttpRequestStart } = require('../../dd-trace/src/appsec/gateway/channels')
|
|
9
8
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
10
9
|
|
|
11
10
|
class Http2ServerPlugin extends Plugin {
|
|
@@ -30,10 +29,6 @@ class Http2ServerPlugin extends Plugin {
|
|
|
30
29
|
context.res.writeHead = web.wrapWriteHead(context)
|
|
31
30
|
context.instrumented = true
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
if (incomingHttpRequestStart.hasSubscribers) {
|
|
35
|
-
incomingHttpRequestStart.publish({ req, res })
|
|
36
|
-
}
|
|
37
32
|
})
|
|
38
33
|
|
|
39
34
|
this.addSub('apm:http2:server:request:error', (error) => {
|
|
@@ -9,7 +9,8 @@ const {
|
|
|
9
9
|
addIntelligentTestRunnerSpanTags,
|
|
10
10
|
TEST_PARAMETERS,
|
|
11
11
|
TEST_COMMAND,
|
|
12
|
-
TEST_FRAMEWORK_VERSION
|
|
12
|
+
TEST_FRAMEWORK_VERSION,
|
|
13
|
+
TEST_SOURCE_START
|
|
13
14
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
14
15
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
15
16
|
const id = require('../../dd-trace/src/id')
|
|
@@ -31,8 +32,11 @@ class JestPlugin extends CiPlugin {
|
|
|
31
32
|
// Used to handle the end of a jest worker to be able to flush
|
|
32
33
|
const handler = ([message]) => {
|
|
33
34
|
if (message === CHILD_MESSAGE_END) {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// testSuiteSpan is not defined for older versions of jest, where jest-jasmine2 is still used
|
|
36
|
+
if (this.testSuiteSpan) {
|
|
37
|
+
this.testSuiteSpan.finish()
|
|
38
|
+
finishAllTraceSpans(this.testSuiteSpan)
|
|
39
|
+
}
|
|
36
40
|
this.tracer._exporter.flush()
|
|
37
41
|
process.removeListener('message', handler)
|
|
38
42
|
}
|
|
@@ -187,12 +191,13 @@ class JestPlugin extends CiPlugin {
|
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
startTestSpan (test) {
|
|
190
|
-
const { suite, name, runner, testParameters, frameworkVersion } = test
|
|
194
|
+
const { suite, name, runner, testParameters, frameworkVersion, testStartLine } = test
|
|
191
195
|
|
|
192
196
|
const extraTags = {
|
|
193
197
|
[JEST_TEST_RUNNER]: runner,
|
|
194
198
|
[TEST_PARAMETERS]: testParameters,
|
|
195
|
-
[TEST_FRAMEWORK_VERSION]: frameworkVersion
|
|
199
|
+
[TEST_FRAMEWORK_VERSION]: frameworkVersion,
|
|
200
|
+
[TEST_SOURCE_START]: testStartLine
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)
|
|
@@ -8,12 +8,9 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
|
|
|
8
8
|
|
|
9
9
|
start ({ topic, partition, message }) {
|
|
10
10
|
const childOf = extract(this.tracer, message.headers)
|
|
11
|
-
|
|
12
|
-
this.startSpan('kafka.consume', {
|
|
11
|
+
this.startSpan({
|
|
13
12
|
childOf,
|
|
14
|
-
service: this.config.service || `${this.tracer._service}-kafka`,
|
|
15
13
|
resource: topic,
|
|
16
|
-
kind: 'consumer',
|
|
17
14
|
type: 'worker',
|
|
18
15
|
meta: {
|
|
19
16
|
'component': 'kafkajs',
|
|
@@ -7,10 +7,8 @@ class KafkajsProducerPlugin extends ProducerPlugin {
|
|
|
7
7
|
static get operation () { return 'produce' }
|
|
8
8
|
|
|
9
9
|
start ({ topic, messages }) {
|
|
10
|
-
const span = this.startSpan(
|
|
11
|
-
service: this.config.service || `${this.tracer._service}-kafka`,
|
|
10
|
+
const span = this.startSpan({
|
|
12
11
|
resource: topic,
|
|
13
|
-
kind: 'producer',
|
|
14
12
|
meta: {
|
|
15
13
|
'component': 'kafkajs',
|
|
16
14
|
'kafka.topic': topic
|
|
@@ -10,7 +10,8 @@ const {
|
|
|
10
10
|
getTestSuitePath,
|
|
11
11
|
getTestParametersString,
|
|
12
12
|
getTestSuiteCommonTags,
|
|
13
|
-
addIntelligentTestRunnerSpanTags
|
|
13
|
+
addIntelligentTestRunnerSpanTags,
|
|
14
|
+
TEST_SOURCE_START
|
|
14
15
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
15
16
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
16
17
|
|
|
@@ -85,9 +86,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
85
86
|
}
|
|
86
87
|
})
|
|
87
88
|
|
|
88
|
-
this.addSub('ci:mocha:test:start', (test) => {
|
|
89
|
+
this.addSub('ci:mocha:test:start', ({ test, testStartLine }) => {
|
|
89
90
|
const store = storage.getStore()
|
|
90
|
-
const span = this.startTestSpan(test)
|
|
91
|
+
const span = this.startTestSpan(test, testStartLine)
|
|
91
92
|
|
|
92
93
|
this.enter(span, store)
|
|
93
94
|
})
|
|
@@ -153,7 +154,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
153
154
|
})
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
startTestSpan (test) {
|
|
157
|
+
startTestSpan (test, testStartLine) {
|
|
157
158
|
const testName = test.fullTitle()
|
|
158
159
|
const { file: testSuiteAbsolutePath, title } = test
|
|
159
160
|
|
|
@@ -163,6 +164,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
163
164
|
extraTags[TEST_PARAMETERS] = testParametersString
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
if (testStartLine) {
|
|
168
|
+
extraTags[TEST_SOURCE_START] = testStartLine
|
|
169
|
+
}
|
|
170
|
+
|
|
166
171
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
167
172
|
const testSuiteSpan = this._testSuites.get(testSuiteAbsolutePath)
|
|
168
173
|
|
|
@@ -7,7 +7,8 @@ const {
|
|
|
7
7
|
TEST_STATUS,
|
|
8
8
|
finishAllTraceSpans,
|
|
9
9
|
getTestSuitePath,
|
|
10
|
-
getTestSuiteCommonTags
|
|
10
|
+
getTestSuiteCommonTags,
|
|
11
|
+
TEST_SOURCE_START
|
|
11
12
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
12
13
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
13
14
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -64,10 +65,10 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
64
65
|
span.finish()
|
|
65
66
|
})
|
|
66
67
|
|
|
67
|
-
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath }) => {
|
|
68
|
+
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine }) => {
|
|
68
69
|
const store = storage.getStore()
|
|
69
70
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
70
|
-
const span = this.startTestSpan(testName, testSuite)
|
|
71
|
+
const span = this.startTestSpan(testName, testSuite, testSourceLine)
|
|
71
72
|
|
|
72
73
|
this.enter(span, store)
|
|
73
74
|
})
|
|
@@ -104,9 +105,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
104
105
|
})
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
startTestSpan (testName, testSuite) {
|
|
108
|
+
startTestSpan (testName, testSuite, testSourceLine) {
|
|
108
109
|
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
109
|
-
return super.startTestSpan(testName, testSuite, testSuiteSpan)
|
|
110
|
+
return super.startTestSpan(testName, testSuite, testSuiteSpan, { [TEST_SOURCE_START]: testSourceLine })
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -9,17 +9,19 @@ class RedisPlugin extends CachePlugin {
|
|
|
9
9
|
static get system () { return 'redis' }
|
|
10
10
|
|
|
11
11
|
start ({ db, command, args, connectionOptions = {}, connectionName }) {
|
|
12
|
-
|
|
12
|
+
const resource = command
|
|
13
|
+
const normalizedCommand = command.toUpperCase()
|
|
14
|
+
if (!this.config.filter(normalizedCommand)) return this.skip()
|
|
13
15
|
|
|
14
16
|
this.startSpan('redis.command', {
|
|
15
17
|
service: getService(this.config, connectionName),
|
|
16
|
-
resource
|
|
18
|
+
resource,
|
|
17
19
|
type: 'redis',
|
|
18
20
|
kind: 'client',
|
|
19
21
|
meta: {
|
|
20
22
|
'db.type': 'redis',
|
|
21
23
|
'db.name': db || '0',
|
|
22
|
-
'redis.raw_command': formatCommand(
|
|
24
|
+
'redis.raw_command': formatCommand(normalizedCommand, args),
|
|
23
25
|
'out.host': connectionOptions.host,
|
|
24
26
|
[CLIENT_PORT_KEY]: connectionOptions.port
|
|
25
27
|
}
|
|
@@ -42,8 +44,6 @@ function getService (config, connectionName) {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function formatCommand (command, args) {
|
|
45
|
-
command = command.toUpperCase()
|
|
46
|
-
|
|
47
47
|
if (!args || command === 'AUTH') return command
|
|
48
48
|
|
|
49
49
|
for (let i = 0, l = args.length; i < l; i++) {
|
|
@@ -76,6 +76,11 @@ function trim (str, maxlen) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function normalizeConfig (config) {
|
|
79
|
+
if (config.allowlist) uppercaseAllEntries(config.allowlist)
|
|
80
|
+
if (config.whitelist) uppercaseAllEntries(config.whitelist)
|
|
81
|
+
if (config.blocklist) uppercaseAllEntries(config.blocklist)
|
|
82
|
+
if (config.blacklist) uppercaseAllEntries(config.blacklist)
|
|
83
|
+
|
|
79
84
|
const filter = urlFilter.getFilter(config)
|
|
80
85
|
|
|
81
86
|
return Object.assign({}, config, {
|
|
@@ -83,4 +88,10 @@ function normalizeConfig (config) {
|
|
|
83
88
|
})
|
|
84
89
|
}
|
|
85
90
|
|
|
91
|
+
function uppercaseAllEntries (entries) {
|
|
92
|
+
for (let i = 0; i < entries.length; i++) {
|
|
93
|
+
entries[i] = String(entries[i]).toUpperCase()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
module.exports = RedisPlugin
|
|
@@ -19,12 +19,10 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
19
19
|
const name = getResourceNameFromMessage(msgObj)
|
|
20
20
|
const childOf = extractTextMap(msgObj, this.tracer)
|
|
21
21
|
|
|
22
|
-
this.startSpan(
|
|
22
|
+
this.startSpan({
|
|
23
23
|
childOf,
|
|
24
|
-
service: this.config.service,
|
|
25
24
|
resource: name,
|
|
26
25
|
type: 'worker',
|
|
27
|
-
kind: 'consumer',
|
|
28
26
|
meta: {
|
|
29
27
|
'component': 'rhea',
|
|
30
28
|
'amqp.link.source.address': name,
|
|
@@ -9,17 +9,13 @@ class RheaProducerPlugin extends ProducerPlugin {
|
|
|
9
9
|
|
|
10
10
|
constructor (...args) {
|
|
11
11
|
super(...args)
|
|
12
|
-
|
|
13
12
|
this.addTraceSub('encode', this.encode.bind(this))
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
start ({ targetAddress, host, port }) {
|
|
17
16
|
const name = targetAddress || 'amq.topic'
|
|
18
|
-
|
|
19
|
-
this.startSpan('amqp.send', {
|
|
20
|
-
service: this.config.service || `${this.tracer._service}-amqp-producer`,
|
|
17
|
+
this.startSpan({
|
|
21
18
|
resource: name,
|
|
22
|
-
kind: 'producer',
|
|
23
19
|
meta: {
|
|
24
20
|
'component': 'rhea',
|
|
25
21
|
'amqp.link.target.address': name,
|
|
@@ -7,14 +7,11 @@ module.exports = {
|
|
|
7
7
|
// TODO: 'server.request.trailers',
|
|
8
8
|
HTTP_INCOMING_URL: 'server.request.uri.raw',
|
|
9
9
|
HTTP_INCOMING_METHOD: 'server.request.method',
|
|
10
|
-
HTTP_INCOMING_ENDPOINT: 'server.request.framework_endpoint',
|
|
11
10
|
HTTP_INCOMING_PARAMS: 'server.request.path_params',
|
|
12
11
|
HTTP_INCOMING_COOKIES: 'server.request.cookies',
|
|
13
12
|
HTTP_INCOMING_RESPONSE_CODE: 'server.response.status',
|
|
14
13
|
HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies',
|
|
15
14
|
// TODO: 'server.response.trailers',
|
|
16
|
-
HTTP_INCOMING_REMOTE_IP: 'server.request.client_ip',
|
|
17
|
-
HTTP_INCOMING_REMOTE_PORT: 'server.request.client_port',
|
|
18
15
|
|
|
19
16
|
HTTP_CLIENT_IP: 'http.client_ip',
|
|
20
17
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
const html = `<!-- Sorry, you
|
|
4
|
+
const html = `<!-- Sorry, you’ve been blocked -->
|
|
5
5
|
<!DOCTYPE html>
|
|
6
6
|
<html lang="en">
|
|
7
7
|
|
|
@@ -102,14 +102,7 @@ const html = `<!-- Sorry, you've been blocked -->
|
|
|
102
102
|
</html>
|
|
103
103
|
`
|
|
104
104
|
|
|
105
|
-
const json = `{
|
|
106
|
-
"errors": [
|
|
107
|
-
{
|
|
108
|
-
"title": "You've been blocked",
|
|
109
|
-
"detail": "Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
}`
|
|
105
|
+
const json = `{"errors": [{"title": "You've been blocked", "detail": "Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}`
|
|
113
106
|
|
|
114
107
|
module.exports = {
|
|
115
108
|
html,
|
|
@@ -19,7 +19,7 @@ function block (req, res, rootSpan, abortController) {
|
|
|
19
19
|
const accept = req.headers.accept && req.headers.accept.split(',').map((str) => str.split(';', 1)[0].trim())
|
|
20
20
|
|
|
21
21
|
if (accept && accept.includes('text/html') && !accept.includes('application/json')) {
|
|
22
|
-
type = 'text/html'
|
|
22
|
+
type = 'text/html; charset=utf-8'
|
|
23
23
|
body = templateHtml
|
|
24
24
|
} else {
|
|
25
25
|
type = 'application/json'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dc = require('diagnostics_channel')
|
|
3
|
+
const dc = require('../../../diagnostics_channel')
|
|
4
4
|
|
|
5
5
|
// TODO: use TBD naming convention
|
|
6
|
-
// or directly use http plugin's channels
|
|
7
|
-
// when it gets converted to new plugin system
|
|
8
6
|
module.exports = {
|
|
9
7
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
10
|
-
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd')
|
|
8
|
+
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
9
|
+
bodyParser: dc.channel('datadog:body-parser:read:finish'),
|
|
10
|
+
queryParser: dc.channel('datadog:query:read:finish')
|
|
11
11
|
}
|
|
@@ -3,7 +3,7 @@ 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
|
-
const dc = require('diagnostics_channel')
|
|
6
|
+
const dc = require('../../../../diagnostics_channel')
|
|
7
7
|
const iastContextFunctions = require('./iast-context')
|
|
8
8
|
const { enableTaintTracking, disableTaintTracking, createTransaction, removeTransaction } = require('./taint-tracking')
|
|
9
9
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
4
4
|
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
5
|
+
const iastLog = require('../iast-log')
|
|
5
6
|
const { TaintTracking, TaintTrackingDummy } = require('./taint-tracking-impl')
|
|
6
7
|
|
|
7
8
|
function createTransaction (id, iastContext) {
|
|
@@ -37,20 +38,27 @@ function taintObject (iastContext, object, type) {
|
|
|
37
38
|
const visited = new WeakSet()
|
|
38
39
|
while (queue.length > 0) {
|
|
39
40
|
const { parent, property, value } = queue.pop()
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
if (value === null) {
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
if (typeof value === 'string') {
|
|
46
|
+
const tainted = TaintedUtils.newTaintedString(transactionId, value, property, type)
|
|
47
|
+
if (!parent) {
|
|
48
|
+
result = tainted
|
|
49
|
+
} else {
|
|
50
|
+
parent[property] = tainted
|
|
51
|
+
}
|
|
52
|
+
} else if (typeof value === 'object' && !visited.has(value)) {
|
|
53
|
+
visited.add(value)
|
|
54
|
+
const keys = Object.keys(value)
|
|
55
|
+
for (let i = 0; i < keys.length; i++) {
|
|
56
|
+
const key = keys[i]
|
|
57
|
+
queue.push({ parent: value, property: property ? `${property}.${key}` : key, value: value[key] })
|
|
58
|
+
}
|
|
53
59
|
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
iastLog.error(`Error visiting property : ${property}`).errorAndPublish(e)
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
64
|
}
|
|
@@ -12,7 +12,7 @@ class TaintTrackingPlugin extends Plugin {
|
|
|
12
12
|
this._type = 'taint-tracking'
|
|
13
13
|
this.addSub(
|
|
14
14
|
'datadog:body-parser:read:finish',
|
|
15
|
-
({
|
|
15
|
+
({ req }) => this._taintTrackingHandler(HTTP_REQUEST_BODY, req, 'body')
|
|
16
16
|
)
|
|
17
17
|
this.addSub(
|
|
18
18
|
'datadog:qs:parse:finish',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dc = require('diagnostics_channel')
|
|
3
|
+
const dc = require('../../../../../diagnostics_channel')
|
|
4
4
|
const logCollector = require('./log_collector')
|
|
5
5
|
const { sendData } = require('../../../telemetry/send-data')
|
|
6
6
|
const log = require('../../../log')
|
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
const log = require('../log')
|
|
4
4
|
const RuleManager = require('./rule_manager')
|
|
5
5
|
const remoteConfig = require('./remote_config')
|
|
6
|
-
const {
|
|
7
|
-
|
|
6
|
+
const {
|
|
7
|
+
incomingHttpRequestStart,
|
|
8
|
+
incomingHttpRequestEnd,
|
|
9
|
+
bodyParser,
|
|
10
|
+
queryParser
|
|
11
|
+
} = require('./channels')
|
|
12
|
+
const waf = require('./waf')
|
|
8
13
|
const addresses = require('./addresses')
|
|
9
14
|
const Reporter = require('./reporter')
|
|
10
15
|
const web = require('../plugins/util/web')
|
|
@@ -21,39 +26,25 @@ function enable (_config) {
|
|
|
21
26
|
try {
|
|
22
27
|
setTemplates(_config)
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
enableFromRules(_config, _config.appsec.rules)
|
|
26
|
-
} catch (err) {
|
|
27
|
-
abortEnable(err)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
29
|
+
RuleManager.applyRules(_config.appsec.rules, _config.appsec)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
RuleManager.applyRules(rules, _config.appsec)
|
|
33
|
-
remoteConfig.enableAsmData(_config.appsec)
|
|
31
|
+
remoteConfig.enableWafUpdate(_config.appsec)
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
Reporter.setRateLimit(_config.appsec.rateLimit)
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_HEADERS)
|
|
42
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_ENDPOINT)
|
|
43
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS)
|
|
44
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
|
|
45
|
-
|
|
46
|
-
isEnabled = true
|
|
47
|
-
config = _config
|
|
48
|
-
}
|
|
35
|
+
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
36
|
+
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
37
|
+
bodyParser.subscribe(onRequestBodyParsed)
|
|
38
|
+
queryParser.subscribe(onRequestQueryParsed)
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
isEnabled = true
|
|
41
|
+
config = _config
|
|
42
|
+
} catch (err) {
|
|
43
|
+
log.error('Unable to start AppSec')
|
|
44
|
+
log.error(err)
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
remoteConfig.disableAsmData()
|
|
46
|
+
disable()
|
|
47
|
+
}
|
|
57
48
|
}
|
|
58
49
|
|
|
59
50
|
function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
@@ -68,78 +59,92 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
|
68
59
|
[HTTP_CLIENT_IP]: clientIp
|
|
69
60
|
})
|
|
70
61
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
store.set('req', req)
|
|
74
|
-
store.set('res', res)
|
|
62
|
+
const requestHeaders = Object.assign({}, req.headers)
|
|
63
|
+
delete requestHeaders.cookie
|
|
75
64
|
|
|
76
|
-
const
|
|
65
|
+
const payload = {
|
|
66
|
+
[addresses.HTTP_INCOMING_URL]: req.url,
|
|
67
|
+
[addresses.HTTP_INCOMING_HEADERS]: requestHeaders,
|
|
68
|
+
[addresses.HTTP_INCOMING_METHOD]: req.method
|
|
69
|
+
}
|
|
77
70
|
|
|
78
71
|
if (clientIp) {
|
|
79
|
-
|
|
80
|
-
[addresses.HTTP_CLIENT_IP]: clientIp
|
|
81
|
-
}, context)
|
|
82
|
-
|
|
83
|
-
if (!results || !abortController) return
|
|
84
|
-
|
|
85
|
-
for (const entry of results) {
|
|
86
|
-
if (entry && entry.includes('block')) {
|
|
87
|
-
block(req, res, rootSpan, abortController)
|
|
88
|
-
break
|
|
89
|
-
}
|
|
90
|
-
}
|
|
72
|
+
payload[addresses.HTTP_CLIENT_IP] = clientIp
|
|
91
73
|
}
|
|
92
|
-
}
|
|
93
74
|
|
|
94
|
-
|
|
95
|
-
const context = Gateway.getContext()
|
|
96
|
-
if (!context) return
|
|
75
|
+
const actions = waf.run(payload, req)
|
|
97
76
|
|
|
98
|
-
|
|
99
|
-
|
|
77
|
+
handleResults(actions, req, res, rootSpan, abortController)
|
|
78
|
+
}
|
|
100
79
|
|
|
80
|
+
function incomingHttpEndTranslator ({ req, res }) {
|
|
101
81
|
// TODO: this doesn't support headers sent with res.writeHead()
|
|
102
|
-
const responseHeaders = Object.assign({},
|
|
82
|
+
const responseHeaders = Object.assign({}, res.getHeaders())
|
|
103
83
|
delete responseHeaders['set-cookie']
|
|
104
84
|
|
|
105
85
|
const payload = {
|
|
106
|
-
[addresses.
|
|
107
|
-
[addresses.HTTP_INCOMING_HEADERS]: requestHeaders,
|
|
108
|
-
[addresses.HTTP_INCOMING_METHOD]: data.req.method,
|
|
109
|
-
[addresses.HTTP_INCOMING_REMOTE_IP]: data.req.socket.remoteAddress,
|
|
110
|
-
[addresses.HTTP_INCOMING_REMOTE_PORT]: data.req.socket.remotePort,
|
|
111
|
-
[addresses.HTTP_INCOMING_RESPONSE_CODE]: data.res.statusCode,
|
|
86
|
+
[addresses.HTTP_INCOMING_RESPONSE_CODE]: res.statusCode,
|
|
112
87
|
[addresses.HTTP_INCOMING_RESPONSE_HEADERS]: responseHeaders
|
|
113
88
|
}
|
|
114
89
|
|
|
115
|
-
//
|
|
116
|
-
if
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (data.req.query && typeof data.req.query === 'object') {
|
|
121
|
-
payload[addresses.HTTP_INCOMING_QUERY] = data.req.query
|
|
90
|
+
// we need to keep this to support other body parsers
|
|
91
|
+
// TODO: no need to analyze it if it was already done by the body-parser hook
|
|
92
|
+
if (req.body !== undefined && req.body !== null) {
|
|
93
|
+
payload[addresses.HTTP_INCOMING_BODY] = req.body
|
|
122
94
|
}
|
|
123
95
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (data.req.params && typeof data.req.params === 'object') {
|
|
129
|
-
payload[addresses.HTTP_INCOMING_PARAMS] = data.req.params
|
|
96
|
+
// TODO: temporary express instrumentation, will use express plugin later
|
|
97
|
+
if (req.params && typeof req.params === 'object') {
|
|
98
|
+
payload[addresses.HTTP_INCOMING_PARAMS] = req.params
|
|
130
99
|
}
|
|
131
100
|
|
|
132
|
-
if (
|
|
101
|
+
if (req.cookies && typeof req.cookies === 'object') {
|
|
133
102
|
payload[addresses.HTTP_INCOMING_COOKIES] = {}
|
|
134
103
|
|
|
135
|
-
for (const k of Object.keys(
|
|
136
|
-
payload[addresses.HTTP_INCOMING_COOKIES][k] = [
|
|
104
|
+
for (const k of Object.keys(req.cookies)) {
|
|
105
|
+
payload[addresses.HTTP_INCOMING_COOKIES][k] = [req.cookies[k]]
|
|
137
106
|
}
|
|
138
107
|
}
|
|
139
108
|
|
|
140
|
-
|
|
109
|
+
waf.run(payload, req)
|
|
110
|
+
|
|
111
|
+
waf.disposeContext(req)
|
|
112
|
+
|
|
113
|
+
Reporter.finishRequest(req, res)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function onRequestBodyParsed ({ req, res, abortController }) {
|
|
117
|
+
const rootSpan = web.root(req)
|
|
118
|
+
if (!rootSpan) return
|
|
119
|
+
|
|
120
|
+
if (req.body === undefined || req.body === null) return
|
|
121
|
+
|
|
122
|
+
const results = waf.run({
|
|
123
|
+
[addresses.HTTP_INCOMING_BODY]: req.body
|
|
124
|
+
}, req)
|
|
141
125
|
|
|
142
|
-
|
|
126
|
+
handleResults(results, req, res, rootSpan, abortController)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function onRequestQueryParsed ({ req, res, abortController }) {
|
|
130
|
+
const rootSpan = web.root(req)
|
|
131
|
+
if (!rootSpan) return
|
|
132
|
+
|
|
133
|
+
if (!req.query || typeof req.query !== 'object') return
|
|
134
|
+
|
|
135
|
+
const results = waf.run({
|
|
136
|
+
[addresses.HTTP_INCOMING_QUERY]: req.query
|
|
137
|
+
}, req)
|
|
138
|
+
|
|
139
|
+
handleResults(results, req, res, rootSpan, abortController)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function handleResults (actions, req, res, rootSpan, abortController) {
|
|
143
|
+
if (!actions || !req || !res || !rootSpan || !abortController) return
|
|
144
|
+
|
|
145
|
+
if (actions.includes('block')) {
|
|
146
|
+
block(req, res, rootSpan, abortController)
|
|
147
|
+
}
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
function disable () {
|
|
@@ -147,11 +152,14 @@ function disable () {
|
|
|
147
152
|
config = null
|
|
148
153
|
|
|
149
154
|
RuleManager.clearAllRules()
|
|
150
|
-
|
|
155
|
+
|
|
156
|
+
remoteConfig.disableWafUpdate()
|
|
151
157
|
|
|
152
158
|
// Channel#unsubscribe() is undefined for non active channels
|
|
153
159
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
|
|
154
160
|
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
|
|
161
|
+
if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed)
|
|
162
|
+
if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed)
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
module.exports = {
|