dd-trace 3.20.0 → 3.21.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/grpc/client.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/server.js +8 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
- package/packages/datadog-instrumentations/src/jest.js +20 -17
- package/packages/datadog-instrumentations/src/next.js +6 -1
- 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-http/src/client.js +2 -1
- package/packages/datadog-plugin-http2/src/client.js +2 -1
- package/packages/datadog-plugin-jest/src/util.js +10 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
- 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 +22 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +40 -4
- 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 +2 -1
- 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 +95 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +144 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +8 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -76
- package/packages/dd-trace/src/config.js +58 -8
- package/packages/dd-trace/src/constants.js +3 -1
- 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/plugins/util/ci.js +62 -7
- package/packages/dd-trace/src/plugins/util/tags.js +5 -1
- 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/span_processor.js +3 -0
- package/packages/dd-trace/src/tracer.js +3 -2
- package/version.js +9 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const iastLog = require('../../../iast-log')
|
|
4
|
+
|
|
5
|
+
const COMMAND_PATTERN = '^(?:\\s*(?:sudo|doas)\\s+)?\\b\\S+\\b\\s(.*)'
|
|
6
|
+
|
|
7
|
+
class CommandSensitiveAnalyzer {
|
|
8
|
+
constructor () {
|
|
9
|
+
this._pattern = new RegExp(COMMAND_PATTERN, 'gmi')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extractSensitiveRanges (evidence) {
|
|
13
|
+
try {
|
|
14
|
+
this._pattern.lastIndex = 0
|
|
15
|
+
|
|
16
|
+
const regexResult = this._pattern.exec(evidence.value)
|
|
17
|
+
if (regexResult && regexResult.length > 1) {
|
|
18
|
+
const start = regexResult.index + (regexResult[0].length - regexResult[1].length)
|
|
19
|
+
const end = start + regexResult[1].length
|
|
20
|
+
return [{ start, end }]
|
|
21
|
+
}
|
|
22
|
+
} catch (e) {
|
|
23
|
+
iastLog.debug(e)
|
|
24
|
+
}
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = CommandSensitiveAnalyzer
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const iastLog = require('../../../iast-log')
|
|
4
|
+
|
|
5
|
+
const LDAP_PATTERN = '\\(.*?(?:~=|=|<=|>=)(?<LITERAL>[^)]+)\\)'
|
|
6
|
+
|
|
7
|
+
class LdapSensitiveAnalyzer {
|
|
8
|
+
constructor () {
|
|
9
|
+
this._pattern = new RegExp(LDAP_PATTERN, 'gmi')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extractSensitiveRanges (evidence) {
|
|
13
|
+
try {
|
|
14
|
+
this._pattern.lastIndex = 0
|
|
15
|
+
const tokens = []
|
|
16
|
+
|
|
17
|
+
let regexResult = this._pattern.exec(evidence.value)
|
|
18
|
+
while (regexResult != null) {
|
|
19
|
+
if (!regexResult.groups.LITERAL) continue
|
|
20
|
+
// Computing indices manually since NodeJs 12 does not support d flag on regular expressions
|
|
21
|
+
// TODO Get indices from group by adding d flag in regular expression
|
|
22
|
+
const start = regexResult.index + (regexResult[0].length - regexResult.groups.LITERAL.length - 1)
|
|
23
|
+
const end = start + regexResult.groups.LITERAL.length
|
|
24
|
+
tokens.push({ start, end })
|
|
25
|
+
regexResult = this._pattern.exec(evidence.value)
|
|
26
|
+
}
|
|
27
|
+
return tokens
|
|
28
|
+
} catch (e) {
|
|
29
|
+
iastLog.debug(e)
|
|
30
|
+
}
|
|
31
|
+
return []
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = LdapSensitiveAnalyzer
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const iastLog = require('../../../iast-log')
|
|
4
|
+
|
|
5
|
+
const STRING_LITERAL = '\'(?:\'\'|[^\'])*\''
|
|
6
|
+
const POSTGRESQL_ESCAPED_LITERAL = '\\$([^$]*)\\$.*?\\$\\1\\$'
|
|
7
|
+
const MYSQL_STRING_LITERAL = '"(?:\\\\"|[^"])*"|\'(?:\\\\\'|[^\'])*\''
|
|
8
|
+
const LINE_COMMENT = '--.*$'
|
|
9
|
+
const BLOCK_COMMENT = '/\\*[\\s\\S]*\\*/'
|
|
10
|
+
const EXPONENT = '(?:E[-+]?\\d+[fd]?)?'
|
|
11
|
+
const INTEGER_NUMBER = '(?<!\\w)\\d+'
|
|
12
|
+
const DECIMAL_NUMBER = '\\d*\\.\\d+'
|
|
13
|
+
const HEX_NUMBER = 'x\'[0-9a-f]+\'|0x[0-9a-f]+'
|
|
14
|
+
const BIN_NUMBER = 'b\'[0-9a-f]+\'|0b[0-9a-f]+'
|
|
15
|
+
const NUMERIC_LITERAL =
|
|
16
|
+
`[-+]?(?:${
|
|
17
|
+
[
|
|
18
|
+
HEX_NUMBER,
|
|
19
|
+
BIN_NUMBER,
|
|
20
|
+
DECIMAL_NUMBER + EXPONENT,
|
|
21
|
+
INTEGER_NUMBER + EXPONENT
|
|
22
|
+
].join('|')
|
|
23
|
+
})`
|
|
24
|
+
|
|
25
|
+
class SqlSensitiveAnalyzer {
|
|
26
|
+
constructor () {
|
|
27
|
+
this._patterns = {
|
|
28
|
+
MYSQL: new RegExp(
|
|
29
|
+
[
|
|
30
|
+
NUMERIC_LITERAL,
|
|
31
|
+
MYSQL_STRING_LITERAL,
|
|
32
|
+
LINE_COMMENT,
|
|
33
|
+
BLOCK_COMMENT
|
|
34
|
+
].join('|'),
|
|
35
|
+
'gmi'
|
|
36
|
+
),
|
|
37
|
+
POSTGRES: new RegExp(
|
|
38
|
+
[
|
|
39
|
+
NUMERIC_LITERAL,
|
|
40
|
+
POSTGRESQL_ESCAPED_LITERAL,
|
|
41
|
+
STRING_LITERAL,
|
|
42
|
+
LINE_COMMENT,
|
|
43
|
+
BLOCK_COMMENT
|
|
44
|
+
].join('|'),
|
|
45
|
+
'gmi'
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
extractSensitiveRanges (evidence) {
|
|
51
|
+
try {
|
|
52
|
+
const pattern = this._patterns[evidence.dialect]
|
|
53
|
+
pattern.lastIndex = 0
|
|
54
|
+
const tokens = []
|
|
55
|
+
|
|
56
|
+
let regexResult = pattern.exec(evidence.value)
|
|
57
|
+
while (regexResult != null) {
|
|
58
|
+
let start = regexResult.index
|
|
59
|
+
let end = regexResult.index + regexResult[0].length
|
|
60
|
+
const startChar = evidence.value.charAt(start)
|
|
61
|
+
if (startChar === '\'' || startChar === '"') {
|
|
62
|
+
start++
|
|
63
|
+
end--
|
|
64
|
+
} else if (end > start + 1) {
|
|
65
|
+
const nextChar = evidence.value.charAt(start + 1)
|
|
66
|
+
if (startChar === '/' && nextChar === '*') {
|
|
67
|
+
start += 2
|
|
68
|
+
end -= 2
|
|
69
|
+
} else if (startChar === '-' && startChar === nextChar) {
|
|
70
|
+
start += 2
|
|
71
|
+
} else if (startChar.toLowerCase() === 'q' && nextChar === '\'') {
|
|
72
|
+
start += 3
|
|
73
|
+
end -= 2
|
|
74
|
+
} else if (startChar === '$') {
|
|
75
|
+
const match = regexResult[0]
|
|
76
|
+
const size = match.indexOf('$', 1) + 1
|
|
77
|
+
if (size > 1) {
|
|
78
|
+
start += size
|
|
79
|
+
end -= size
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
tokens.push({ start, end })
|
|
85
|
+
regexResult = pattern.exec(evidence.value)
|
|
86
|
+
}
|
|
87
|
+
return tokens
|
|
88
|
+
} catch (e) {
|
|
89
|
+
iastLog.debug(e)
|
|
90
|
+
}
|
|
91
|
+
return []
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = SqlSensitiveAnalyzer
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const vulnerabilities = require('../../vulnerabilities')
|
|
4
|
+
|
|
5
|
+
const { contains, intersects, remove } = require('./range-utils')
|
|
6
|
+
|
|
7
|
+
const CommandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')
|
|
8
|
+
const LdapSensitiveAnalyzer = require('./sensitive-analyzers/ldap-sensitive-analyzer')
|
|
9
|
+
const SqlSensitiveAnalyzer = require('./sensitive-analyzers/sql-sensitive-analyzer')
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line max-len
|
|
12
|
+
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)'
|
|
13
|
+
// eslint-disable-next-line max-len
|
|
14
|
+
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,}))'
|
|
15
|
+
|
|
16
|
+
class SensitiveHandler {
|
|
17
|
+
constructor () {
|
|
18
|
+
this._namePattern = new RegExp(DEFAULT_IAST_REDACTION_NAME_PATTERN, 'gmi')
|
|
19
|
+
this._valuePattern = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
20
|
+
|
|
21
|
+
this._sensitiveAnalyzers = new Map()
|
|
22
|
+
this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, new CommandSensitiveAnalyzer())
|
|
23
|
+
this._sensitiveAnalyzers.set(vulnerabilities.LDAP_INJECTION, new LdapSensitiveAnalyzer())
|
|
24
|
+
this._sensitiveAnalyzers.set(vulnerabilities.SQL_INJECTION, new SqlSensitiveAnalyzer())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isSensibleName (name) {
|
|
28
|
+
this._namePattern.lastIndex = 0
|
|
29
|
+
return this._namePattern.test(name)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
isSensibleValue (value) {
|
|
33
|
+
this._valuePattern.lastIndex = 0
|
|
34
|
+
return this._valuePattern.test(value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
isSensibleSource (source) {
|
|
38
|
+
return source != null && (this.isSensibleName(source.name) || this.isSensibleValue(source.value))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
scrubEvidence (vulnerabilityType, evidence, sourcesIndexes, sources) {
|
|
42
|
+
const sensitiveAnalyzer = this._sensitiveAnalyzers.get(vulnerabilityType)
|
|
43
|
+
if (sensitiveAnalyzer) {
|
|
44
|
+
const sensitiveRanges = sensitiveAnalyzer.extractSensitiveRanges(evidence)
|
|
45
|
+
return this.toRedactedJson(evidence, sensitiveRanges, sourcesIndexes, sources)
|
|
46
|
+
}
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
toRedactedJson (evidence, sensitive, sourcesIndexes, sources) {
|
|
51
|
+
const valueParts = []
|
|
52
|
+
const redactedSources = []
|
|
53
|
+
|
|
54
|
+
const { value, ranges } = evidence
|
|
55
|
+
|
|
56
|
+
let start = 0
|
|
57
|
+
let nextTaintedIndex = 0
|
|
58
|
+
let sourceIndex
|
|
59
|
+
|
|
60
|
+
let nextTainted = ranges.shift()
|
|
61
|
+
let nextSensitive = sensitive.shift()
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < value.length; i++) {
|
|
64
|
+
if (nextTainted != null && nextTainted.start === i) {
|
|
65
|
+
this.writeValuePart(valueParts, value.substring(start, i), sourceIndex)
|
|
66
|
+
|
|
67
|
+
sourceIndex = sourcesIndexes[nextTaintedIndex]
|
|
68
|
+
|
|
69
|
+
while (nextSensitive != null && contains(nextTainted, nextSensitive)) {
|
|
70
|
+
sourceIndex != null && redactedSources.push(sourceIndex)
|
|
71
|
+
nextSensitive = sensitive.shift()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (nextSensitive != null && intersects(nextSensitive, nextTainted)) {
|
|
75
|
+
sourceIndex != null && redactedSources.push(sourceIndex)
|
|
76
|
+
|
|
77
|
+
const entries = remove(nextSensitive, nextTainted)
|
|
78
|
+
nextSensitive = entries.length > 0 ? entries[0] : null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.isSensibleSource(sources[sourceIndex]) && redactedSources.push(sourceIndex)
|
|
82
|
+
|
|
83
|
+
if (redactedSources.indexOf(sourceIndex) > -1) {
|
|
84
|
+
this.writeRedactedValuePart(valueParts, sourceIndex)
|
|
85
|
+
} else {
|
|
86
|
+
const substringEnd = Math.min(nextTainted.end, value.length)
|
|
87
|
+
this.writeValuePart(valueParts, value.substring(nextTainted.start, substringEnd), sourceIndex)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
start = i + (nextTainted.end - nextTainted.start)
|
|
91
|
+
i = start - 1
|
|
92
|
+
nextTainted = ranges.shift()
|
|
93
|
+
nextTaintedIndex++
|
|
94
|
+
sourceIndex = null
|
|
95
|
+
} else if (nextSensitive != null && nextSensitive.start === i) {
|
|
96
|
+
this.writeValuePart(valueParts, value.substring(start, i), sourceIndex)
|
|
97
|
+
if (nextTainted != null && intersects(nextSensitive, nextTainted)) {
|
|
98
|
+
sourceIndex = sourcesIndexes[nextTaintedIndex]
|
|
99
|
+
sourceIndex != null && redactedSources.push(sourceIndex)
|
|
100
|
+
|
|
101
|
+
for (const entry of remove(nextSensitive, nextTainted)) {
|
|
102
|
+
if (entry.start === i) {
|
|
103
|
+
nextSensitive = entry
|
|
104
|
+
} else {
|
|
105
|
+
sensitive.push(entry)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.writeRedactedValuePart(valueParts)
|
|
111
|
+
|
|
112
|
+
start = i + (nextSensitive.end - nextSensitive.start)
|
|
113
|
+
i = start - 1
|
|
114
|
+
nextSensitive = sensitive.shift()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (start < value.length) {
|
|
119
|
+
this.writeValuePart(valueParts, value.substring(start))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { redactedValueParts: valueParts, redactedSources }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
writeValuePart (valueParts, value, source) {
|
|
126
|
+
if (value.length > 0) {
|
|
127
|
+
if (source != null) {
|
|
128
|
+
valueParts.push({ value, source })
|
|
129
|
+
} else {
|
|
130
|
+
valueParts.push({ value })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
writeRedactedValuePart (valueParts, source) {
|
|
136
|
+
if (source != null) {
|
|
137
|
+
valueParts.push({ redacted: true, source })
|
|
138
|
+
} else {
|
|
139
|
+
valueParts.push({ redacted: true })
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = new SensitiveHandler()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const sensitiveHandler = require('./evidence-redaction/sensitive-handler')
|
|
2
|
+
|
|
3
|
+
class VulnerabilityFormatter {
|
|
4
|
+
constructor () {
|
|
5
|
+
this._redactVulnearbilities = true
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
setRedactVulnerabilities (shouldRedactVulnerabilities) {
|
|
9
|
+
this._redactVulnearbilities = shouldRedactVulnerabilities
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extractSourcesFromVulnerability (vulnerability) {
|
|
13
|
+
if (!vulnerability.evidence.ranges) {
|
|
14
|
+
return []
|
|
15
|
+
}
|
|
16
|
+
return vulnerability.evidence.ranges.map(range => (
|
|
17
|
+
{
|
|
18
|
+
origin: range.iinfo.type,
|
|
19
|
+
name: range.iinfo.parameterName,
|
|
20
|
+
value: range.iinfo.parameterValue
|
|
21
|
+
}
|
|
22
|
+
))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getRedactedValueParts (type, evidence, sourcesIndexes, sources) {
|
|
26
|
+
const scrubbingResult = sensitiveHandler.scrubEvidence(type, evidence, sourcesIndexes, sources)
|
|
27
|
+
if (scrubbingResult) {
|
|
28
|
+
const { redactedValueParts, redactedSources } = scrubbingResult
|
|
29
|
+
redactedSources.forEach(i => {
|
|
30
|
+
delete sources[i].value
|
|
31
|
+
sources[i].redacted = true
|
|
32
|
+
})
|
|
33
|
+
return { valueParts: redactedValueParts }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.getUnredactedValueParts(evidence, sourcesIndexes)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getUnredactedValueParts (evidence, sourcesIndexes) {
|
|
40
|
+
const valueParts = []
|
|
41
|
+
let fromIndex = 0
|
|
42
|
+
evidence.ranges.forEach((range, rangeIndex) => {
|
|
43
|
+
if (fromIndex < range.start) {
|
|
44
|
+
valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
|
|
45
|
+
}
|
|
46
|
+
valueParts.push({ value: evidence.value.substring(range.start, range.end), source: sourcesIndexes[rangeIndex] })
|
|
47
|
+
fromIndex = range.end
|
|
48
|
+
})
|
|
49
|
+
if (fromIndex < evidence.value.length) {
|
|
50
|
+
valueParts.push({ value: evidence.value.substring(fromIndex) })
|
|
51
|
+
}
|
|
52
|
+
return { valueParts }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
formatEvidence (type, evidence, sourcesIndexes, sources) {
|
|
56
|
+
if (!evidence.ranges) {
|
|
57
|
+
return { value: evidence.value }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return this._redactVulnearbilities
|
|
61
|
+
? this.getRedactedValueParts(type, evidence, sourcesIndexes, sources)
|
|
62
|
+
: this.getUnredactedValueParts(evidence, sourcesIndexes)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
formatVulnerability (vulnerability, sourcesIndexes, sources) {
|
|
66
|
+
const formattedVulnerability = {
|
|
67
|
+
type: vulnerability.type,
|
|
68
|
+
hash: vulnerability.hash,
|
|
69
|
+
evidence: this.formatEvidence(vulnerability.type, vulnerability.evidence, sourcesIndexes, sources),
|
|
70
|
+
location: {
|
|
71
|
+
spanId: vulnerability.location.spanId
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (vulnerability.location.path) {
|
|
75
|
+
formattedVulnerability.location.path = vulnerability.location.path
|
|
76
|
+
}
|
|
77
|
+
if (vulnerability.location.line) {
|
|
78
|
+
formattedVulnerability.location.line = vulnerability.location.line
|
|
79
|
+
}
|
|
80
|
+
return formattedVulnerability
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toJson (vulnerabilitiesToFormat) {
|
|
84
|
+
const sources = []
|
|
85
|
+
|
|
86
|
+
const vulnerabilities = vulnerabilitiesToFormat.map(vulnerability => {
|
|
87
|
+
const vulnerabilitySources = this.extractSourcesFromVulnerability(vulnerability)
|
|
88
|
+
const sourcesIndexes = []
|
|
89
|
+
vulnerabilitySources.forEach((source) => {
|
|
90
|
+
let sourceIndex = sources.findIndex(
|
|
91
|
+
existingSource =>
|
|
92
|
+
existingSource.origin === source.origin &&
|
|
93
|
+
existingSource.name === source.name &&
|
|
94
|
+
existingSource.value === source.value
|
|
95
|
+
)
|
|
96
|
+
if (sourceIndex === -1) {
|
|
97
|
+
sourceIndex = sources.length
|
|
98
|
+
sources.push(source)
|
|
99
|
+
}
|
|
100
|
+
sourcesIndexes.push(sourceIndex)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return this.formatVulnerability(vulnerability, sourcesIndexes, sources)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
sources,
|
|
108
|
+
vulnerabilities
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = new VulnerabilityFormatter()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
2
2
|
const LRU = require('lru-cache')
|
|
3
|
+
const vulnerabilitiesFormatter = require('./vulnerabilities-formatter')
|
|
3
4
|
const VULNERABILITIES_KEY = 'vulnerabilities'
|
|
4
5
|
const IAST_JSON_TAG_KEY = '_dd.iast.json'
|
|
5
6
|
const VULNERABILITY_HASHES_MAX_SIZE = 1000
|
|
@@ -60,57 +61,6 @@ function isValidVulnerability (vulnerability) {
|
|
|
60
61
|
vulnerability.location && vulnerability.location.spanId
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
function formatEvidence (evidence, sourcesIndexes) {
|
|
64
|
-
if (!evidence.ranges) {
|
|
65
|
-
return { value: evidence.value }
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const valueParts = []
|
|
69
|
-
let fromIndex = 0
|
|
70
|
-
evidence.ranges.forEach((range, rangeIndex) => {
|
|
71
|
-
if (fromIndex < range.start) {
|
|
72
|
-
valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
|
|
73
|
-
}
|
|
74
|
-
valueParts.push({ value: evidence.value.substring(range.start, range.end), source: sourcesIndexes[rangeIndex] })
|
|
75
|
-
fromIndex = range.end
|
|
76
|
-
})
|
|
77
|
-
if (fromIndex < evidence.value.length) {
|
|
78
|
-
valueParts.push({ value: evidence.value.substring(fromIndex) })
|
|
79
|
-
}
|
|
80
|
-
return { valueParts }
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function extractSourcesFromVulnerability (vulnerability) {
|
|
84
|
-
if (!vulnerability.evidence.ranges) {
|
|
85
|
-
return []
|
|
86
|
-
}
|
|
87
|
-
return vulnerability.evidence.ranges.map(range => (
|
|
88
|
-
{
|
|
89
|
-
origin: range.iinfo.type,
|
|
90
|
-
name: range.iinfo.parameterName,
|
|
91
|
-
value: range.iinfo.parameterValue
|
|
92
|
-
}
|
|
93
|
-
))
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function jsonVulnerabilityFromVulnerability (vulnerability, sourcesIndexes) {
|
|
97
|
-
const jsonVulnerability = {
|
|
98
|
-
type: vulnerability.type,
|
|
99
|
-
hash: vulnerability.hash,
|
|
100
|
-
evidence: formatEvidence(vulnerability.evidence, sourcesIndexes),
|
|
101
|
-
location: {
|
|
102
|
-
spanId: vulnerability.location.spanId
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (vulnerability.location.path) {
|
|
106
|
-
jsonVulnerability.location.path = vulnerability.location.path
|
|
107
|
-
}
|
|
108
|
-
if (vulnerability.location.line) {
|
|
109
|
-
jsonVulnerability.location.line = vulnerability.location.line
|
|
110
|
-
}
|
|
111
|
-
return jsonVulnerability
|
|
112
|
-
}
|
|
113
|
-
|
|
114
64
|
function sendVulnerabilities (vulnerabilities, rootSpan) {
|
|
115
65
|
if (vulnerabilities && vulnerabilities.length) {
|
|
116
66
|
let span = rootSpan
|
|
@@ -124,31 +74,8 @@ function sendVulnerabilities (vulnerabilities, rootSpan) {
|
|
|
124
74
|
}
|
|
125
75
|
|
|
126
76
|
if (span && span.addTags) {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
vulnerabilities: []
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
deduplicateVulnerabilities(vulnerabilities).forEach((vulnerability) => {
|
|
133
|
-
if (isValidVulnerability(vulnerability)) {
|
|
134
|
-
const sourcesIndexes = []
|
|
135
|
-
const vulnerabilitySources = extractSourcesFromVulnerability(vulnerability)
|
|
136
|
-
vulnerabilitySources.forEach((source) => {
|
|
137
|
-
let sourceIndex = jsonToSend.sources.findIndex(
|
|
138
|
-
existingSource =>
|
|
139
|
-
existingSource.origin === source.origin &&
|
|
140
|
-
existingSource.name === source.name &&
|
|
141
|
-
existingSource.value === source.value
|
|
142
|
-
)
|
|
143
|
-
if (sourceIndex === -1) {
|
|
144
|
-
sourceIndex = jsonToSend.sources.length
|
|
145
|
-
jsonToSend.sources.push(source)
|
|
146
|
-
}
|
|
147
|
-
sourcesIndexes.push(sourceIndex)
|
|
148
|
-
})
|
|
149
|
-
jsonToSend.vulnerabilities.push(jsonVulnerabilityFromVulnerability(vulnerability, sourcesIndexes))
|
|
150
|
-
}
|
|
151
|
-
})
|
|
77
|
+
const validAndDedupVulnerabilities = deduplicateVulnerabilities(vulnerabilities).filter(isValidVulnerability)
|
|
78
|
+
const jsonToSend = vulnerabilitiesFormatter.toJson(validAndDedupVulnerabilities)
|
|
152
79
|
|
|
153
80
|
if (jsonToSend.vulnerabilities.length > 0) {
|
|
154
81
|
const tags = {}
|
|
@@ -194,6 +121,7 @@ function deduplicateVulnerabilities (vulnerabilities) {
|
|
|
194
121
|
|
|
195
122
|
function start (config, _tracer) {
|
|
196
123
|
deduplicationEnabled = config.iast.deduplicationEnabled
|
|
124
|
+
vulnerabilitiesFormatter.setRedactVulnerabilities(config.iast.redactionEnabled)
|
|
197
125
|
if (deduplicationEnabled) {
|
|
198
126
|
startClearCacheTimer()
|
|
199
127
|
}
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const os = require('os')
|
|
5
|
+
const uuid = require('crypto-randomuuid')
|
|
5
6
|
const URL = require('url').URL
|
|
6
7
|
const log = require('./log')
|
|
7
8
|
const pkg = require('./pkg')
|
|
8
9
|
const coalesce = require('koalas')
|
|
9
10
|
const tagger = require('./tagger')
|
|
10
11
|
const { isTrue, isFalse } = require('./util')
|
|
11
|
-
const
|
|
12
|
+
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
|
|
13
|
+
const { getGitMetadataFromGitProperties } = require('./git_properties')
|
|
12
14
|
|
|
13
15
|
const fromEntries = Object.fromEntries || (entries =>
|
|
14
16
|
entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
|
|
@@ -159,6 +161,8 @@ class Config {
|
|
|
159
161
|
process.env.DD_SERVICE_NAME ||
|
|
160
162
|
this.tags.service ||
|
|
161
163
|
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
164
|
+
process.env.FUNCTION_NAME || // Google Cloud Function Name set by deprecated runtimes
|
|
165
|
+
process.env.K_SERVICE || // Google Cloud Function Name set by newer runtimes
|
|
162
166
|
pkg.name ||
|
|
163
167
|
'node'
|
|
164
168
|
const DD_SERVICE_MAPPING = coalesce(
|
|
@@ -183,9 +187,18 @@ class Config {
|
|
|
183
187
|
process.env.DD_TRACE_STARTUP_LOGS,
|
|
184
188
|
false
|
|
185
189
|
)
|
|
190
|
+
|
|
191
|
+
const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
|
|
192
|
+
|
|
193
|
+
const isDeprecatedGCPFunction = process.env.FUNCTION_NAME !== undefined && process.env.GCP_PROJECT !== undefined
|
|
194
|
+
const isNewerGCPFunction = process.env.K_SERVICE !== undefined && process.env.FUNCTION_TARGET !== undefined
|
|
195
|
+
const isGCPFunction = isDeprecatedGCPFunction || isNewerGCPFunction
|
|
196
|
+
|
|
197
|
+
const inServerlessEnvironment = inAWSLambda || isGCPFunction
|
|
198
|
+
|
|
186
199
|
const DD_TRACE_TELEMETRY_ENABLED = coalesce(
|
|
187
200
|
process.env.DD_TRACE_TELEMETRY_ENABLED,
|
|
188
|
-
!
|
|
201
|
+
!inServerlessEnvironment
|
|
189
202
|
)
|
|
190
203
|
const DD_TELEMETRY_DEBUG_ENABLED = coalesce(
|
|
191
204
|
process.env.DD_TELEMETRY_DEBUG_ENABLED,
|
|
@@ -269,7 +282,7 @@ class Config {
|
|
|
269
282
|
const DD_TRACE_STATS_COMPUTATION_ENABLED = coalesce(
|
|
270
283
|
options.stats,
|
|
271
284
|
process.env.DD_TRACE_STATS_COMPUTATION_ENABLED,
|
|
272
|
-
|
|
285
|
+
isGCPFunction
|
|
273
286
|
)
|
|
274
287
|
|
|
275
288
|
const DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED = coalesce(
|
|
@@ -336,12 +349,10 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
336
349
|
maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
|
|
337
350
|
)
|
|
338
351
|
|
|
339
|
-
const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
|
|
340
|
-
|
|
341
352
|
const remoteConfigOptions = options.remoteConfig || {}
|
|
342
353
|
const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
|
|
343
354
|
process.env.DD_REMOTE_CONFIGURATION_ENABLED && isTrue(process.env.DD_REMOTE_CONFIGURATION_ENABLED),
|
|
344
|
-
!
|
|
355
|
+
!inServerlessEnvironment
|
|
345
356
|
)
|
|
346
357
|
const DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = coalesce(
|
|
347
358
|
parseInt(remoteConfigOptions.pollInterval),
|
|
@@ -388,11 +399,22 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
388
399
|
true
|
|
389
400
|
)
|
|
390
401
|
|
|
402
|
+
const DD_IAST_REDACTION_ENABLED = coalesce(
|
|
403
|
+
iastOptions && iastOptions.redactionEnabled,
|
|
404
|
+
!isFalse(process.env.DD_IAST_REDACTION_ENABLED),
|
|
405
|
+
true
|
|
406
|
+
)
|
|
407
|
+
|
|
391
408
|
const DD_CIVISIBILITY_GIT_UPLOAD_ENABLED = coalesce(
|
|
392
409
|
process.env.DD_CIVISIBILITY_GIT_UPLOAD_ENABLED,
|
|
393
410
|
true
|
|
394
411
|
)
|
|
395
412
|
|
|
413
|
+
const DD_TRACE_GIT_METADATA_ENABLED = coalesce(
|
|
414
|
+
process.env.DD_TRACE_GIT_METADATA_ENABLED,
|
|
415
|
+
true
|
|
416
|
+
)
|
|
417
|
+
|
|
396
418
|
const ingestion = options.ingestion || {}
|
|
397
419
|
const dogstatsd = coalesce(options.dogstatsd, {})
|
|
398
420
|
const sampler = {
|
|
@@ -424,7 +446,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
424
446
|
})
|
|
425
447
|
}
|
|
426
448
|
|
|
427
|
-
const defaultFlushInterval =
|
|
449
|
+
const defaultFlushInterval = inServerlessEnvironment ? 0 : 2000
|
|
428
450
|
|
|
429
451
|
this.tracing = !isFalse(DD_TRACING_ENABLED)
|
|
430
452
|
this.dbmPropagationMode = DD_DBM_PROPAGATION_MODE
|
|
@@ -497,7 +519,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
497
519
|
requestSampling: DD_IAST_REQUEST_SAMPLING,
|
|
498
520
|
maxConcurrentRequests: DD_IAST_MAX_CONCURRENT_REQUESTS,
|
|
499
521
|
maxContextOperations: DD_IAST_MAX_CONTEXT_OPERATIONS,
|
|
500
|
-
deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED
|
|
522
|
+
deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED,
|
|
523
|
+
redactionEnabled: DD_IAST_REDACTION_ENABLED
|
|
501
524
|
}
|
|
502
525
|
|
|
503
526
|
this.isCiVisibility = isTrue(DD_IS_CIVISIBILITY)
|
|
@@ -506,6 +529,31 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
506
529
|
this.isGitUploadEnabled = this.isCiVisibility &&
|
|
507
530
|
(this.isIntelligentTestRunnerEnabled && !isFalse(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED))
|
|
508
531
|
|
|
532
|
+
this.gitMetadataEnabled = isTrue(DD_TRACE_GIT_METADATA_ENABLED)
|
|
533
|
+
|
|
534
|
+
if (this.gitMetadataEnabled) {
|
|
535
|
+
this.repositoryUrl = coalesce(
|
|
536
|
+
process.env.DD_GIT_REPOSITORY_URL,
|
|
537
|
+
this.tags[GIT_REPOSITORY_URL]
|
|
538
|
+
)
|
|
539
|
+
this.commitSHA = coalesce(
|
|
540
|
+
process.env.DD_GIT_COMMIT_SHA,
|
|
541
|
+
this.tags[GIT_COMMIT_SHA]
|
|
542
|
+
)
|
|
543
|
+
if (!this.repositoryUrl || !this.commitSHA) {
|
|
544
|
+
const DD_GIT_PROPERTIES_FILE = coalesce(
|
|
545
|
+
process.env.DD_GIT_PROPERTIES_FILE,
|
|
546
|
+
`${process.cwd()}/git.properties`
|
|
547
|
+
)
|
|
548
|
+
const gitPropertiesString = maybeFile(DD_GIT_PROPERTIES_FILE)
|
|
549
|
+
if (gitPropertiesString) {
|
|
550
|
+
const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
|
|
551
|
+
this.commitSHA = this.commitSHA || commitSHA
|
|
552
|
+
this.repositoryUrl = this.repositoryUrl || repositoryUrl
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
509
557
|
this.stats = {
|
|
510
558
|
enabled: isTrue(DD_TRACE_STATS_COMPUTATION_ENABLED)
|
|
511
559
|
}
|
|
@@ -513,6 +561,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
513
561
|
this.traceId128BitGenerationEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
|
|
514
562
|
this.traceId128BitLoggingEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)
|
|
515
563
|
|
|
564
|
+
this.isGCPFunction = isGCPFunction
|
|
565
|
+
|
|
516
566
|
tagger.add(this.tags, {
|
|
517
567
|
service: this.service,
|
|
518
568
|
env: this.env,
|
|
@@ -25,5 +25,7 @@ module.exports = {
|
|
|
25
25
|
ERROR_MESSAGE: 'error.message',
|
|
26
26
|
ERROR_STACK: 'error.stack',
|
|
27
27
|
COMPONENT: 'component',
|
|
28
|
-
CLIENT_PORT_KEY: 'network.destination.port'
|
|
28
|
+
CLIENT_PORT_KEY: 'network.destination.port',
|
|
29
|
+
SCI_REPOSITORY_URL: '_dd.git.repository_url',
|
|
30
|
+
SCI_COMMIT_SHA: '_dd.git.commit.sha'
|
|
29
31
|
}
|