dd-trace 2.33.0 → 2.34.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.
Files changed (47) hide show
  1. package/index.d.ts +8 -1
  2. package/package.json +4 -3
  3. package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
  4. package/packages/datadog-instrumentations/src/jest.js +20 -17
  5. package/packages/datadog-instrumentations/src/next.js +6 -1
  6. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -0
  7. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -1
  8. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -2
  9. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +4 -3
  10. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -1
  11. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -0
  12. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -1
  13. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -1
  14. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +8 -1
  15. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -1
  16. package/packages/datadog-plugin-http/src/client.js +2 -1
  17. package/packages/datadog-plugin-http2/src/client.js +2 -1
  18. package/packages/datadog-plugin-jest/src/util.js +10 -1
  19. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
  20. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +2 -1
  21. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +22 -5
  22. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +40 -4
  23. package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +2 -1
  24. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
  25. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  26. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +37 -0
  28. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +29 -0
  29. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +35 -0
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +95 -0
  31. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +144 -0
  32. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
  33. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +8 -0
  34. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -76
  35. package/packages/dd-trace/src/config.js +58 -8
  36. package/packages/dd-trace/src/constants.js +3 -1
  37. package/packages/dd-trace/src/git_metadata_tagger.js +17 -0
  38. package/packages/dd-trace/src/git_properties.js +32 -0
  39. package/packages/dd-trace/src/plugins/util/ci.js +62 -7
  40. package/packages/dd-trace/src/plugins/util/tags.js +5 -1
  41. package/packages/dd-trace/src/profiling/constants.js +0 -1
  42. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  43. package/packages/dd-trace/src/proxy.js +4 -0
  44. package/packages/dd-trace/src/serverless.js +25 -0
  45. package/packages/dd-trace/src/span_processor.js +3 -0
  46. package/packages/dd-trace/src/tracer.js +3 -2
  47. package/version.js +9 -0
@@ -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()
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ WEAK_HASH: 'WEAK_HASH',
3
+ WEAK_CIPHER: 'WEAK_CIPHER',
4
+ SQL_INJECTION: 'SQL_INJECTION',
5
+ PATH_TRAVERSAL: 'PATH_TRAVERSAL',
6
+ COMMAND_INJECTION: 'COMMAND_INJECTION',
7
+ LDAP_INJECTION: 'LDAP_INJECTION'
8
+ }
@@ -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 jsonToSend = {
128
- sources: [],
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 uuid = require('crypto-randomuuid')
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 }), {}))
@@ -156,6 +158,8 @@ class Config {
156
158
  process.env.DD_SERVICE_NAME ||
157
159
  this.tags.service ||
158
160
  process.env.AWS_LAMBDA_FUNCTION_NAME ||
161
+ process.env.FUNCTION_NAME || // Google Cloud Function Name set by deprecated runtimes
162
+ process.env.K_SERVICE || // Google Cloud Function Name set by newer runtimes
159
163
  pkg.name ||
160
164
  'node'
161
165
  const DD_SERVICE_MAPPING = coalesce(
@@ -180,9 +184,18 @@ class Config {
180
184
  process.env.DD_TRACE_STARTUP_LOGS,
181
185
  false
182
186
  )
187
+
188
+ const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
189
+
190
+ const isDeprecatedGCPFunction = process.env.FUNCTION_NAME !== undefined && process.env.GCP_PROJECT !== undefined
191
+ const isNewerGCPFunction = process.env.K_SERVICE !== undefined && process.env.FUNCTION_TARGET !== undefined
192
+ const isGCPFunction = isDeprecatedGCPFunction || isNewerGCPFunction
193
+
194
+ const inServerlessEnvironment = inAWSLambda || isGCPFunction
195
+
183
196
  const DD_TRACE_TELEMETRY_ENABLED = coalesce(
184
197
  process.env.DD_TRACE_TELEMETRY_ENABLED,
185
- !process.env.AWS_LAMBDA_FUNCTION_NAME
198
+ !inServerlessEnvironment
186
199
  )
187
200
  const DD_TELEMETRY_DEBUG_ENABLED = coalesce(
188
201
  process.env.DD_TELEMETRY_DEBUG_ENABLED,
@@ -266,7 +279,7 @@ class Config {
266
279
  const DD_TRACE_STATS_COMPUTATION_ENABLED = coalesce(
267
280
  options.stats,
268
281
  process.env.DD_TRACE_STATS_COMPUTATION_ENABLED,
269
- false
282
+ isGCPFunction
270
283
  )
271
284
 
272
285
  const DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED = coalesce(
@@ -333,12 +346,10 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
333
346
  maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
334
347
  )
335
348
 
336
- const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
337
-
338
349
  const remoteConfigOptions = options.remoteConfig || {}
339
350
  const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
340
351
  process.env.DD_REMOTE_CONFIGURATION_ENABLED && isTrue(process.env.DD_REMOTE_CONFIGURATION_ENABLED),
341
- !inAWSLambda
352
+ !inServerlessEnvironment
342
353
  )
343
354
  const DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = coalesce(
344
355
  parseInt(remoteConfigOptions.pollInterval),
@@ -385,11 +396,22 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
385
396
  true
386
397
  )
387
398
 
399
+ const DD_IAST_REDACTION_ENABLED = coalesce(
400
+ iastOptions && iastOptions.redactionEnabled,
401
+ !isFalse(process.env.DD_IAST_REDACTION_ENABLED),
402
+ true
403
+ )
404
+
388
405
  const DD_CIVISIBILITY_GIT_UPLOAD_ENABLED = coalesce(
389
406
  process.env.DD_CIVISIBILITY_GIT_UPLOAD_ENABLED,
390
407
  true
391
408
  )
392
409
 
410
+ const DD_TRACE_GIT_METADATA_ENABLED = coalesce(
411
+ process.env.DD_TRACE_GIT_METADATA_ENABLED,
412
+ true
413
+ )
414
+
393
415
  const ingestion = options.ingestion || {}
394
416
  const dogstatsd = coalesce(options.dogstatsd, {})
395
417
  const sampler = {
@@ -421,7 +443,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
421
443
  })
422
444
  }
423
445
 
424
- const defaultFlushInterval = inAWSLambda ? 0 : 2000
446
+ const defaultFlushInterval = inServerlessEnvironment ? 0 : 2000
425
447
 
426
448
  this.tracing = !isFalse(DD_TRACING_ENABLED)
427
449
  this.dbmPropagationMode = DD_DBM_PROPAGATION_MODE
@@ -494,7 +516,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
494
516
  requestSampling: DD_IAST_REQUEST_SAMPLING,
495
517
  maxConcurrentRequests: DD_IAST_MAX_CONCURRENT_REQUESTS,
496
518
  maxContextOperations: DD_IAST_MAX_CONTEXT_OPERATIONS,
497
- deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED
519
+ deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED,
520
+ redactionEnabled: DD_IAST_REDACTION_ENABLED
498
521
  }
499
522
 
500
523
  this.isCiVisibility = isTrue(DD_IS_CIVISIBILITY)
@@ -503,6 +526,31 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
503
526
  this.isGitUploadEnabled = this.isCiVisibility &&
504
527
  (this.isIntelligentTestRunnerEnabled && !isFalse(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED))
505
528
 
529
+ this.gitMetadataEnabled = isTrue(DD_TRACE_GIT_METADATA_ENABLED)
530
+
531
+ if (this.gitMetadataEnabled) {
532
+ this.repositoryUrl = coalesce(
533
+ process.env.DD_GIT_REPOSITORY_URL,
534
+ this.tags[GIT_REPOSITORY_URL]
535
+ )
536
+ this.commitSHA = coalesce(
537
+ process.env.DD_GIT_COMMIT_SHA,
538
+ this.tags[GIT_COMMIT_SHA]
539
+ )
540
+ if (!this.repositoryUrl || !this.commitSHA) {
541
+ const DD_GIT_PROPERTIES_FILE = coalesce(
542
+ process.env.DD_GIT_PROPERTIES_FILE,
543
+ `${process.cwd()}/git.properties`
544
+ )
545
+ const gitPropertiesString = maybeFile(DD_GIT_PROPERTIES_FILE)
546
+ if (gitPropertiesString) {
547
+ const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
548
+ this.commitSHA = this.commitSHA || commitSHA
549
+ this.repositoryUrl = this.repositoryUrl || repositoryUrl
550
+ }
551
+ }
552
+ }
553
+
506
554
  this.stats = {
507
555
  enabled: isTrue(DD_TRACE_STATS_COMPUTATION_ENABLED)
508
556
  }
@@ -510,6 +558,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
510
558
  this.traceId128BitGenerationEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
511
559
  this.traceId128BitLoggingEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)
512
560
 
561
+ this.isGCPFunction = isGCPFunction
562
+
513
563
  tagger.add(this.tags, {
514
564
  service: this.service,
515
565
  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
  }
@@ -0,0 +1,17 @@
1
+ const { SCI_COMMIT_SHA, SCI_REPOSITORY_URL } = require('./constants')
2
+
3
+ class GitMetadataTagger {
4
+ constructor (config) {
5
+ this._config = config
6
+ }
7
+
8
+ tagGitMetadata (spanContext) {
9
+ if (this._config.gitMetadataEnabled) {
10
+ // These tags are added only to the local root span
11
+ spanContext._trace.tags[SCI_COMMIT_SHA] = this._config.commitSHA
12
+ spanContext._trace.tags[SCI_REPOSITORY_URL] = this._config.repositoryUrl
13
+ }
14
+ }
15
+ }
16
+
17
+ module.exports = GitMetadataTagger
@@ -0,0 +1,32 @@
1
+ const commitSHARegex = /git\.commit\.sha=([a-f\d]{40})/
2
+ const repositoryUrlRegex = /git\.repository_url=([\w\d:@/.-]+)/
3
+
4
+ function getGitMetadataFromGitProperties (gitPropertiesString) {
5
+ if (!gitPropertiesString) {
6
+ return {}
7
+ }
8
+ const commitSHAMatch = gitPropertiesString.match(commitSHARegex)
9
+ const repositoryUrlMatch = gitPropertiesString.match(repositoryUrlRegex)
10
+
11
+ 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
+ return {
27
+ commitSHA: commitSHAMatch ? commitSHAMatch[1] : undefined,
28
+ repositoryUrl: parsedUrl
29
+ }
30
+ }
31
+
32
+ module.exports = { getGitMetadataFromGitProperties }