dd-trace 3.19.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.
Files changed (82) hide show
  1. package/index.d.ts +8 -1
  2. package/package.json +5 -4
  3. package/packages/datadog-instrumentations/src/grpc/client.js +9 -5
  4. package/packages/datadog-instrumentations/src/grpc/server.js +8 -4
  5. package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
  6. package/packages/datadog-instrumentations/src/jest.js +20 -17
  7. package/packages/datadog-instrumentations/src/mocha.js +8 -4
  8. package/packages/datadog-instrumentations/src/next.js +6 -1
  9. package/packages/datadog-instrumentations/src/pg.js +3 -4
  10. package/packages/datadog-instrumentations/src/playwright.js +11 -1
  11. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  12. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  13. package/packages/datadog-plugin-amqplib/src/client.js +3 -4
  14. package/packages/datadog-plugin-amqplib/src/consumer.js +3 -1
  15. package/packages/datadog-plugin-amqplib/src/producer.js +3 -1
  16. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -0
  17. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -1
  18. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -2
  19. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +4 -3
  20. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -1
  21. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -0
  22. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -1
  23. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -1
  24. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +8 -1
  25. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -1
  26. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +3 -4
  27. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +3 -1
  28. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +3 -1
  29. package/packages/datadog-plugin-http/src/client.js +2 -1
  30. package/packages/datadog-plugin-http2/src/client.js +2 -1
  31. package/packages/datadog-plugin-jest/src/util.js +10 -1
  32. package/packages/datadog-plugin-kafkajs/src/consumer.js +6 -1
  33. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -1
  34. package/packages/datadog-plugin-pg/src/index.js +5 -5
  35. package/packages/datadog-plugin-rhea/src/consumer.js +3 -1
  36. package/packages/datadog-plugin-rhea/src/producer.js +5 -1
  37. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
  38. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +2 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +22 -5
  40. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +40 -4
  41. package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +2 -1
  42. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
  43. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  45. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +37 -0
  46. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +29 -0
  47. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +35 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +95 -0
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +144 -0
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
  51. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +8 -0
  52. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -76
  53. package/packages/dd-trace/src/config.js +59 -25
  54. package/packages/dd-trace/src/constants.js +3 -1
  55. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -4
  56. package/packages/dd-trace/src/git_metadata_tagger.js +17 -0
  57. package/packages/dd-trace/src/git_properties.js +32 -0
  58. package/packages/dd-trace/src/plugin_manager.js +0 -2
  59. package/packages/dd-trace/src/plugins/client.js +2 -3
  60. package/packages/dd-trace/src/plugins/consumer.js +2 -17
  61. package/packages/dd-trace/src/plugins/incoming.js +7 -0
  62. package/packages/dd-trace/src/plugins/{outbound.js → outgoing.js} +2 -2
  63. package/packages/dd-trace/src/plugins/producer.js +2 -17
  64. package/packages/dd-trace/src/plugins/server.js +2 -2
  65. package/packages/dd-trace/src/plugins/tracing.js +0 -11
  66. package/packages/dd-trace/src/plugins/util/ci.js +62 -7
  67. package/packages/dd-trace/src/plugins/util/tags.js +5 -1
  68. package/packages/dd-trace/src/plugins/util/test.js +34 -17
  69. package/packages/dd-trace/src/profiling/constants.js +0 -1
  70. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  71. package/packages/dd-trace/src/proxy.js +4 -0
  72. package/packages/dd-trace/src/serverless.js +25 -0
  73. package/packages/dd-trace/src/span_processor.js +3 -0
  74. package/packages/dd-trace/src/telemetry/send-data.js +13 -3
  75. package/packages/dd-trace/src/tracer.js +3 -2
  76. package/version.js +9 -0
  77. package/packages/dd-trace/src/plugins/inbound.js +0 -7
  78. package/packages/dd-trace/src/service-naming/index.js +0 -41
  79. package/packages/dd-trace/src/service-naming/schemas/definition.js +0 -28
  80. package/packages/dd-trace/src/service-naming/schemas/index.js +0 -6
  81. package/packages/dd-trace/src/service-naming/schemas/v0.js +0 -66
  82. package/packages/dd-trace/src/service-naming/schemas/v1.js +0 -58
@@ -9,7 +9,7 @@ class PGPlugin extends DatabasePlugin {
9
9
  static get system () { return 'postgres' }
10
10
 
11
11
  start ({ params = {}, query, processId }) {
12
- const service = getServiceName(this.config, params)
12
+ const service = getServiceName(this, params)
13
13
  const originalStatement = query.text
14
14
 
15
15
  this.startSpan('pg.query', {
@@ -31,12 +31,12 @@ class PGPlugin extends DatabasePlugin {
31
31
  }
32
32
  }
33
33
 
34
- function getServiceName (config, params) {
35
- if (typeof config.service === 'function') {
36
- return config.service(params)
34
+ function getServiceName (tracer, params) {
35
+ if (typeof tracer.config.service === 'function') {
36
+ return tracer.config.service(params)
37
37
  }
38
38
 
39
- return config.service
39
+ return tracer.config.service || `${tracer._tracer._tracer._service}-postgres`
40
40
  }
41
41
 
42
42
  module.exports = PGPlugin
@@ -19,10 +19,12 @@ class RheaConsumerPlugin extends ConsumerPlugin {
19
19
  const name = getResourceNameFromMessage(msgObj)
20
20
  const childOf = extractTextMap(msgObj, this.tracer)
21
21
 
22
- this.startSpan({
22
+ this.startSpan('amqp.receive', {
23
23
  childOf,
24
+ service: this.config.service,
24
25
  resource: name,
25
26
  type: 'worker',
27
+ kind: 'consumer',
26
28
  meta: {
27
29
  'component': 'rhea',
28
30
  'amqp.link.source.address': name,
@@ -9,13 +9,17 @@ class RheaProducerPlugin extends ProducerPlugin {
9
9
 
10
10
  constructor (...args) {
11
11
  super(...args)
12
+
12
13
  this.addTraceSub('encode', this.encode.bind(this))
13
14
  }
14
15
 
15
16
  start ({ targetAddress, host, port }) {
16
17
  const name = targetAddress || 'amq.topic'
17
- this.startSpan({
18
+
19
+ this.startSpan('amqp.send', {
20
+ service: this.config.service || `${this.tracer._service}-amqp-producer`,
18
21
  resource: name,
22
+ kind: 'producer',
19
23
  meta: {
20
24
  'component': 'rhea',
21
25
  'amqp.link.target.address': name,
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
  const InjectionAnalyzer = require('./injection-analyzer')
3
+ const { COMMAND_INJECTION } = require('../vulnerabilities')
3
4
 
4
5
  class CommandInjectionAnalyzer extends InjectionAnalyzer {
5
6
  constructor () {
6
- super('COMMAND_INJECTION')
7
+ super(COMMAND_INJECTION)
7
8
  this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
8
9
  }
9
10
  }
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
  const InjectionAnalyzer = require('./injection-analyzer')
3
+ const { LDAP_INJECTION } = require('../vulnerabilities')
3
4
 
4
5
  class LdapInjectionAnalyzer extends InjectionAnalyzer {
5
6
  constructor () {
6
- super('LDAP_INJECTION')
7
+ super(LDAP_INJECTION)
7
8
  this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
8
9
  }
9
10
  }
@@ -4,10 +4,11 @@ const path = require('path')
4
4
  const { getIastContext } = require('../iast-context')
5
5
  const { storage } = require('../../../../../datadog-core')
6
6
  const InjectionAnalyzer = require('./injection-analyzer')
7
+ const { PATH_TRAVERSAL } = require('../vulnerabilities')
7
8
 
8
9
  class PathTraversalAnalyzer extends InjectionAnalyzer {
9
10
  constructor () {
10
- super('PATH_TRAVERSAL')
11
+ super(PATH_TRAVERSAL)
11
12
  this.addSub('apm:fs:operation:start', obj => {
12
13
  const pathArguments = []
13
14
  if (obj.dest) {
@@ -40,13 +41,29 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
40
41
  this.analyze(pathArguments)
41
42
  })
42
43
 
43
- this.exclusionList = [ path.join('node_modules', 'send') + path.sep ]
44
+ this.exclusionList = [
45
+ path.join('node_modules', 'send') + path.sep
46
+ ]
47
+
48
+ this.internalExclusionList = [
49
+ 'node:fs',
50
+ 'node:internal/fs',
51
+ 'node:internal\\fs',
52
+ 'fs.js',
53
+ 'internal/fs',
54
+ 'internal\\fs'
55
+ ]
44
56
  }
45
57
 
46
58
  _isExcluded (location) {
47
- let ret = false
59
+ let ret = true
48
60
  if (location && location.path) {
49
- ret = this.exclusionList.some(elem => location.path.includes(elem))
61
+ // Exclude from reporting those vulnerabilities which location is from an internal fs call
62
+ if (location.isInternal) {
63
+ ret = this.internalExclusionList.some(elem => location.path.includes(elem))
64
+ } else {
65
+ ret = this.exclusionList.some(elem => location.path.includes(elem))
66
+ }
50
67
  }
51
68
  return ret
52
69
  }
@@ -59,7 +76,7 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
59
76
 
60
77
  if (value && value.constructor === Array) {
61
78
  for (const val of value) {
62
- if (this._isVulnerable(val, iastContext)) {
79
+ if (this._isVulnerable(val, iastContext) && this._checkOCE(iastContext)) {
63
80
  this._report(val, iastContext)
64
81
  // no support several evidences in the same vulnerability, just report the 1st one
65
82
  break
@@ -1,12 +1,48 @@
1
1
  'use strict'
2
+
2
3
  const InjectionAnalyzer = require('./injection-analyzer')
4
+ const { SQL_INJECTION } = require('../vulnerabilities')
5
+ const { getRanges } = require('../taint-tracking/operations')
6
+ const { storage } = require('../../../../../datadog-core')
7
+ const { getIastContext } = require('../iast-context')
8
+ const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
3
9
 
4
10
  class SqlInjectionAnalyzer extends InjectionAnalyzer {
5
11
  constructor () {
6
- super('SQL_INJECTION')
7
- this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql))
8
- this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql))
9
- this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text))
12
+ super(SQL_INJECTION)
13
+ this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
14
+ this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
15
+ this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, 'POSTGRES'))
16
+ }
17
+
18
+ _getEvidence (value, iastContext, dialect) {
19
+ const ranges = getRanges(iastContext, value)
20
+ return { value, ranges, dialect }
21
+ }
22
+
23
+ analyze (value, dialect) {
24
+ const store = storage.getStore()
25
+ const iastContext = getIastContext(store)
26
+ if (store && !iastContext) return
27
+ this._reportIfVulnerable(value, iastContext, dialect)
28
+ }
29
+
30
+ _reportIfVulnerable (value, context, dialect) {
31
+ if (this._isVulnerable(value, context) && this._checkOCE(context)) {
32
+ this._report(value, context, dialect)
33
+ return true
34
+ }
35
+ return false
36
+ }
37
+
38
+ _report (value, context, dialect) {
39
+ const evidence = this._getEvidence(value, context, dialect)
40
+ const location = this._getLocation()
41
+ if (!this._isExcluded(location)) {
42
+ const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
43
+ const vulnerability = createVulnerability(this._type, evidence, spanId, location)
44
+ addVulnerability(context, vulnerability)
45
+ }
10
46
  }
11
47
  }
12
48
 
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
  const Analyzer = require('./vulnerability-analyzer')
3
+ const { WEAK_CIPHER } = require('../vulnerabilities')
3
4
 
4
5
  const INSECURE_CIPHERS = new Set([
5
6
  'des', 'des-cbc', 'des-cfb', 'des-cfb1', 'des-cfb8', 'des-ecb', 'des-ede', 'des-ede-cbc', 'des-ede-cfb',
@@ -12,7 +13,7 @@ const INSECURE_CIPHERS = new Set([
12
13
 
13
14
  class WeakCipherAnalyzer extends Analyzer {
14
15
  constructor () {
15
- super('WEAK_CIPHER')
16
+ super(WEAK_CIPHER)
16
17
  this.addSub('datadog:crypto:cipher:start', ({ algorithm }) => this.analyze(algorithm))
17
18
  }
18
19
 
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
  const Analyzer = require('./vulnerability-analyzer')
3
+ const { WEAK_HASH } = require('../vulnerabilities')
3
4
 
4
5
  const INSECURE_HASH_ALGORITHMS = new Set([
5
6
  'md4', 'md4WithRSAEncryption', 'RSA-MD4',
@@ -9,7 +10,7 @@ const INSECURE_HASH_ALGORITHMS = new Set([
9
10
 
10
11
  class WeakHashAnalyzer extends Analyzer {
11
12
  constructor () {
12
- super('WEAK_HASH')
13
+ super(WEAK_HASH)
13
14
  this.addSub('datadog:crypto:hashing:start', ({ algorithm }) => this.analyze(algorithm))
14
15
  }
15
16
 
@@ -51,7 +51,7 @@ function onIncomingHttpRequestStart (data) {
51
51
  }
52
52
  if (rootSpan.addTags) {
53
53
  rootSpan.addTags({
54
- [IAST_ENABLED_TAG_KEY]: isRequestAcquired ? '1' : '0'
54
+ [IAST_ENABLED_TAG_KEY]: isRequestAcquired ? 1 : 0
55
55
  })
56
56
  }
57
57
  }
@@ -45,7 +45,8 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
45
45
  if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
46
46
  return {
47
47
  path: path.relative(process.cwd(), filepath),
48
- line: callsite.getLineNumber()
48
+ line: callsite.getLineNumber(),
49
+ isInternal: !path.isAbsolute(filepath)
49
50
  }
50
51
  }
51
52
  }
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ function contains (rangeContainer, rangeContained) {
4
+ if (rangeContainer.start > rangeContained.start) {
5
+ return false
6
+ }
7
+ return rangeContainer.end >= rangeContained.end
8
+ }
9
+
10
+ function intersects (rangeA, rangeB) {
11
+ return rangeB.start < rangeA.end && rangeB.end > rangeA.start
12
+ }
13
+
14
+ function remove (range, rangeToRemove) {
15
+ if (!intersects(range, rangeToRemove)) {
16
+ return [range]
17
+ } else if (contains(rangeToRemove, range)) {
18
+ return []
19
+ } else {
20
+ const result = []
21
+ if (rangeToRemove.start > range.start) {
22
+ const offset = rangeToRemove.start - range.start
23
+ result.push({ start: range.start, end: range.start + offset })
24
+ }
25
+ if (rangeToRemove.end < range.end) {
26
+ const offset = range.end - rangeToRemove.end
27
+ result.push({ start: rangeToRemove.end, end: rangeToRemove.end + offset })
28
+ }
29
+ return result
30
+ }
31
+ }
32
+
33
+ module.exports = {
34
+ contains,
35
+ intersects,
36
+ remove
37
+ }
@@ -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()