dd-trace 4.15.0 → 4.17.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 (76) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/ext/tags.d.ts +1 -0
  3. package/ext/tags.js +1 -0
  4. package/index.d.ts +1 -0
  5. package/package.json +9 -6
  6. package/packages/datadog-esbuild/index.js +30 -25
  7. package/packages/datadog-instrumentations/src/body-parser.js +4 -3
  8. package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
  9. package/packages/datadog-instrumentations/src/cucumber.js +24 -4
  10. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +45 -0
  11. package/packages/datadog-instrumentations/src/express.js +3 -2
  12. package/packages/datadog-instrumentations/src/graphql.js +5 -0
  13. package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -1
  14. package/packages/datadog-instrumentations/src/http/server.js +1 -1
  15. package/packages/datadog-instrumentations/src/jest.js +20 -11
  16. package/packages/datadog-instrumentations/src/knex.js +62 -1
  17. package/packages/datadog-instrumentations/src/mocha.js +19 -4
  18. package/packages/datadog-instrumentations/src/mongodb.js +63 -0
  19. package/packages/datadog-instrumentations/src/mongoose.js +140 -1
  20. package/packages/datadog-instrumentations/src/next.js +62 -80
  21. package/packages/datadog-instrumentations/src/pg.js +14 -15
  22. package/packages/datadog-instrumentations/src/playwright.js +26 -5
  23. package/packages/datadog-plugin-cucumber/src/index.js +17 -5
  24. package/packages/datadog-plugin-cypress/src/plugin.js +38 -8
  25. package/packages/datadog-plugin-jest/src/index.js +19 -4
  26. package/packages/datadog-plugin-jest/src/util.js +45 -2
  27. package/packages/datadog-plugin-memcached/src/index.js +10 -5
  28. package/packages/datadog-plugin-mocha/src/index.js +19 -6
  29. package/packages/datadog-plugin-mysql/src/index.js +2 -2
  30. package/packages/datadog-plugin-next/src/index.js +14 -5
  31. package/packages/datadog-plugin-pg/src/index.js +2 -2
  32. package/packages/dd-trace/src/appsec/channels.js +4 -1
  33. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  34. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +166 -0
  35. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +21 -1
  36. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -3
  37. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -2
  38. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
  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/secure-marks-generator.js +13 -0
  42. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
  43. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +16 -0
  44. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
  45. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +9 -0
  46. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +13 -1
  47. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +169 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  49. package/packages/dd-trace/src/appsec/index.js +45 -14
  50. package/packages/dd-trace/src/appsec/recommended.json +549 -24
  51. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  52. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  53. package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -3
  54. package/packages/dd-trace/src/appsec/reporter.js +7 -5
  55. package/packages/dd-trace/src/appsec/telemetry.js +2 -2
  56. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +18 -5
  57. package/packages/dd-trace/src/appsec/waf/waf_manager.js +5 -4
  58. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +1 -14
  59. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -13
  60. package/packages/dd-trace/src/config.js +8 -0
  61. package/packages/dd-trace/src/datastreams/processor.js +6 -2
  62. package/packages/dd-trace/src/format.js +9 -1
  63. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
  64. package/packages/dd-trace/src/opentracing/tracer.js +0 -2
  65. package/packages/dd-trace/src/plugin_manager.js +4 -3
  66. package/packages/dd-trace/src/plugins/database.js +14 -4
  67. package/packages/dd-trace/src/plugins/index.js +1 -0
  68. package/packages/dd-trace/src/plugins/outbound.js +4 -3
  69. package/packages/dd-trace/src/plugins/util/ci.js +17 -0
  70. package/packages/dd-trace/src/plugins/util/git.js +26 -4
  71. package/packages/dd-trace/src/plugins/util/test.js +16 -1
  72. package/packages/dd-trace/src/profiling/config.js +36 -5
  73. package/packages/dd-trace/src/profiling/profilers/wall.js +7 -1
  74. package/packages/dd-trace/src/service-naming/extra-services.js +24 -0
  75. package/packages/dd-trace/src/telemetry/index.js +10 -1
  76. package/packages/dd-trace/src/telemetry/metrics.js +0 -5
@@ -8,5 +8,6 @@ module.exports = {
8
8
  ASM_REQUEST_BLOCKING: 1n << 5n,
9
9
  ASM_USER_BLOCKING: 1n << 7n,
10
10
  ASM_CUSTOM_RULES: 1n << 8n,
11
- ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n
11
+ ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n,
12
+ ASM_TRUSTED_IPS: 1n << 10n
12
13
  }
@@ -46,6 +46,7 @@ function enableWafUpdate (appsecConfig) {
46
46
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, true)
47
47
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
48
48
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
49
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
49
50
 
50
51
  rc.on('ASM_DATA', noop)
51
52
  rc.on('ASM_DD', noop)
@@ -66,6 +67,7 @@ function disableWafUpdate () {
66
67
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, false)
67
68
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
68
69
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
70
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
69
71
 
70
72
  rc.off('ASM_DATA', noop)
71
73
  rc.off('ASM_DD', noop)
@@ -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
 
@@ -69,16 +69,18 @@ function formatHeaderName (name) {
69
69
  .toLowerCase()
70
70
  }
71
71
 
72
- function reportWafInit (wafVersion, rulesInfo) {
72
+ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
73
73
  metricsQueue.set('_dd.appsec.waf.version', wafVersion)
74
74
 
75
- metricsQueue.set('_dd.appsec.event_rules.loaded', rulesInfo.loaded)
76
- metricsQueue.set('_dd.appsec.event_rules.error_count', rulesInfo.failed)
77
- if (rulesInfo.failed) metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(rulesInfo.errors))
75
+ metricsQueue.set('_dd.appsec.event_rules.loaded', diagnosticsRules.loaded?.length || 0)
76
+ metricsQueue.set('_dd.appsec.event_rules.error_count', diagnosticsRules.failed?.length || 0)
77
+ if (diagnosticsRules.failed?.length) {
78
+ metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
79
+ }
78
80
 
79
81
  metricsQueue.set('manual.keep', 'true')
80
82
 
81
- incrementWafInitMetric(wafVersion, rulesInfo.version)
83
+ incrementWafInitMetric(wafVersion, rulesVersion)
82
84
  }
83
85
 
84
86
  function reportMetrics (metrics) {
@@ -51,7 +51,7 @@ function trackWafDurations (metrics, versionsTags) {
51
51
  }
52
52
  }
53
53
 
54
- function getOrCreateMetricTags ({ wafVersion, rulesVersion }, req, versionsTags) {
54
+ function getOrCreateMetricTags (req, versionsTags) {
55
55
  const store = getStore(req)
56
56
 
57
57
  let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
@@ -75,7 +75,7 @@ function updateWafRequestsMetricTags (metrics, req) {
75
75
 
76
76
  trackWafDurations(metrics, versionsTags)
77
77
 
78
- const metricTags = getOrCreateMetricTags(metrics, req, versionsTags)
78
+ const metricTags = getOrCreateMetricTags(req, versionsTags)
79
79
 
80
80
  const { blockTriggered, ruleTriggered, wafTimeout } = metrics
81
81
 
@@ -2,24 +2,35 @@
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
- constructor (ddwafContext, requiredAddresses, wafTimeout, rulesInfo, wafVersion) {
13
+ constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
8
14
  this.ddwafContext = ddwafContext
9
15
  this.requiredAddresses = requiredAddresses
10
16
  this.wafTimeout = wafTimeout
11
- this.rulesVersion = rulesInfo.version
12
17
  this.wafVersion = wafVersion
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,7 +44,9 @@ class WAFContextWrapper {
33
44
 
34
45
  const end = process.hrtime.bigint()
35
46
 
36
- const ruleTriggered = !!result.data && result.data !== '[]'
47
+ this.addressesToSkip = newAddressesToSkip
48
+
49
+ const ruleTriggered = !!result.events?.length
37
50
  const blockTriggered = result.actions?.includes('block')
38
51
 
39
52
  Reporter.reportMetrics({
@@ -47,7 +60,7 @@ class WAFContextWrapper {
47
60
  })
48
61
 
49
62
  if (ruleTriggered) {
50
- Reporter.reportAttack(result.data)
63
+ Reporter.reportAttack(JSON.stringify(result.events))
51
64
  }
52
65
 
53
66
  return result.actions
@@ -12,8 +12,9 @@ class WAFManager {
12
12
  this.wafTimeout = config.wafTimeout
13
13
  this.ddwaf = this._loadDDWAF(rules)
14
14
  this.ddwafVersion = this.ddwaf.constructor.version()
15
+ this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
15
16
 
16
- Reporter.reportWafInit(this.ddwafVersion, this.ddwaf.rulesInfo)
17
+ Reporter.reportWafInit(this.ddwafVersion, this.rulesVersion, this.ddwaf.diagnostics.rules)
17
18
  }
18
19
 
19
20
  _loadDDWAF (rules) {
@@ -38,8 +39,8 @@ class WAFManager {
38
39
  this.ddwaf.createContext(),
39
40
  this.ddwaf.requiredAddresses,
40
41
  this.wafTimeout,
41
- this.ddwaf.rulesInfo,
42
- this.ddwafVersion
42
+ this.ddwafVersion,
43
+ this.rulesVersion
43
44
  )
44
45
  contexts.set(req, wafContext)
45
46
  }
@@ -50,7 +51,7 @@ class WAFManager {
50
51
  update (newRules) {
51
52
  this.ddwaf.update(newRules)
52
53
 
53
- Reporter.reportWafUpdate(this.ddwafVersion, this.ddwaf.rulesInfo.version)
54
+ Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
54
55
  }
55
56
 
56
57
  destroy () {
@@ -28,25 +28,12 @@ function getItrConfiguration ({
28
28
  if (isEvpProxy) {
29
29
  options.path = '/evp_proxy/v2/api/v2/libraries/tests/services/setting'
30
30
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
31
- options.headers['X-Datadog-NeedsAppKey'] = 'true'
32
31
  } else {
33
32
  const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
34
- const appKey = process.env.DATADOG_APP_KEY ||
35
- process.env.DD_APP_KEY ||
36
- process.env.DATADOG_APPLICATION_KEY ||
37
- process.env.DD_APPLICATION_KEY
38
-
39
- const messagePrefix = 'Request to settings endpoint was not done because Datadog'
40
-
41
- if (!appKey) {
42
- return done(new Error(`${messagePrefix} application key is not defined.`))
43
- }
44
33
  if (!apiKey) {
45
- return done(new Error(`${messagePrefix} API key is not defined.`))
34
+ return done(new Error('Request to settings endpoint was not done because Datadog API key is not defined.'))
46
35
  }
47
-
48
36
  options.headers['dd-api-key'] = apiKey
49
- options.headers['dd-application-key'] = appKey
50
37
  }
51
38
 
52
39
  const data = JSON.stringify({
@@ -28,25 +28,13 @@ function getSkippableSuites ({
28
28
  if (isEvpProxy) {
29
29
  options.path = '/evp_proxy/v2/api/v2/ci/tests/skippable'
30
30
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
31
- options.headers['X-Datadog-NeedsAppKey'] = 'true'
32
31
  } else {
33
32
  const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
34
- const appKey = process.env.DATADOG_APP_KEY ||
35
- process.env.DD_APP_KEY ||
36
- process.env.DATADOG_APPLICATION_KEY ||
37
- process.env.DD_APPLICATION_KEY
38
-
39
- const messagePrefix = 'Skippable suites were not fetched because Datadog'
40
-
41
- if (!appKey) {
42
- return done(new Error(`${messagePrefix} application key is not defined.`))
43
- }
44
33
  if (!apiKey) {
45
- return done(new Error(`${messagePrefix} API key is not defined.`))
34
+ return done(new Error('Skippable suites were not fetched because Datadog API key is not defined.'))
46
35
  }
47
36
 
48
37
  options.headers['dd-api-key'] = apiKey
49
- options.headers['dd-application-key'] = appKey
50
38
  }
51
39
 
52
40
  const data = JSON.stringify({
@@ -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 ||
@@ -629,6 +634,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
629
634
 
630
635
  this.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
631
636
 
637
+ // Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
638
+ this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED)
639
+
632
640
  if (this.gitMetadataEnabled) {
633
641
  this.repositoryUrl = coalesce(
634
642
  process.env.DD_GIT_REPOSITORY_URL,
@@ -66,7 +66,9 @@ class DataStreamsProcessor {
66
66
  port,
67
67
  url,
68
68
  env,
69
- tags
69
+ tags,
70
+ version,
71
+ service
70
72
  } = {}) {
71
73
  this.writer = new DataStreamsWriter({
72
74
  hostname,
@@ -79,7 +81,8 @@ class DataStreamsProcessor {
79
81
  this.enabled = dsmEnabled
80
82
  this.env = env
81
83
  this.tags = tags || {}
82
- this.service = this.tags.service || 'unnamed-nodejs-service'
84
+ this.service = service || 'unnamed-nodejs-service'
85
+ this.version = version || ''
83
86
  this.sequence = 0
84
87
 
85
88
  if (this.enabled) {
@@ -96,6 +99,7 @@ class DataStreamsProcessor {
96
99
  Service: this.service,
97
100
  Stats: serialized,
98
101
  TracerVersion: pkg.version,
102
+ Version: this.version,
99
103
  Lang: 'javascript'
100
104
  }
101
105
  this.writer.flush(payload)
@@ -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
@@ -13,7 +14,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
13
14
  const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
14
15
  const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
15
16
  const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
16
- const MEASURED = tags.MEASURED
17
+ const { MEASURED, BASE_SERVICE } = tags
17
18
  const ORIGIN_KEY = constants.ORIGIN_KEY
18
19
  const HOSTNAME_KEY = constants.HOSTNAME_KEY
19
20
  const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
@@ -73,6 +74,13 @@ function extractTags (trace, span) {
73
74
  addTag({}, trace.metrics, MEASURED, 1)
74
75
  }
75
76
 
77
+ const tracerService = span.tracer()._service.toLowerCase()
78
+ if (tags['service.name']?.toLowerCase() !== tracerService) {
79
+ span.setTag(BASE_SERVICE, tracerService)
80
+
81
+ registerExtraService(tags['service.name'])
82
+ }
83
+
76
84
  for (const tag in tags) {
77
85
  switch (tag) {
78
86
  case 'service.name':
@@ -343,10 +343,10 @@ class TextMapPropagator {
343
343
  spanContext._trace.origin = value
344
344
  break
345
345
  case 't.dm': {
346
- const mechanism = -Math.abs(parseInt(value, 10))
346
+ const mechanism = Math.abs(parseInt(value, 10))
347
347
  if (Number.isInteger(mechanism)) {
348
348
  spanContext._sampling.mechanism = mechanism
349
- spanContext._trace.tags['_dd.p.dm'] = String(mechanism)
349
+ spanContext._trace.tags['_dd.p.dm'] = `-${mechanism}`
350
350
  }
351
351
  break
352
352
  }
@@ -26,8 +26,6 @@ class DatadogTracer {
26
26
  this._version = config.version
27
27
  this._env = config.env
28
28
  this._tags = config.tags
29
- this._computePeerService = config.spanComputePeerService
30
- this._peerServiceMapping = config.peerServiceMapping
31
29
  this._logInjection = config.logInjection
32
30
  this._debug = config.debug
33
31
  this._prioritySampler = new PrioritySampler(config.env, config.sampler)
@@ -72,11 +72,10 @@ module.exports = class PluginManager {
72
72
  const Plugin = pluginClasses[name]
73
73
 
74
74
  if (!Plugin) return
75
+ if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
75
76
  if (!this._pluginsByName[name]) {
76
77
  this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
77
78
  }
78
- if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
79
-
80
79
  const pluginConfig = this._configsByName[name] || {
81
80
  enabled: this._tracerConfig.plugins !== false
82
81
  }
@@ -137,7 +136,8 @@ module.exports = class PluginManager {
137
136
  headerTags,
138
137
  dbmPropagationMode,
139
138
  dsmEnabled,
140
- clientIpEnabled
139
+ clientIpEnabled,
140
+ memcachedCommandEnabled
141
141
  } = this._tracerConfig
142
142
 
143
143
  const sharedConfig = {}
@@ -152,6 +152,7 @@ module.exports = class PluginManager {
152
152
 
153
153
  sharedConfig.dbmPropagationMode = dbmPropagationMode
154
154
  sharedConfig.dsmEnabled = dsmEnabled
155
+ sharedConfig.memcachedCommandEnabled = memcachedCommandEnabled
155
156
 
156
157
  if (serviceMapping && serviceMapping[name]) {
157
158
  sharedConfig.service = serviceMapping[name]
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const StoragePlugin = require('./storage')
4
+ const { PEER_SERVICE_KEY } = require('../constants')
4
5
 
5
6
  class DatabasePlugin extends StoragePlugin {
6
7
  static get operation () { return 'query' }
@@ -38,20 +39,29 @@ class DatabasePlugin extends StoragePlugin {
38
39
  `ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
39
40
  }
40
41
 
41
- injectDbmQuery (query, serviceName, isPreparedStatement = false) {
42
+ getDbmServiceName (span, tracerService) {
43
+ if (this._tracerConfig.spanComputePeerService) {
44
+ const peerData = this.getPeerService(span.context()._tags)
45
+ return this.getPeerServiceRemap(peerData)[PEER_SERVICE_KEY] || tracerService
46
+ }
47
+ return tracerService
48
+ }
49
+
50
+ injectDbmQuery (span, query, serviceName, isPreparedStatement = false) {
42
51
  const mode = this.config.dbmPropagationMode
52
+ const dbmService = this.getDbmServiceName(span, serviceName)
43
53
 
44
54
  if (mode === 'disabled') {
45
55
  return query
46
56
  }
47
57
 
48
- const servicePropagation = this.createDBMPropagationCommentService(serviceName)
58
+ const servicePropagation = this.createDBMPropagationCommentService(dbmService)
49
59
 
50
60
  if (isPreparedStatement || mode === 'service') {
51
61
  return `/*${servicePropagation}*/ ${query}`
52
62
  } else if (mode === 'full') {
53
- this.activeSpan.setTag('_dd.dbm_trace_injected', 'true')
54
- const traceparent = this.activeSpan._spanContext.toTraceparent()
63
+ span.setTag('_dd.dbm_trace_injected', 'true')
64
+ const traceparent = span._spanContext.toTraceparent()
55
65
  return `/*${servicePropagation},traceparent='${traceparent}'*/ ${query}`
56
66
  }
57
67
  }
@@ -64,6 +64,7 @@ module.exports = {
64
64
  get 'pg' () { return require('../../../datadog-plugin-pg/src') },
65
65
  get 'pino' () { return require('../../../datadog-plugin-pino/src') },
66
66
  get 'pino-pretty' () { return require('../../../datadog-plugin-pino/src') },
67
+ get 'playwright' () { return require('../../../datadog-plugin-playwright/src') },
67
68
  get 'redis' () { return require('../../../datadog-plugin-redis/src') },
68
69
  get 'restify' () { return require('../../../datadog-plugin-restify/src') },
69
70
  get 'rhea' () { return require('../../../datadog-plugin-rhea/src') },
@@ -64,10 +64,11 @@ class OutboundPlugin extends TracingPlugin {
64
64
  * peer service and add the value we overrode.
65
65
  */
66
66
  const peerService = peerData[PEER_SERVICE_KEY]
67
- if (peerService && this.tracer._peerServiceMapping[peerService]) {
67
+ const mappedService = this._tracerConfig.peerServiceMapping?.[peerService]
68
+ if (peerService && mappedService) {
68
69
  return {
69
70
  ...peerData,
70
- [PEER_SERVICE_KEY]: this.tracer._peerServiceMapping[peerService],
71
+ [PEER_SERVICE_KEY]: mappedService,
71
72
  [PEER_SERVICE_REMAP_KEY]: peerService
72
73
  }
73
74
  }
@@ -80,7 +81,7 @@ class OutboundPlugin extends TracingPlugin {
80
81
  }
81
82
 
82
83
  tagPeerService (span) {
83
- if (this.tracer._computePeerService) {
84
+ if (this._tracerConfig.spanComputePeerService) {
84
85
  const peerData = this.getPeerService(span.context()._tags)
85
86
  if (peerData !== undefined) {
86
87
  span.addTags(this.getPeerServiceRemap(peerData))
@@ -602,6 +602,23 @@ module.exports = {
602
602
  tags[refKey] = ref
603
603
  }
604
604
 
605
+ if (env.CODEBUILD_INITIATOR?.startsWith('codepipeline/')) {
606
+ const {
607
+ CODEBUILD_BUILD_ARN,
608
+ DD_ACTION_EXECUTION_ID,
609
+ DD_PIPELINE_EXECUTION_ID
610
+ } = env
611
+ tags = {
612
+ [CI_PROVIDER_NAME]: 'awscodepipeline',
613
+ [CI_PIPELINE_ID]: DD_PIPELINE_EXECUTION_ID,
614
+ [CI_ENV_VARS]: JSON.stringify({
615
+ CODEBUILD_BUILD_ARN,
616
+ DD_PIPELINE_EXECUTION_ID,
617
+ DD_ACTION_EXECUTION_ID
618
+ })
619
+ }
620
+ }
621
+
605
622
  normalizeTag(tags, CI_WORKSPACE_PATH, resolveTilde)
606
623
  normalizeTag(tags, GIT_REPOSITORY_URL, filterSensitiveInfoFromRepository)
607
624
  normalizeTag(tags, GIT_BRANCH, normalizeRef)
@@ -61,15 +61,37 @@ function unshallowRepository () {
61
61
  }
62
62
  const defaultRemoteName = sanitizedExec('git', ['config', '--default', 'origin', '--get', 'clone.defaultRemoteName'])
63
63
  const revParseHead = sanitizedExec('git', ['rev-parse', 'HEAD'])
64
- sanitizedExec('git', [
64
+
65
+ const baseGitOptions = [
65
66
  'fetch',
66
67
  '--shallow-since="1 month ago"',
67
68
  '--update-shallow',
68
69
  '--filter=blob:none',
69
70
  '--recurse-submodules=no',
70
- defaultRemoteName,
71
- revParseHead
72
- ])
71
+ defaultRemoteName
72
+ ]
73
+
74
+ try {
75
+ execFileSync('git', [
76
+ ...baseGitOptions,
77
+ revParseHead
78
+ ], { stdio: 'pipe' })
79
+ } catch (e) {
80
+ // If the local HEAD is a commit that has not been pushed to the remote, the above command will fail.
81
+ log.error(e)
82
+ const upstreamRemote = sanitizedExec('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'])
83
+ try {
84
+ execFileSync('git', [
85
+ ...baseGitOptions,
86
+ upstreamRemote
87
+ ], { stdio: 'pipe' })
88
+ } catch (e) {
89
+ // If the CI is working on a detached HEAD or branch tracking hasn’t been set up, the above command will fail.
90
+ log.error(e)
91
+ // We use sanitizedExec here because if this last option fails, we'll give up.
92
+ sanitizedExec('git', baseGitOptions)
93
+ }
94
+ }
73
95
  }
74
96
 
75
97
  function getRepositoryUrl () {
@@ -58,6 +58,8 @@ const TEST_ITR_SKIPPING_ENABLED = 'test.itr.tests_skipping.enabled'
58
58
  const TEST_ITR_SKIPPING_TYPE = 'test.itr.tests_skipping.type'
59
59
  const TEST_ITR_SKIPPING_COUNT = 'test.itr.tests_skipping.count'
60
60
  const TEST_CODE_COVERAGE_ENABLED = 'test.code_coverage.enabled'
61
+ const TEST_ITR_UNSKIPPABLE = 'test.itr.unskippable'
62
+ const TEST_ITR_FORCED_RUN = 'test.itr.forced_run'
61
63
 
62
64
  const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
63
65
 
@@ -107,6 +109,8 @@ module.exports = {
107
109
  TEST_ITR_SKIPPING_COUNT,
108
110
  TEST_CODE_COVERAGE_ENABLED,
109
111
  TEST_CODE_COVERAGE_LINES_PCT,
112
+ TEST_ITR_UNSKIPPABLE,
113
+ TEST_ITR_FORCED_RUN,
110
114
  addIntelligentTestRunnerSpanTags,
111
115
  getCoveredFilenamesFromCoverage,
112
116
  resetCoverage,
@@ -366,7 +370,9 @@ function addIntelligentTestRunnerSpanTags (
366
370
  isCodeCoverageEnabled,
367
371
  testCodeCoverageLinesTotal,
368
372
  skippingCount,
369
- skippingType = 'suite'
373
+ skippingType = 'suite',
374
+ hasUnskippableSuites,
375
+ hasForcedToRunSuites
370
376
  }
371
377
  ) {
372
378
  testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
@@ -381,6 +387,15 @@ function addIntelligentTestRunnerSpanTags (
381
387
  testModuleSpan.setTag(TEST_ITR_SKIPPING_COUNT, skippingCount)
382
388
  testModuleSpan.setTag(TEST_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
383
389
 
390
+ if (hasUnskippableSuites) {
391
+ testSessionSpan.setTag(TEST_ITR_UNSKIPPABLE, 'true')
392
+ testModuleSpan.setTag(TEST_ITR_UNSKIPPABLE, 'true')
393
+ }
394
+ if (hasForcedToRunSuites) {
395
+ testSessionSpan.setTag(TEST_ITR_FORCED_RUN, 'true')
396
+ testModuleSpan.setTag(TEST_ITR_FORCED_RUN, 'true')
397
+ }
398
+
384
399
  // If suites have been skipped we don't want to report the total coverage, as it will be wrong
385
400
  if (testCodeCoverageLinesTotal !== undefined && !isSuitesSkipped) {
386
401
  testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal)
@@ -37,6 +37,8 @@ class Config {
37
37
  DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
38
38
  DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
39
39
  DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
40
+ DD_PROFILING_CODEHOTSPOTS_ENABLED,
41
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
40
42
  DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
41
43
  DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED
42
44
  } = process.env
@@ -53,8 +55,6 @@ class Config {
53
55
  Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
54
56
  const sourceMap = coalesce(options.sourceMap,
55
57
  DD_PROFILING_SOURCE_MAP, true)
56
- const endpointCollectionEnabled = coalesce(options.endpointCollection,
57
- DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false)
58
58
  const pprofPrefix = coalesce(options.pprofPrefix,
59
59
  DD_PROFILING_PPROF_PREFIX, '')
60
60
 
@@ -71,11 +71,25 @@ class Config {
71
71
  tagger.parse({ env, host, service, version, functionname })
72
72
  )
73
73
  this.logger = ensureLogger(options.logger)
74
+ const logger = this.logger
75
+ function logExperimentalVarDeprecation (shortVarName) {
76
+ const deprecatedEnvVarName = `DD_PROFILING_EXPERIMENTAL_${shortVarName}`
77
+ const v = process.env[deprecatedEnvVarName]
78
+ // not null, undefined, or NaN -- same logic as koalas.hasValue
79
+ // eslint-disable-next-line no-self-compare
80
+ if (v != null && v === v) {
81
+ logger.warn(`${deprecatedEnvVarName} is deprecated. Use DD_PROFILING_${shortVarName} instead.`)
82
+ }
83
+ }
74
84
  this.flushInterval = flushInterval
75
85
  this.uploadTimeout = uploadTimeout
76
86
  this.sourceMap = sourceMap
77
87
  this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
78
- this.endpointCollectionEnabled = endpointCollectionEnabled
88
+ this.endpointCollectionEnabled = isTrue(coalesce(options.endpointCollection,
89
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
90
+ DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false))
91
+ logExperimentalVarDeprecation('ENDPOINT_COLLECTION_ENABLED')
92
+
79
93
  this.pprofPrefix = pprofPrefix
80
94
  this.v8ProfilerBugWorkaroundEnabled = isTrue(coalesce(options.v8ProfilerBugWorkaround,
81
95
  DD_PROFILING_V8_PROFILER_BUG_WORKAROUND, true))
@@ -113,8 +127,25 @@ class Config {
113
127
  const profilers = options.profilers
114
128
  ? options.profilers
115
129
  : getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
116
- this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
117
- DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, false))
130
+
131
+ function getCodeHotspotsOptionsOr (defvalue) {
132
+ return coalesce(options.codeHotspotsEnabled,
133
+ DD_PROFILING_CODEHOTSPOTS_ENABLED,
134
+ DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, defvalue)
135
+ }
136
+ this.codeHotspotsEnabled = isTrue(getCodeHotspotsOptionsOr(false))
137
+ logExperimentalVarDeprecation('CODEHOTSPOTS_ENABLED')
138
+ if (this.endpointCollectionEnabled && !this.codeHotspotsEnabled) {
139
+ if (getCodeHotspotsOptionsOr(undefined) !== undefined) {
140
+ this.logger.warn(
141
+ 'Endpoint collection is enabled, but Code Hotspots are disabled. ' +
142
+ 'Enable Code Hotspots too for endpoint collection to work.')
143
+ this.endpointCollectionEnabled = false
144
+ } else {
145
+ this.logger.info('Code Hotspots are implicitly enabled by endpoint collection.')
146
+ this.codeHotspotsEnabled = true
147
+ }
148
+ }
118
149
 
119
150
  this.profilers = ensureProfilers(profilers, this)
120
151
  }