dd-trace 4.16.0 → 4.18.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 -0
- package/index.d.ts +9 -1
- package/package.json +5 -4
- package/packages/datadog-instrumentations/src/body-parser.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +29 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +45 -0
- package/packages/datadog-instrumentations/src/express.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/jest.js +58 -20
- package/packages/datadog-instrumentations/src/knex.js +69 -1
- package/packages/datadog-instrumentations/src/mocha.js +34 -4
- package/packages/datadog-instrumentations/src/mongodb.js +63 -0
- package/packages/datadog-instrumentations/src/mongoose.js +140 -1
- package/packages/datadog-instrumentations/src/next.js +98 -23
- package/packages/datadog-instrumentations/src/playwright.js +22 -8
- package/packages/datadog-plugin-cucumber/src/index.js +17 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +38 -8
- package/packages/datadog-plugin-http/src/client.js +2 -0
- package/packages/datadog-plugin-jest/src/index.js +29 -6
- package/packages/datadog-plugin-jest/src/util.js +45 -2
- package/packages/datadog-plugin-memcached/src/index.js +10 -5
- package/packages/datadog-plugin-mocha/src/index.js +25 -6
- package/packages/datadog-plugin-next/src/index.js +4 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +60 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +269 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +22 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +173 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +21 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-log.js +9 -4
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +6 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +25 -12
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +13 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks-generator.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +1 -14
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +22 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +9 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +15 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +169 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +5 -1
- package/packages/dd-trace/src/appsec/index.js +31 -13
- package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -3
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +14 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -0
- package/packages/dd-trace/src/config.js +37 -13
- package/packages/dd-trace/src/format.js +3 -0
- package/packages/dd-trace/src/git_properties.js +16 -15
- package/packages/dd-trace/src/plugin_manager.js +3 -1
- package/packages/dd-trace/src/plugins/util/ci.js +17 -0
- package/packages/dd-trace/src/plugins/util/git.js +26 -4
- package/packages/dd-trace/src/plugins/util/test.js +45 -2
- package/packages/dd-trace/src/profiling/config.js +20 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +51 -40
- package/packages/dd-trace/src/service-naming/extra-services.js +24 -0
- package/packages/dd-trace/src/telemetry/index.js +4 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +65 -0
- package/packages/dd-trace/src/{appsec/iast/telemetry/log → telemetry/logs}/log-collector.js +9 -22
- package/packages/dd-trace/src/telemetry/metrics.js +0 -5
- package/packages/dd-trace/src/appsec/iast/telemetry/log/index.js +0 -87
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
1
3
|
const sensitiveHandler = require('./evidence-redaction/sensitive-handler')
|
|
4
|
+
const { stringifyWithRanges } = require('./utils')
|
|
2
5
|
|
|
3
6
|
class VulnerabilityFormatter {
|
|
4
7
|
constructor () {
|
|
5
8
|
this._redactVulnearbilities = true
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
setRedactVulnerabilities (shouldRedactVulnerabilities) {
|
|
11
|
+
setRedactVulnerabilities (shouldRedactVulnerabilities, redactionNamePattern, redactionValuePattern) {
|
|
9
12
|
this._redactVulnearbilities = shouldRedactVulnerabilities
|
|
13
|
+
sensitiveHandler.setRedactionPatterns(redactionNamePattern, redactionValuePattern)
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
extractSourcesFromVulnerability (vulnerability) {
|
|
@@ -38,6 +42,13 @@ class VulnerabilityFormatter {
|
|
|
38
42
|
getUnredactedValueParts (evidence, sourcesIndexes) {
|
|
39
43
|
const valueParts = []
|
|
40
44
|
let fromIndex = 0
|
|
45
|
+
|
|
46
|
+
if (typeof evidence.value === 'object' && evidence.rangesToApply) {
|
|
47
|
+
const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply)
|
|
48
|
+
evidence.value = value
|
|
49
|
+
evidence.ranges = ranges
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
evidence.ranges.forEach((range, rangeIndex) => {
|
|
42
53
|
if (fromIndex < range.start) {
|
|
43
54
|
valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
|
|
@@ -45,14 +56,16 @@ class VulnerabilityFormatter {
|
|
|
45
56
|
valueParts.push({ value: evidence.value.substring(range.start, range.end), source: sourcesIndexes[rangeIndex] })
|
|
46
57
|
fromIndex = range.end
|
|
47
58
|
})
|
|
59
|
+
|
|
48
60
|
if (fromIndex < evidence.value.length) {
|
|
49
61
|
valueParts.push({ value: evidence.value.substring(fromIndex) })
|
|
50
62
|
}
|
|
63
|
+
|
|
51
64
|
return { valueParts }
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
formatEvidence (type, evidence, sourcesIndexes, sources) {
|
|
55
|
-
if (!evidence.ranges) {
|
|
68
|
+
if (!evidence.ranges && !evidence.rangesToApply) {
|
|
56
69
|
if (typeof evidence.value === 'undefined') {
|
|
57
70
|
return undefined
|
|
58
71
|
} else {
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto')
|
|
4
|
+
const { DEFAULT_IAST_REDACTION_VALUE_PATTERN } = require('./evidence-redaction/sensitive-regex')
|
|
5
|
+
|
|
6
|
+
const STRINGIFY_RANGE_KEY = 'DD_' + crypto.randomBytes(20).toString('hex')
|
|
7
|
+
const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
|
|
8
|
+
const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line max-len
|
|
11
|
+
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_\\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\\d+_(\\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\\d+_([\\s0-9.a-zA-Z]*)")`, 'gm')
|
|
12
|
+
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_\\d+_)`, 'gm')
|
|
13
|
+
|
|
14
|
+
const sensitiveValueRegex = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
15
|
+
|
|
16
|
+
function iterateObject (target, fn, levelKeys = [], depth = 50) {
|
|
17
|
+
Object.keys(target).forEach((key) => {
|
|
18
|
+
const nextLevelKeys = [...levelKeys, key]
|
|
19
|
+
const val = target[key]
|
|
20
|
+
|
|
21
|
+
fn(val, nextLevelKeys, target, key)
|
|
22
|
+
|
|
23
|
+
if (val !== null && typeof val === 'object') {
|
|
24
|
+
iterateObject(val, fn, nextLevelKeys, depth - 1)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
30
|
+
let value
|
|
31
|
+
const ranges = []
|
|
32
|
+
const sensitiveRanges = []
|
|
33
|
+
objRanges = objRanges || {}
|
|
34
|
+
|
|
35
|
+
if (objRanges || loadSensitiveRanges) {
|
|
36
|
+
const cloneObj = Array.isArray(obj) ? [] : {}
|
|
37
|
+
let counter = 0
|
|
38
|
+
const allRanges = {}
|
|
39
|
+
const sensitiveKeysMapping = {}
|
|
40
|
+
|
|
41
|
+
iterateObject(obj, (val, levelKeys, parent, key) => {
|
|
42
|
+
let currentLevelClone = cloneObj
|
|
43
|
+
for (let i = 0; i < levelKeys.length - 1; i++) {
|
|
44
|
+
let levelKey = levelKeys[i]
|
|
45
|
+
|
|
46
|
+
if (!currentLevelClone[levelKey]) {
|
|
47
|
+
const sensitiveKey = sensitiveKeysMapping[levelKey]
|
|
48
|
+
if (currentLevelClone[sensitiveKey]) {
|
|
49
|
+
levelKey = sensitiveKey
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
currentLevelClone = currentLevelClone[levelKey]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (loadSensitiveRanges) {
|
|
57
|
+
const sensitiveKey = sensitiveKeysMapping[key]
|
|
58
|
+
if (sensitiveKey) {
|
|
59
|
+
key = sensitiveKey
|
|
60
|
+
} else {
|
|
61
|
+
sensitiveValueRegex.lastIndex = 0
|
|
62
|
+
|
|
63
|
+
if (sensitiveValueRegex.test(key)) {
|
|
64
|
+
const current = counter++
|
|
65
|
+
const id = `${STRINGIFY_SENSITIVE_KEY}_${current}_${key.length}_`
|
|
66
|
+
key = `${id}${key}`
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof val === 'string') {
|
|
72
|
+
const ranges = objRanges[levelKeys.join('.')]
|
|
73
|
+
if (ranges) {
|
|
74
|
+
const current = counter++
|
|
75
|
+
const id = `${STRINGIFY_RANGE_KEY}_${current}_`
|
|
76
|
+
|
|
77
|
+
allRanges[id] = ranges
|
|
78
|
+
currentLevelClone[key] = `${id}${val}`
|
|
79
|
+
} else {
|
|
80
|
+
currentLevelClone[key] = val
|
|
81
|
+
}
|
|
82
|
+
if (loadSensitiveRanges) {
|
|
83
|
+
const current = counter++
|
|
84
|
+
const id = `${STRINGIFY_SENSITIVE_KEY}_${current}_${val.length}_`
|
|
85
|
+
|
|
86
|
+
currentLevelClone[key] = `${id}${currentLevelClone[key]}`
|
|
87
|
+
}
|
|
88
|
+
} else if (typeof val !== 'object' || val === null) {
|
|
89
|
+
if (loadSensitiveRanges) {
|
|
90
|
+
const current = counter++
|
|
91
|
+
const id = `${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_${current}_`
|
|
92
|
+
|
|
93
|
+
// this is special, in the final string we should modify "key_value_[null|false|true]..."
|
|
94
|
+
// by null|false|..... ignoring the beginning and ending quotes
|
|
95
|
+
currentLevelClone[key] = id + val
|
|
96
|
+
} else {
|
|
97
|
+
currentLevelClone[key] = val
|
|
98
|
+
}
|
|
99
|
+
} else if (Array.isArray(val)) {
|
|
100
|
+
currentLevelClone[key] = []
|
|
101
|
+
} else {
|
|
102
|
+
currentLevelClone[key] = {}
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
value = JSON.stringify(cloneObj, null, 2)
|
|
107
|
+
|
|
108
|
+
if (counter > 0) {
|
|
109
|
+
let keysRegex
|
|
110
|
+
if (loadSensitiveRanges) {
|
|
111
|
+
keysRegex = KEYS_REGEX_WITH_SENSITIVE_RANGES
|
|
112
|
+
} else {
|
|
113
|
+
keysRegex = KEYS_REGEX_WITHOUT_SENSITIVE_RANGES
|
|
114
|
+
}
|
|
115
|
+
keysRegex.lastIndex = 0
|
|
116
|
+
|
|
117
|
+
let regexRes = keysRegex.exec(value)
|
|
118
|
+
while (regexRes) {
|
|
119
|
+
const offset = regexRes.index + 1 // +1 to increase the " char
|
|
120
|
+
|
|
121
|
+
if (regexRes[1]) {
|
|
122
|
+
// is a range
|
|
123
|
+
const rangesId = regexRes[1]
|
|
124
|
+
value = value.replace(rangesId, '')
|
|
125
|
+
|
|
126
|
+
const updatedRanges = allRanges[rangesId].map(range => {
|
|
127
|
+
return {
|
|
128
|
+
...range,
|
|
129
|
+
start: range.start + offset,
|
|
130
|
+
end: range.end + offset
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
ranges.push(...updatedRanges)
|
|
135
|
+
} else if (regexRes[2]) {
|
|
136
|
+
// is a sensitive string literal
|
|
137
|
+
const sensitiveId = regexRes[2]
|
|
138
|
+
|
|
139
|
+
sensitiveRanges.push({
|
|
140
|
+
start: offset,
|
|
141
|
+
end: offset + parseInt(regexRes[3])
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
value = value.replace(sensitiveId, '')
|
|
145
|
+
} else if (regexRes[4]) {
|
|
146
|
+
// is a sensitive value (number, null, false, ...)
|
|
147
|
+
const sensitiveId = regexRes[4]
|
|
148
|
+
const originalValue = regexRes[5]
|
|
149
|
+
|
|
150
|
+
sensitiveRanges.push({
|
|
151
|
+
start: regexRes.index,
|
|
152
|
+
end: regexRes.index + originalValue.length
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
value = value.replace(sensitiveId, originalValue)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
keysRegex.lastIndex = 0
|
|
159
|
+
regexRes = keysRegex.exec(value)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
value = JSON.stringify(obj, null, 2)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { value, ranges, sensitiveRanges }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { stringifyWithRanges }
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
COMMAND_INJECTION: 'COMMAND_INJECTION',
|
|
3
|
+
HARDCODED_SECRET: 'HARDCODED_SECRET',
|
|
3
4
|
HSTS_HEADER_MISSING: 'HSTS_HEADER_MISSING',
|
|
4
5
|
INSECURE_COOKIE: 'INSECURE_COOKIE',
|
|
5
6
|
LDAP_INJECTION: 'LDAP_INJECTION',
|
|
6
7
|
NO_HTTPONLY_COOKIE: 'NO_HTTPONLY_COOKIE',
|
|
7
8
|
NO_SAMESITE_COOKIE: 'NO_SAMESITE_COOKIE',
|
|
9
|
+
NOSQL_MONGODB_INJECTION: 'NOSQL_MONGODB_INJECTION',
|
|
8
10
|
PATH_TRAVERSAL: 'PATH_TRAVERSAL',
|
|
9
11
|
SQL_INJECTION: 'SQL_INJECTION',
|
|
10
12
|
SSRF: 'SSRF',
|
|
@@ -95,7 +95,11 @@ function deduplicateVulnerabilities (vulnerabilities) {
|
|
|
95
95
|
|
|
96
96
|
function start (config, _tracer) {
|
|
97
97
|
deduplicationEnabled = config.iast.deduplicationEnabled
|
|
98
|
-
vulnerabilitiesFormatter.setRedactVulnerabilities(
|
|
98
|
+
vulnerabilitiesFormatter.setRedactVulnerabilities(
|
|
99
|
+
config.iast.redactionEnabled,
|
|
100
|
+
config.iast.redactionNamePattern,
|
|
101
|
+
config.iast.redactionValuePattern
|
|
102
|
+
)
|
|
99
103
|
if (deduplicationEnabled) {
|
|
100
104
|
startClearCacheTimer()
|
|
101
105
|
}
|
|
@@ -10,7 +10,9 @@ const {
|
|
|
10
10
|
incomingHttpRequestStart,
|
|
11
11
|
incomingHttpRequestEnd,
|
|
12
12
|
passportVerify,
|
|
13
|
-
queryParser
|
|
13
|
+
queryParser,
|
|
14
|
+
nextBodyParsed,
|
|
15
|
+
nextQueryParsed
|
|
14
16
|
} = require('./channels')
|
|
15
17
|
const waf = require('./waf')
|
|
16
18
|
const addresses = require('./addresses')
|
|
@@ -30,6 +32,8 @@ function enable (_config) {
|
|
|
30
32
|
if (isEnabled) return
|
|
31
33
|
|
|
32
34
|
try {
|
|
35
|
+
appsecTelemetry.enable(_config.telemetry)
|
|
36
|
+
|
|
33
37
|
setTemplates(_config)
|
|
34
38
|
|
|
35
39
|
RuleManager.applyRules(_config.appsec.rules, _config.appsec)
|
|
@@ -38,11 +42,11 @@ function enable (_config) {
|
|
|
38
42
|
|
|
39
43
|
Reporter.setRateLimit(_config.appsec.rateLimit)
|
|
40
44
|
|
|
41
|
-
appsecTelemetry.enable(_config.telemetry)
|
|
42
|
-
|
|
43
45
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
44
46
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
45
47
|
bodyParser.subscribe(onRequestBodyParsed)
|
|
48
|
+
nextBodyParsed.subscribe(onRequestBodyParsed)
|
|
49
|
+
nextQueryParsed.subscribe(onRequestQueryParsed)
|
|
46
50
|
queryParser.subscribe(onRequestQueryParsed)
|
|
47
51
|
cookieParser.subscribe(onRequestCookieParser)
|
|
48
52
|
graphqlFinishExecute.subscribe(onGraphqlFinishExecute)
|
|
@@ -117,6 +121,10 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
117
121
|
payload[addresses.HTTP_INCOMING_COOKIES] = req.cookies
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
if (req.query && typeof req.query === 'object') {
|
|
125
|
+
payload[addresses.HTTP_INCOMING_QUERY] = req.query
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
waf.run(payload, req)
|
|
121
129
|
|
|
122
130
|
waf.disposeContext(req)
|
|
@@ -124,38 +132,48 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
124
132
|
Reporter.finishRequest(req, res)
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
function onRequestBodyParsed ({ req, res, abortController }) {
|
|
135
|
+
function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
136
|
+
if (body === undefined || body === null) return
|
|
137
|
+
|
|
138
|
+
if (!req) {
|
|
139
|
+
const store = storage.getStore()
|
|
140
|
+
req = store?.req
|
|
141
|
+
}
|
|
142
|
+
|
|
128
143
|
const rootSpan = web.root(req)
|
|
129
144
|
if (!rootSpan) return
|
|
130
145
|
|
|
131
|
-
if (req.body === undefined || req.body === null) return
|
|
132
|
-
|
|
133
146
|
const results = waf.run({
|
|
134
|
-
[addresses.HTTP_INCOMING_BODY]:
|
|
147
|
+
[addresses.HTTP_INCOMING_BODY]: body
|
|
135
148
|
}, req)
|
|
136
149
|
|
|
137
150
|
handleResults(results, req, res, rootSpan, abortController)
|
|
138
151
|
}
|
|
139
152
|
|
|
140
|
-
function onRequestQueryParsed ({ req, res, abortController }) {
|
|
153
|
+
function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
154
|
+
if (!query || typeof query !== 'object') return
|
|
155
|
+
|
|
156
|
+
if (!req) {
|
|
157
|
+
const store = storage.getStore()
|
|
158
|
+
req = store?.req
|
|
159
|
+
}
|
|
160
|
+
|
|
141
161
|
const rootSpan = web.root(req)
|
|
142
162
|
if (!rootSpan) return
|
|
143
163
|
|
|
144
|
-
if (!req.query || typeof req.query !== 'object') return
|
|
145
|
-
|
|
146
164
|
const results = waf.run({
|
|
147
|
-
[addresses.HTTP_INCOMING_QUERY]:
|
|
165
|
+
[addresses.HTTP_INCOMING_QUERY]: query
|
|
148
166
|
}, req)
|
|
149
167
|
|
|
150
168
|
handleResults(results, req, res, rootSpan, abortController)
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
function onRequestCookieParser ({ req, res, abortController, cookies }) {
|
|
172
|
+
if (!cookies || typeof cookies !== 'object') return
|
|
173
|
+
|
|
154
174
|
const rootSpan = web.root(req)
|
|
155
175
|
if (!rootSpan) return
|
|
156
176
|
|
|
157
|
-
if (!cookies || typeof cookies !== 'object') return
|
|
158
|
-
|
|
159
177
|
const results = waf.run({
|
|
160
178
|
[addresses.HTTP_INCOMING_COOKIES]: cookies
|
|
161
179
|
}, req)
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
const { URL, format } = require('url')
|
|
4
4
|
const uuid = require('crypto-randomuuid')
|
|
5
5
|
const { EventEmitter } = require('events')
|
|
6
|
-
const Scheduler = require('./scheduler')
|
|
7
6
|
const tracerVersion = require('../../../../../package.json').version
|
|
8
7
|
const request = require('../../exporters/common/request')
|
|
9
8
|
const log = require('../../log')
|
|
9
|
+
const { getExtraServices } = require('../../service-naming/extra-services')
|
|
10
10
|
const { UNACKNOWLEDGED, ACKNOWLEDGED, ERROR } = require('./apply_states')
|
|
11
|
+
const Scheduler = require('./scheduler')
|
|
11
12
|
|
|
12
13
|
const clientId = uuid()
|
|
13
14
|
|
|
@@ -57,7 +58,8 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
57
58
|
tracer_version: tracerVersion,
|
|
58
59
|
service: config.service,
|
|
59
60
|
env: config.env,
|
|
60
|
-
app_version: config.version
|
|
61
|
+
app_version: config.version,
|
|
62
|
+
extra_services: []
|
|
61
63
|
},
|
|
62
64
|
capabilities: DEFAULT_CAPABILITY // updated by `updateCapabilities()`
|
|
63
65
|
},
|
|
@@ -113,8 +115,14 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
113
115
|
this.state.client.products = this.eventNames().filter(e => typeof e === 'string')
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
getPayload () {
|
|
119
|
+
this.state.client.client_tracer.extra_services = getExtraServices()
|
|
120
|
+
|
|
121
|
+
return JSON.stringify(this.state)
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
poll (cb) {
|
|
117
|
-
request(
|
|
125
|
+
request(this.getPayload(), this.requestOptions, (err, data, statusCode) => {
|
|
118
126
|
// 404 means RC is disabled, ignore it
|
|
119
127
|
if (statusCode === 404) return cb()
|
|
120
128
|
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const Reporter = require('../reporter')
|
|
5
|
+
const addresses = require('../addresses')
|
|
6
|
+
|
|
7
|
+
// TODO: remove once ephemeral addresses are implemented
|
|
8
|
+
const preventDuplicateAddresses = new Set([
|
|
9
|
+
addresses.HTTP_INCOMING_QUERY
|
|
10
|
+
])
|
|
5
11
|
|
|
6
12
|
class WAFContextWrapper {
|
|
7
13
|
constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
|
|
@@ -10,16 +16,21 @@ class WAFContextWrapper {
|
|
|
10
16
|
this.wafTimeout = wafTimeout
|
|
11
17
|
this.wafVersion = wafVersion
|
|
12
18
|
this.rulesVersion = rulesVersion
|
|
19
|
+
this.addressesToSkip = new Set()
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
run (params) {
|
|
16
23
|
const inputs = {}
|
|
17
24
|
let someInputAdded = false
|
|
25
|
+
const newAddressesToSkip = new Set(this.addressesToSkip)
|
|
18
26
|
|
|
19
27
|
// TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
|
|
20
28
|
for (const key of Object.keys(params)) {
|
|
21
|
-
if (this.requiredAddresses.has(key)) {
|
|
29
|
+
if (this.requiredAddresses.has(key) && !this.addressesToSkip.has(key)) {
|
|
22
30
|
inputs[key] = params[key]
|
|
31
|
+
if (preventDuplicateAddresses.has(key)) {
|
|
32
|
+
newAddressesToSkip.add(key)
|
|
33
|
+
}
|
|
23
34
|
someInputAdded = true
|
|
24
35
|
}
|
|
25
36
|
}
|
|
@@ -33,6 +44,8 @@ class WAFContextWrapper {
|
|
|
33
44
|
|
|
34
45
|
const end = process.hrtime.bigint()
|
|
35
46
|
|
|
47
|
+
this.addressesToSkip = newAddressesToSkip
|
|
48
|
+
|
|
36
49
|
const ruleTriggered = !!result.events?.length
|
|
37
50
|
const blockTriggered = result.actions?.includes('block')
|
|
38
51
|
|
package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const request = require('../../exporters/common/request')
|
|
2
2
|
const id = require('../../id')
|
|
3
|
+
const log = require('../../log')
|
|
3
4
|
|
|
4
5
|
function getItrConfiguration ({
|
|
5
6
|
url,
|
|
@@ -72,8 +73,9 @@ function getItrConfiguration ({
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
} = JSON.parse(res)
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const config = { isCodeCoverageEnabled, isSuitesSkippingEnabled }
|
|
77
|
+
log.debug(() => `Received settings: ${config}`)
|
|
78
|
+
done(null, config)
|
|
77
79
|
} catch (err) {
|
|
78
80
|
done(err)
|
|
79
81
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const request = require('../../exporters/common/request')
|
|
2
|
+
const log = require('../../log')
|
|
2
3
|
|
|
3
4
|
function getSkippableSuites ({
|
|
4
5
|
url,
|
|
@@ -73,6 +74,7 @@ function getSkippableSuites ({
|
|
|
73
74
|
}
|
|
74
75
|
return { suite, name }
|
|
75
76
|
})
|
|
77
|
+
log.debug(() => `Number of received skippable ${testLevel}s: ${skippableSuites.length}`)
|
|
76
78
|
done(null, skippableSuites)
|
|
77
79
|
} catch (err) {
|
|
78
80
|
done(err)
|
|
@@ -10,7 +10,7 @@ const coalesce = require('koalas')
|
|
|
10
10
|
const tagger = require('./tagger')
|
|
11
11
|
const { isTrue, isFalse } = require('./util')
|
|
12
12
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
|
|
13
|
-
const { getGitMetadataFromGitProperties } = require('./git_properties')
|
|
13
|
+
const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./git_properties')
|
|
14
14
|
const { updateConfig } = require('./telemetry')
|
|
15
15
|
const { getIsGCPFunction, getIsAzureFunctionConsumptionPlan } = require('./serverless')
|
|
16
16
|
|
|
@@ -179,6 +179,11 @@ class Config {
|
|
|
179
179
|
false
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
+
const DD_TRACE_MEMCACHED_COMMAND_ENABLED = coalesce(
|
|
183
|
+
process.env.DD_TRACE_MEMCACHED_COMMAND_ENABLED,
|
|
184
|
+
false
|
|
185
|
+
)
|
|
186
|
+
|
|
182
187
|
const DD_SERVICE = options.service ||
|
|
183
188
|
process.env.DD_SERVICE ||
|
|
184
189
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -246,7 +251,7 @@ class Config {
|
|
|
246
251
|
)
|
|
247
252
|
const DD_TELEMETRY_METRICS_ENABLED = coalesce(
|
|
248
253
|
process.env.DD_TELEMETRY_METRICS_ENABLED,
|
|
249
|
-
|
|
254
|
+
true
|
|
250
255
|
)
|
|
251
256
|
const DD_TRACE_AGENT_PROTOCOL_VERSION = coalesce(
|
|
252
257
|
options.protocolVersion,
|
|
@@ -442,7 +447,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
442
447
|
5 // seconds
|
|
443
448
|
)
|
|
444
449
|
|
|
445
|
-
const iastOptions = options
|
|
450
|
+
const iastOptions = options?.experimental?.iast
|
|
446
451
|
const DD_IAST_ENABLED = coalesce(
|
|
447
452
|
iastOptions &&
|
|
448
453
|
(iastOptions === true || iastOptions.enabled === true),
|
|
@@ -456,7 +461,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
456
461
|
|
|
457
462
|
const defaultIastRequestSampling = 30
|
|
458
463
|
const iastRequestSampling = coalesce(
|
|
459
|
-
parseInt(iastOptions
|
|
464
|
+
parseInt(iastOptions?.requestSampling),
|
|
460
465
|
parseInt(process.env.DD_IAST_REQUEST_SAMPLING),
|
|
461
466
|
defaultIastRequestSampling
|
|
462
467
|
)
|
|
@@ -464,31 +469,43 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
464
469
|
iastRequestSampling > 100 ? defaultIastRequestSampling : iastRequestSampling
|
|
465
470
|
|
|
466
471
|
const DD_IAST_MAX_CONCURRENT_REQUESTS = coalesce(
|
|
467
|
-
parseInt(iastOptions
|
|
472
|
+
parseInt(iastOptions?.maxConcurrentRequests),
|
|
468
473
|
parseInt(process.env.DD_IAST_MAX_CONCURRENT_REQUESTS),
|
|
469
474
|
2
|
|
470
475
|
)
|
|
471
476
|
|
|
472
477
|
const DD_IAST_MAX_CONTEXT_OPERATIONS = coalesce(
|
|
473
|
-
parseInt(iastOptions
|
|
478
|
+
parseInt(iastOptions?.maxContextOperations),
|
|
474
479
|
parseInt(process.env.DD_IAST_MAX_CONTEXT_OPERATIONS),
|
|
475
480
|
2
|
|
476
481
|
)
|
|
477
482
|
|
|
478
483
|
const DD_IAST_DEDUPLICATION_ENABLED = coalesce(
|
|
479
|
-
iastOptions
|
|
484
|
+
iastOptions?.deduplicationEnabled,
|
|
480
485
|
process.env.DD_IAST_DEDUPLICATION_ENABLED && isTrue(process.env.DD_IAST_DEDUPLICATION_ENABLED),
|
|
481
486
|
true
|
|
482
487
|
)
|
|
483
488
|
|
|
484
489
|
const DD_IAST_REDACTION_ENABLED = coalesce(
|
|
485
|
-
iastOptions
|
|
490
|
+
iastOptions?.redactionEnabled,
|
|
486
491
|
!isFalse(process.env.DD_IAST_REDACTION_ENABLED),
|
|
487
492
|
true
|
|
488
493
|
)
|
|
489
494
|
|
|
495
|
+
const DD_IAST_REDACTION_NAME_PATTERN = coalesce(
|
|
496
|
+
iastOptions?.redactionNamePattern,
|
|
497
|
+
process.env.DD_IAST_REDACTION_NAME_PATTERN,
|
|
498
|
+
null
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
const DD_IAST_REDACTION_VALUE_PATTERN = coalesce(
|
|
502
|
+
iastOptions?.redactionValuePattern,
|
|
503
|
+
process.env.DD_IAST_REDACTION_VALUE_PATTERN,
|
|
504
|
+
null
|
|
505
|
+
)
|
|
506
|
+
|
|
490
507
|
const DD_IAST_TELEMETRY_VERBOSITY = coalesce(
|
|
491
|
-
iastOptions
|
|
508
|
+
iastOptions?.telemetryVerbosity,
|
|
492
509
|
process.env.DD_IAST_TELEMETRY_VERBOSITY,
|
|
493
510
|
'INFORMATION'
|
|
494
511
|
)
|
|
@@ -583,8 +600,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
583
600
|
this.telemetry = {
|
|
584
601
|
enabled: DD_TRACE_EXPORTER !== 'datadog' && isTrue(DD_TRACE_TELEMETRY_ENABLED),
|
|
585
602
|
heartbeatInterval: DD_TELEMETRY_HEARTBEAT_INTERVAL,
|
|
586
|
-
logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
|
|
587
603
|
debug: isTrue(DD_TELEMETRY_DEBUG),
|
|
604
|
+
logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
|
|
588
605
|
metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED)
|
|
589
606
|
}
|
|
590
607
|
this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
|
|
@@ -615,6 +632,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
615
632
|
maxContextOperations: DD_IAST_MAX_CONTEXT_OPERATIONS,
|
|
616
633
|
deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED,
|
|
617
634
|
redactionEnabled: DD_IAST_REDACTION_ENABLED,
|
|
635
|
+
redactionNamePattern: DD_IAST_REDACTION_NAME_PATTERN,
|
|
636
|
+
redactionValuePattern: DD_IAST_REDACTION_VALUE_PATTERN,
|
|
618
637
|
telemetryVerbosity: DD_IAST_TELEMETRY_VERBOSITY
|
|
619
638
|
}
|
|
620
639
|
|
|
@@ -629,10 +648,15 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
629
648
|
|
|
630
649
|
this.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
|
|
631
650
|
|
|
651
|
+
// Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
|
|
652
|
+
this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED)
|
|
653
|
+
|
|
632
654
|
if (this.gitMetadataEnabled) {
|
|
633
|
-
this.repositoryUrl =
|
|
634
|
-
|
|
635
|
-
|
|
655
|
+
this.repositoryUrl = removeUserSensitiveInfo(
|
|
656
|
+
coalesce(
|
|
657
|
+
process.env.DD_GIT_REPOSITORY_URL,
|
|
658
|
+
this.tags[GIT_REPOSITORY_URL]
|
|
659
|
+
)
|
|
636
660
|
)
|
|
637
661
|
this.commitSHA = coalesce(
|
|
638
662
|
process.env.DD_GIT_COMMIT_SHA,
|
|
@@ -4,6 +4,7 @@ const constants = require('./constants')
|
|
|
4
4
|
const tags = require('../../../ext/tags')
|
|
5
5
|
const id = require('./id')
|
|
6
6
|
const { isError } = require('./util')
|
|
7
|
+
const { registerExtraService } = require('./service-naming/extra-services')
|
|
7
8
|
|
|
8
9
|
const SAMPLING_PRIORITY_KEY = constants.SAMPLING_PRIORITY_KEY
|
|
9
10
|
const SAMPLING_RULE_DECISION = constants.SAMPLING_RULE_DECISION
|
|
@@ -76,6 +77,8 @@ function extractTags (trace, span) {
|
|
|
76
77
|
const tracerService = span.tracer()._service.toLowerCase()
|
|
77
78
|
if (tags['service.name']?.toLowerCase() !== tracerService) {
|
|
78
79
|
span.setTag(BASE_SERVICE, tracerService)
|
|
80
|
+
|
|
81
|
+
registerExtraService(tags['service.name'])
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
for (const tag in tags) {
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
const commitSHARegex = /git\.commit\.sha=([a-f\d]{40})/
|
|
2
2
|
const repositoryUrlRegex = /git\.repository_url=([\w\d:@/.-]+)/
|
|
3
3
|
|
|
4
|
+
function removeUserSensitiveInfo (repositoryUrl) {
|
|
5
|
+
try {
|
|
6
|
+
// repository URLs can contain username and password, so we want to filter those out
|
|
7
|
+
const parsedUrl = new URL(repositoryUrl)
|
|
8
|
+
if (parsedUrl.username || parsedUrl.password) {
|
|
9
|
+
return `${parsedUrl.origin}${parsedUrl.pathname}`
|
|
10
|
+
}
|
|
11
|
+
return repositoryUrl
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// if protocol isn't https, no password will be used
|
|
14
|
+
return repositoryUrl
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
function getGitMetadataFromGitProperties (gitPropertiesString) {
|
|
5
19
|
if (!gitPropertiesString) {
|
|
6
20
|
return {}
|
|
@@ -9,24 +23,11 @@ function getGitMetadataFromGitProperties (gitPropertiesString) {
|
|
|
9
23
|
const repositoryUrlMatch = gitPropertiesString.match(repositoryUrlRegex)
|
|
10
24
|
|
|
11
25
|
const repositoryUrl = repositoryUrlMatch ? repositoryUrlMatch[1] : undefined
|
|
12
|
-
let parsedUrl = repositoryUrl
|
|
13
|
-
|
|
14
|
-
if (repositoryUrl) {
|
|
15
|
-
try {
|
|
16
|
-
// repository URLs can contain username and password, so we want to filter those out
|
|
17
|
-
parsedUrl = new URL(repositoryUrl)
|
|
18
|
-
if (parsedUrl.password) {
|
|
19
|
-
parsedUrl = `${parsedUrl.origin}${parsedUrl.pathname}`
|
|
20
|
-
}
|
|
21
|
-
} catch (e) {
|
|
22
|
-
// if protocol isn't https, no password will be used
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
26
|
|
|
26
27
|
return {
|
|
27
28
|
commitSHA: commitSHAMatch ? commitSHAMatch[1] : undefined,
|
|
28
|
-
repositoryUrl:
|
|
29
|
+
repositoryUrl: removeUserSensitiveInfo(repositoryUrl)
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
module.exports = { getGitMetadataFromGitProperties }
|
|
33
|
+
module.exports = { getGitMetadataFromGitProperties, removeUserSensitiveInfo }
|
|
@@ -136,7 +136,8 @@ module.exports = class PluginManager {
|
|
|
136
136
|
headerTags,
|
|
137
137
|
dbmPropagationMode,
|
|
138
138
|
dsmEnabled,
|
|
139
|
-
clientIpEnabled
|
|
139
|
+
clientIpEnabled,
|
|
140
|
+
memcachedCommandEnabled
|
|
140
141
|
} = this._tracerConfig
|
|
141
142
|
|
|
142
143
|
const sharedConfig = {}
|
|
@@ -151,6 +152,7 @@ module.exports = class PluginManager {
|
|
|
151
152
|
|
|
152
153
|
sharedConfig.dbmPropagationMode = dbmPropagationMode
|
|
153
154
|
sharedConfig.dsmEnabled = dsmEnabled
|
|
155
|
+
sharedConfig.memcachedCommandEnabled = memcachedCommandEnabled
|
|
154
156
|
|
|
155
157
|
if (serviceMapping && serviceMapping[name]) {
|
|
156
158
|
sharedConfig.service = serviceMapping[name]
|