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.
Files changed (71) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +9 -1
  3. package/package.json +5 -4
  4. package/packages/datadog-instrumentations/src/body-parser.js +2 -1
  5. package/packages/datadog-instrumentations/src/cucumber.js +29 -4
  6. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +45 -0
  7. package/packages/datadog-instrumentations/src/express.js +2 -1
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  9. package/packages/datadog-instrumentations/src/jest.js +58 -20
  10. package/packages/datadog-instrumentations/src/knex.js +69 -1
  11. package/packages/datadog-instrumentations/src/mocha.js +34 -4
  12. package/packages/datadog-instrumentations/src/mongodb.js +63 -0
  13. package/packages/datadog-instrumentations/src/mongoose.js +140 -1
  14. package/packages/datadog-instrumentations/src/next.js +98 -23
  15. package/packages/datadog-instrumentations/src/playwright.js +22 -8
  16. package/packages/datadog-plugin-cucumber/src/index.js +17 -5
  17. package/packages/datadog-plugin-cypress/src/plugin.js +38 -8
  18. package/packages/datadog-plugin-http/src/client.js +2 -0
  19. package/packages/datadog-plugin-jest/src/index.js +29 -6
  20. package/packages/datadog-plugin-jest/src/util.js +45 -2
  21. package/packages/datadog-plugin-memcached/src/index.js +10 -5
  22. package/packages/datadog-plugin-mocha/src/index.js +25 -6
  23. package/packages/datadog-plugin-next/src/index.js +4 -3
  24. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  25. package/packages/dd-trace/src/appsec/channels.js +3 -1
  26. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  27. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +60 -0
  28. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +269 -0
  29. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +5 -2
  30. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +22 -4
  31. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +173 -0
  32. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +21 -1
  33. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -3
  34. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -2
  35. package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
  36. package/packages/dd-trace/src/appsec/iast/iast-log.js +9 -4
  37. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
  38. package/packages/dd-trace/src/appsec/iast/path-line.js +6 -1
  39. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +25 -12
  40. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
  41. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +13 -2
  42. package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks-generator.js +13 -0
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
  44. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +1 -14
  45. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +16 -0
  46. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +22 -4
  47. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +9 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +15 -2
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +169 -0
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  51. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +5 -1
  52. package/packages/dd-trace/src/appsec/index.js +31 -13
  53. package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -3
  54. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +14 -1
  55. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
  56. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -0
  57. package/packages/dd-trace/src/config.js +37 -13
  58. package/packages/dd-trace/src/format.js +3 -0
  59. package/packages/dd-trace/src/git_properties.js +16 -15
  60. package/packages/dd-trace/src/plugin_manager.js +3 -1
  61. package/packages/dd-trace/src/plugins/util/ci.js +17 -0
  62. package/packages/dd-trace/src/plugins/util/git.js +26 -4
  63. package/packages/dd-trace/src/plugins/util/test.js +45 -2
  64. package/packages/dd-trace/src/profiling/config.js +20 -3
  65. package/packages/dd-trace/src/profiling/profilers/wall.js +51 -40
  66. package/packages/dd-trace/src/service-naming/extra-services.js +24 -0
  67. package/packages/dd-trace/src/telemetry/index.js +4 -0
  68. package/packages/dd-trace/src/telemetry/logs/index.js +65 -0
  69. package/packages/dd-trace/src/{appsec/iast/telemetry/log → telemetry/logs}/log-collector.js +9 -22
  70. package/packages/dd-trace/src/telemetry/metrics.js +0 -5
  71. 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(config.iast.redactionEnabled)
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]: req.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]: req.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(JSON.stringify(this.state), this.requestOptions, (err, data, statusCode) => {
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
 
@@ -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
- done(null, { isCodeCoverageEnabled, isSuitesSkippingEnabled })
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
- false
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.experimental && options.experimental.iast
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 && iastOptions.requestSampling),
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 && iastOptions.maxConcurrentRequests),
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 && iastOptions.maxContextOperations),
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 && iastOptions.deduplicationEnabled,
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 && iastOptions.redactionEnabled,
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 && iastOptions.telemetryVerbosity,
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 = coalesce(
634
- process.env.DD_GIT_REPOSITORY_URL,
635
- this.tags[GIT_REPOSITORY_URL]
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: parsedUrl
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]