dd-trace 2.33.0 → 2.35.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/index.d.ts +8 -1
- package/package.json +5 -4
- package/packages/datadog-instrumentations/src/cucumber.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
- package/packages/datadog-instrumentations/src/http/client.js +2 -1
- package/packages/datadog-instrumentations/src/http/server.js +14 -0
- package/packages/datadog-instrumentations/src/http2/client.js +4 -0
- package/packages/datadog-instrumentations/src/jest.js +20 -17
- package/packages/datadog-instrumentations/src/next.js +6 -1
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/sequelize.js +51 -0
- 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-aws-sdk/src/base.js +3 -0
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -2
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +4 -3
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -1
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +150 -30
- package/packages/datadog-plugin-cypress/src/support.js +6 -3
- 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 +70 -67
- package/packages/datadog-plugin-http2/src/client.js +50 -46
- package/packages/datadog-plugin-jest/src/index.js +5 -4
- package/packages/datadog-plugin-jest/src/util.js +10 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -4
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -3
- package/packages/datadog-plugin-memcached/src/index.js +2 -3
- package/packages/datadog-plugin-mocha/src/index.js +4 -2
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-redis/src/index.js +2 -13
- 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/blocked_templates.js +2 -101
- package/packages/dd-trace/src/appsec/blocking.js +60 -11
- package/packages/dd-trace/src/appsec/channels.js +3 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +7 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/index.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/insecure-cookie-analyzer.js +31 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +26 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +47 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +65 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +26 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +35 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +16 -8
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +19 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +37 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +29 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +35 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +118 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +49 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +146 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +10 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -109
- package/packages/dd-trace/src/appsec/recommended.json +45 -46
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +3 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +4 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +49 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -7
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -6
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +10 -4
- package/packages/dd-trace/src/config.js +86 -9
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +11 -3
- package/packages/dd-trace/src/exporters/common/util.js +9 -0
- package/packages/dd-trace/src/exporters/common/writer.js +3 -2
- package/packages/dd-trace/src/git_metadata_tagger.js +17 -0
- package/packages/dd-trace/src/git_properties.js +32 -0
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/cache.js +7 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
- package/packages/dd-trace/src/plugins/client.js +3 -2
- package/packages/dd-trace/src/plugins/consumer.js +14 -2
- package/packages/dd-trace/src/plugins/database.js +2 -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/producer.js +19 -2
- package/packages/dd-trace/src/plugins/server.js +2 -2
- package/packages/dd-trace/src/plugins/storage.js +2 -0
- package/packages/dd-trace/src/plugins/tracing.js +11 -0
- package/packages/dd-trace/src/plugins/util/ci.js +63 -8
- package/packages/dd-trace/src/plugins/util/tags.js +5 -1
- package/packages/dd-trace/src/profiling/config.js +4 -2
- package/packages/dd-trace/src/profiling/constants.js +0 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/serverless.js +25 -0
- package/packages/dd-trace/src/service-naming/index.js +30 -0
- package/packages/dd-trace/src/service-naming/schemas/definition.js +24 -0
- package/packages/dd-trace/src/service-naming/schemas/index.js +6 -0
- package/packages/dd-trace/src/service-naming/schemas/util.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +64 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +33 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +52 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +21 -0
- package/packages/dd-trace/src/span_processor.js +3 -0
- package/packages/dd-trace/src/tracer.js +3 -2
- package/version.js +9 -0
- package/packages/dd-trace/src/plugins/incoming.js +0 -7
|
@@ -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
|
|
@@ -9,11 +9,10 @@ class MemcachedPlugin extends CachePlugin {
|
|
|
9
9
|
start ({ client, server, query }) {
|
|
10
10
|
const address = getAddress(client, server, query)
|
|
11
11
|
|
|
12
|
-
this.startSpan(
|
|
13
|
-
service: this.config.
|
|
12
|
+
this.startSpan({
|
|
13
|
+
service: this.serviceName(this.config, this.system),
|
|
14
14
|
resource: query.type,
|
|
15
15
|
type: 'memcached',
|
|
16
|
-
kind: 'client',
|
|
17
16
|
meta: {
|
|
18
17
|
'memcached.command': query.command,
|
|
19
18
|
'out.host': address[0],
|
|
@@ -36,9 +36,11 @@ class MochaPlugin extends CiPlugin {
|
|
|
36
36
|
const relativeCoverageFiles = [...coverageFiles, suiteFile]
|
|
37
37
|
.map(filename => getTestSuitePath(filename, this.sourceRoot))
|
|
38
38
|
|
|
39
|
+
const { _traceId, _spanId } = testSuiteSpan.context()
|
|
40
|
+
|
|
39
41
|
const formattedCoverage = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
sessionId: _traceId,
|
|
43
|
+
suiteId: _spanId,
|
|
42
44
|
files: relativeCoverageFiles
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -13,11 +13,10 @@ class RedisPlugin extends CachePlugin {
|
|
|
13
13
|
const normalizedCommand = command.toUpperCase()
|
|
14
14
|
if (!this.config.filter(normalizedCommand)) return this.skip()
|
|
15
15
|
|
|
16
|
-
this.startSpan(
|
|
17
|
-
service: getService(this.config, connectionName),
|
|
16
|
+
this.startSpan({
|
|
18
17
|
resource,
|
|
18
|
+
service: this.serviceName(this.config, this.system, connectionName),
|
|
19
19
|
type: 'redis',
|
|
20
|
-
kind: 'client',
|
|
21
20
|
meta: {
|
|
22
21
|
'db.type': 'redis',
|
|
23
22
|
'db.name': db || '0',
|
|
@@ -33,16 +32,6 @@ class RedisPlugin extends CachePlugin {
|
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
function getService (config, connectionName) {
|
|
37
|
-
if (config.splitByInstance && connectionName) {
|
|
38
|
-
return config.service
|
|
39
|
-
? `${config.service}-${connectionName}`
|
|
40
|
-
: connectionName
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return config.service
|
|
44
|
-
}
|
|
45
|
-
|
|
46
35
|
function formatCommand (command, args) {
|
|
47
36
|
if (!args || command === 'AUTH') return command
|
|
48
37
|
|
|
@@ -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,
|
|
@@ -1,108 +1,9 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
const html =
|
|
5
|
-
<!DOCTYPE html>
|
|
6
|
-
<html lang="en">
|
|
4
|
+
const html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You've been blocked</title><style>a,body,div,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}p{display:block}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}</style></head><body><main><p>Sorry, you cannot access this page. Please contact the customer service team.</p></main><footer><p>Security provided by <a href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/" target="_blank">Datadog</a></p></footer></body></html>`
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
<meta charset="UTF-8">
|
|
10
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
11
|
-
<title>You've been blocked</title>
|
|
12
|
-
<style>
|
|
13
|
-
a,
|
|
14
|
-
body,
|
|
15
|
-
div,
|
|
16
|
-
html,
|
|
17
|
-
span {
|
|
18
|
-
margin: 0;
|
|
19
|
-
padding: 0;
|
|
20
|
-
border: 0;
|
|
21
|
-
font-size: 100%;
|
|
22
|
-
font: inherit;
|
|
23
|
-
vertical-align: baseline
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
body {
|
|
27
|
-
background: -webkit-radial-gradient(26% 19%, circle, #fff, #f4f7f9);
|
|
28
|
-
background: radial-gradient(circle at 26% 19%, #fff, #f4f7f9);
|
|
29
|
-
display: -webkit-box;
|
|
30
|
-
display: -ms-flexbox;
|
|
31
|
-
display: flex;
|
|
32
|
-
-webkit-box-pack: center;
|
|
33
|
-
-ms-flex-pack: center;
|
|
34
|
-
justify-content: center;
|
|
35
|
-
-webkit-box-align: center;
|
|
36
|
-
-ms-flex-align: center;
|
|
37
|
-
align-items: center;
|
|
38
|
-
-ms-flex-line-pack: center;
|
|
39
|
-
align-content: center;
|
|
40
|
-
width: 100%;
|
|
41
|
-
min-height: 100vh;
|
|
42
|
-
line-height: 1;
|
|
43
|
-
flex-direction: column
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
p {
|
|
47
|
-
display: block
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
main {
|
|
52
|
-
text-align: center;
|
|
53
|
-
flex: 1;
|
|
54
|
-
display: -webkit-box;
|
|
55
|
-
display: -ms-flexbox;
|
|
56
|
-
display: flex;
|
|
57
|
-
-webkit-box-pack: center;
|
|
58
|
-
-ms-flex-pack: center;
|
|
59
|
-
justify-content: center;
|
|
60
|
-
-webkit-box-align: center;
|
|
61
|
-
-ms-flex-align: center;
|
|
62
|
-
align-items: center;
|
|
63
|
-
-ms-flex-line-pack: center;
|
|
64
|
-
align-content: center;
|
|
65
|
-
flex-direction: column
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
p {
|
|
69
|
-
font-size: 18px;
|
|
70
|
-
line-height: normal;
|
|
71
|
-
color: #646464;
|
|
72
|
-
font-family: sans-serif;
|
|
73
|
-
font-weight: 400
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
a {
|
|
77
|
-
color: #4842b7
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
footer {
|
|
81
|
-
width: 100%;
|
|
82
|
-
text-align: center
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
footer p {
|
|
86
|
-
font-size: 16px
|
|
87
|
-
}
|
|
88
|
-
</style>
|
|
89
|
-
</head>
|
|
90
|
-
|
|
91
|
-
<body>
|
|
92
|
-
<main>
|
|
93
|
-
<p>Sorry, you cannot access this page. Please contact the customer service team.</p>
|
|
94
|
-
</main>
|
|
95
|
-
<footer>
|
|
96
|
-
<p>Security provided by <a
|
|
97
|
-
href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/"
|
|
98
|
-
target="_blank">Datadog</a></p>
|
|
99
|
-
</footer>
|
|
100
|
-
</body>
|
|
101
|
-
|
|
102
|
-
</html>
|
|
103
|
-
`
|
|
104
|
-
|
|
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."}]}`
|
|
6
|
+
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."}]}`
|
|
106
7
|
|
|
107
8
|
module.exports = {
|
|
108
9
|
html,
|
|
@@ -5,32 +5,62 @@ const blockedTemplates = require('./blocked_templates')
|
|
|
5
5
|
|
|
6
6
|
let templateHtml = blockedTemplates.html
|
|
7
7
|
let templateJson = blockedTemplates.json
|
|
8
|
+
let blockingConfiguration
|
|
8
9
|
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
function blockWithRedirect (res, rootSpan, abortController) {
|
|
11
|
+
rootSpan.addTags({
|
|
12
|
+
'appsec.blocked': 'true'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
let statusCode = blockingConfiguration.parameters.status_code
|
|
16
|
+
if (!statusCode || statusCode < 300 || statusCode >= 400) {
|
|
17
|
+
statusCode = 303
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
res.writeHead(statusCode, {
|
|
21
|
+
'Location': blockingConfiguration.parameters.location
|
|
22
|
+
}).end()
|
|
23
|
+
|
|
24
|
+
if (abortController) {
|
|
25
|
+
abortController.abort()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function blockWithContent (req, res, rootSpan, abortController) {
|
|
15
30
|
let type
|
|
16
31
|
let body
|
|
17
32
|
|
|
18
33
|
// parse the Accept header, ex: Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
|
|
19
34
|
const accept = req.headers.accept && req.headers.accept.split(',').map((str) => str.split(';', 1)[0].trim())
|
|
20
35
|
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
if (!blockingConfiguration || blockingConfiguration.parameters.type === 'auto') {
|
|
37
|
+
if (accept && accept.includes('text/html') && !accept.includes('application/json')) {
|
|
38
|
+
type = 'text/html; charset=utf-8'
|
|
39
|
+
body = templateHtml
|
|
40
|
+
} else {
|
|
41
|
+
type = 'application/json'
|
|
42
|
+
body = templateJson
|
|
43
|
+
}
|
|
24
44
|
} else {
|
|
25
|
-
type
|
|
26
|
-
|
|
45
|
+
if (blockingConfiguration.parameters.type === 'html') {
|
|
46
|
+
type = 'text/html; charset=utf-8'
|
|
47
|
+
body = templateHtml
|
|
48
|
+
} else {
|
|
49
|
+
type = 'application/json'
|
|
50
|
+
body = templateJson
|
|
51
|
+
}
|
|
27
52
|
}
|
|
28
53
|
|
|
29
54
|
rootSpan.addTags({
|
|
30
55
|
'appsec.blocked': 'true'
|
|
31
56
|
})
|
|
32
57
|
|
|
33
|
-
|
|
58
|
+
if (blockingConfiguration && blockingConfiguration.type === 'block_request' &&
|
|
59
|
+
blockingConfiguration.parameters.status_code) {
|
|
60
|
+
res.statusCode = blockingConfiguration.parameters.status_code
|
|
61
|
+
} else {
|
|
62
|
+
res.statusCode = 403
|
|
63
|
+
}
|
|
34
64
|
res.setHeader('Content-Type', type)
|
|
35
65
|
res.setHeader('Content-Length', Buffer.byteLength(body))
|
|
36
66
|
res.end(body)
|
|
@@ -40,6 +70,20 @@ function block (req, res, rootSpan, abortController) {
|
|
|
40
70
|
}
|
|
41
71
|
}
|
|
42
72
|
|
|
73
|
+
function block (req, res, rootSpan, abortController) {
|
|
74
|
+
if (res.headersSent) {
|
|
75
|
+
log.warn('Cannot send blocking response when headers have already been sent')
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (blockingConfiguration && blockingConfiguration.type === 'redirect_request' &&
|
|
80
|
+
blockingConfiguration.parameters.location) {
|
|
81
|
+
blockWithRedirect(res, rootSpan, abortController)
|
|
82
|
+
} else {
|
|
83
|
+
blockWithContent(req, res, rootSpan, abortController)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
43
87
|
function setTemplates (config) {
|
|
44
88
|
if (config.appsec.blockedTemplateHtml) {
|
|
45
89
|
templateHtml = config.appsec.blockedTemplateHtml
|
|
@@ -49,7 +93,12 @@ function setTemplates (config) {
|
|
|
49
93
|
}
|
|
50
94
|
}
|
|
51
95
|
|
|
96
|
+
function updateBlockingConfiguration (newBlockingConfiguration) {
|
|
97
|
+
blockingConfiguration = newBlockingConfiguration
|
|
98
|
+
}
|
|
99
|
+
|
|
52
100
|
module.exports = {
|
|
53
101
|
block,
|
|
54
|
-
setTemplates
|
|
102
|
+
setTemplates,
|
|
103
|
+
updateBlockingConfiguration
|
|
55
104
|
}
|
|
@@ -4,8 +4,9 @@ const dc = require('../../../diagnostics_channel')
|
|
|
4
4
|
|
|
5
5
|
// TODO: use TBD naming convention
|
|
6
6
|
module.exports = {
|
|
7
|
+
bodyParser: dc.channel('datadog:body-parser:read:finish'),
|
|
7
8
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
8
9
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
queryParser: dc.channel('datadog:query:read:finish'),
|
|
11
|
+
setCookieChannel: dc.channel('datadog:iast:set-cookie')
|
|
11
12
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
-
'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
|
|
5
|
-
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
|
|
6
|
-
'SQL_INJECTION_ANALYZER': require('./sql-injection-analyzer'),
|
|
7
|
-
'PATH_TRAVERSAL_ANALYZER': require('./path-traversal-analyzer'),
|
|
8
4
|
'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
|
|
9
|
-
'
|
|
5
|
+
'INSECURE_COOKIE_ANALYZER': require('./insecure-cookie-analyzer'),
|
|
6
|
+
'LDAP_ANALYZER': require('./ldap-injection-analyzer'),
|
|
7
|
+
'PATH_TRAVERSAL_ANALYZER': require('./path-traversal-analyzer'),
|
|
8
|
+
'SQL_INJECTION_ANALYZER': require('./sql-injection-analyzer'),
|
|
9
|
+
'SSRF': require('./ssrf-analyzer'),
|
|
10
|
+
'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
|
|
11
|
+
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer')
|
|
10
12
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
const { COMMAND_INJECTION } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
6
|
constructor () {
|
|
6
|
-
super(
|
|
7
|
+
super(COMMAND_INJECTION)
|
|
7
8
|
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const analyzers = require('./analyzers')
|
|
4
|
+
const setCookiesHeaderInterceptor = require('./set-cookies-header-interceptor')
|
|
4
5
|
|
|
5
6
|
function enableAllAnalyzers () {
|
|
7
|
+
setCookiesHeaderInterceptor.configure(true)
|
|
6
8
|
for (const analyzer in analyzers) {
|
|
7
9
|
analyzers[analyzer].configure(true)
|
|
8
10
|
}
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
function disableAllAnalyzers () {
|
|
14
|
+
setCookiesHeaderInterceptor.configure(false)
|
|
12
15
|
for (const analyzer in analyzers) {
|
|
13
16
|
analyzers[analyzer].configure(false)
|
|
14
17
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Analyzer = require('./vulnerability-analyzer')
|
|
4
|
+
const { INSECURE_COOKIE } = require('../vulnerabilities')
|
|
5
|
+
|
|
6
|
+
const EXCLUDED_PATHS = ['node_modules/express/lib/response.js', 'node_modules\\express\\lib\\response.js']
|
|
7
|
+
|
|
8
|
+
class InsecureCookieAnalyzer extends Analyzer {
|
|
9
|
+
constructor () {
|
|
10
|
+
super(INSECURE_COOKIE)
|
|
11
|
+
this.addSub('datadog:iast:set-cookie', (cookieInfo) => this.analyze(cookieInfo))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_isVulnerable ({ cookieProperties, cookieValue }) {
|
|
15
|
+
return cookieValue && !(cookieProperties && cookieProperties.map(x => x.toLowerCase().trim()).includes('secure'))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_getEvidence ({ cookieName }) {
|
|
19
|
+
return { value: cookieName }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_createHashSource (type, evidence, location) {
|
|
23
|
+
return `${type}:${evidence.value}`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_getExcludedPaths () {
|
|
27
|
+
return EXCLUDED_PATHS
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = new InsecureCookieAnalyzer()
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
const { LDAP_INJECTION } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
class LdapInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
6
|
constructor () {
|
|
6
|
-
super(
|
|
7
|
+
super(LDAP_INJECTION)
|
|
7
8
|
this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -4,11 +4,16 @@ const path = require('path')
|
|
|
4
4
|
const { getIastContext } = require('../iast-context')
|
|
5
5
|
const { storage } = require('../../../../../datadog-core')
|
|
6
6
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
7
|
+
const { PATH_TRAVERSAL } = require('../vulnerabilities')
|
|
8
|
+
|
|
9
|
+
const ignoredOperations = ['dir.close', 'close']
|
|
7
10
|
|
|
8
11
|
class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
9
12
|
constructor () {
|
|
10
|
-
super(
|
|
13
|
+
super(PATH_TRAVERSAL)
|
|
11
14
|
this.addSub('apm:fs:operation:start', obj => {
|
|
15
|
+
if (ignoredOperations.includes(obj.operation)) return
|
|
16
|
+
|
|
12
17
|
const pathArguments = []
|
|
13
18
|
if (obj.dest) {
|
|
14
19
|
pathArguments.push(obj.dest)
|
|
@@ -40,13 +45,29 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
40
45
|
this.analyze(pathArguments)
|
|
41
46
|
})
|
|
42
47
|
|
|
43
|
-
this.exclusionList = [
|
|
48
|
+
this.exclusionList = [
|
|
49
|
+
path.join('node_modules', 'send') + path.sep
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
this.internalExclusionList = [
|
|
53
|
+
'node:fs',
|
|
54
|
+
'node:internal/fs',
|
|
55
|
+
'node:internal\\fs',
|
|
56
|
+
'fs.js',
|
|
57
|
+
'internal/fs',
|
|
58
|
+
'internal\\fs'
|
|
59
|
+
]
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
_isExcluded (location) {
|
|
47
|
-
let ret =
|
|
63
|
+
let ret = true
|
|
48
64
|
if (location && location.path) {
|
|
49
|
-
|
|
65
|
+
// Exclude from reporting those vulnerabilities which location is from an internal fs call
|
|
66
|
+
if (location.isInternal) {
|
|
67
|
+
ret = this.internalExclusionList.some(elem => location.path.includes(elem))
|
|
68
|
+
} else {
|
|
69
|
+
ret = this.exclusionList.some(elem => location.path.includes(elem))
|
|
70
|
+
}
|
|
50
71
|
}
|
|
51
72
|
return ret
|
|
52
73
|
}
|
|
@@ -59,7 +80,7 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
59
80
|
|
|
60
81
|
if (value && value.constructor === Array) {
|
|
61
82
|
for (const val of value) {
|
|
62
|
-
if (this._isVulnerable(val, iastContext)) {
|
|
83
|
+
if (this._isVulnerable(val, iastContext) && this._checkOCE(iastContext)) {
|
|
63
84
|
this._report(val, iastContext)
|
|
64
85
|
// no support several evidences in the same vulnerability, just report the 1st one
|
|
65
86
|
break
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Plugin = require('../../../plugins/plugin')
|
|
4
|
+
const { setCookieChannel } = require('../../channels')
|
|
5
|
+
|
|
6
|
+
class SetCookiesHeaderInterceptor extends Plugin {
|
|
7
|
+
constructor () {
|
|
8
|
+
super()
|
|
9
|
+
this.cookiesInRequest = new WeakMap()
|
|
10
|
+
this.addSub('datadog:http:server:response:set-header:finish', ({ name, value, res }) => {
|
|
11
|
+
if (name.toLowerCase() === 'set-cookie') {
|
|
12
|
+
let allCookies = value
|
|
13
|
+
if (typeof value === 'string') {
|
|
14
|
+
allCookies = [value]
|
|
15
|
+
}
|
|
16
|
+
const alreadyCheckedCookies = this._getAlreadyCheckedCookiesInResponse(res)
|
|
17
|
+
allCookies.forEach(cookieString => {
|
|
18
|
+
if (!alreadyCheckedCookies.includes(cookieString)) {
|
|
19
|
+
alreadyCheckedCookies.push(cookieString)
|
|
20
|
+
setCookieChannel.publish(this._parseCookie(cookieString))
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_parseCookie (cookieString) {
|
|
28
|
+
const cookieParts = cookieString.split(';')
|
|
29
|
+
const nameValueParts = cookieParts[0].split('=')
|
|
30
|
+
const cookieName = nameValueParts[0]
|
|
31
|
+
const cookieValue = nameValueParts.slice(1).join('=')
|
|
32
|
+
const cookieProperties = cookieParts.slice(1).map(part => part.trim())
|
|
33
|
+
|
|
34
|
+
return { cookieName, cookieValue, cookieProperties, cookieString }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_getAlreadyCheckedCookiesInResponse (res) {
|
|
38
|
+
let alreadyCheckedCookies = this.cookiesInRequest.get(res)
|
|
39
|
+
if (!alreadyCheckedCookies) {
|
|
40
|
+
alreadyCheckedCookies = []
|
|
41
|
+
this.cookiesInRequest.set(res, alreadyCheckedCookies)
|
|
42
|
+
}
|
|
43
|
+
return alreadyCheckedCookies
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = new SetCookiesHeaderInterceptor()
|
|
@@ -1,12 +1,73 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
|
|
2
3
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
4
|
+
const { SQL_INJECTION } = require('../vulnerabilities')
|
|
5
|
+
const { getRanges } = require('../taint-tracking/operations')
|
|
6
|
+
const { storage } = require('../../../../../datadog-core')
|
|
7
|
+
const { getIastContext } = require('../iast-context')
|
|
8
|
+
const { addVulnerability } = require('../vulnerability-reporter')
|
|
9
|
+
|
|
10
|
+
const EXCLUDED_PATHS = ['node_modules/mysql2', 'node_modules/sequelize', 'node_modules\\mysql2',
|
|
11
|
+
'node_modules\\sequelize']
|
|
3
12
|
|
|
4
13
|
class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
14
|
constructor () {
|
|
6
|
-
super(
|
|
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', ({ query }) => this.analyze(query.text))
|
|
15
|
+
super(SQL_INJECTION)
|
|
16
|
+
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
17
|
+
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
18
|
+
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, 'POSTGRES'))
|
|
19
|
+
|
|
20
|
+
this.addSub('datadog:sequelize:query:start', ({ sql, dialect }) => {
|
|
21
|
+
const parentStore = storage.getStore()
|
|
22
|
+
if (parentStore) {
|
|
23
|
+
this.analyze(sql, dialect.toUpperCase())
|
|
24
|
+
|
|
25
|
+
storage.enterWith({ ...parentStore, sqlAnalyzed: true, sequelizeParentStore: parentStore })
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
this.addSub('datadog:sequelize:query:finish', () => {
|
|
30
|
+
const store = storage.getStore()
|
|
31
|
+
if (store.sequelizeParentStore) {
|
|
32
|
+
storage.enterWith(store.sequelizeParentStore)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_getEvidence (value, iastContext, dialect) {
|
|
38
|
+
const ranges = getRanges(iastContext, value)
|
|
39
|
+
return { value, ranges, dialect }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
analyze (value, dialect) {
|
|
43
|
+
const store = storage.getStore()
|
|
44
|
+
|
|
45
|
+
if (!(store && store.sqlAnalyzed)) {
|
|
46
|
+
const iastContext = getIastContext(store)
|
|
47
|
+
if (store && !iastContext) return
|
|
48
|
+
this._reportIfVulnerable(value, iastContext, dialect)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_reportIfVulnerable (value, context, dialect) {
|
|
53
|
+
if (this._isVulnerable(value, context) && this._checkOCE(context)) {
|
|
54
|
+
this._report(value, context, dialect)
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_report (value, context, dialect) {
|
|
61
|
+
const evidence = this._getEvidence(value, context, dialect)
|
|
62
|
+
const location = this._getLocation()
|
|
63
|
+
if (!this._isExcluded(location)) {
|
|
64
|
+
const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
|
|
65
|
+
const vulnerability = this._createVulnerability(this._type, evidence, spanId, location)
|
|
66
|
+
addVulnerability(context, vulnerability)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
_getExcludedPaths () {
|
|
70
|
+
return EXCLUDED_PATHS
|
|
10
71
|
}
|
|
11
72
|
}
|
|
12
73
|
|