dd-trace 4.15.0 → 4.16.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 (38) hide show
  1. package/LICENSE-3rdparty.csv +1 -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 +6 -4
  6. package/packages/datadog-esbuild/index.js +30 -25
  7. package/packages/datadog-instrumentations/src/body-parser.js +2 -2
  8. package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
  9. package/packages/datadog-instrumentations/src/express.js +1 -1
  10. package/packages/datadog-instrumentations/src/graphql.js +5 -0
  11. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  12. package/packages/datadog-instrumentations/src/http/server.js +1 -1
  13. package/packages/datadog-instrumentations/src/next.js +22 -80
  14. package/packages/datadog-instrumentations/src/pg.js +14 -15
  15. package/packages/datadog-instrumentations/src/playwright.js +15 -3
  16. package/packages/datadog-plugin-mysql/src/index.js +2 -2
  17. package/packages/datadog-plugin-next/src/index.js +14 -5
  18. package/packages/datadog-plugin-pg/src/index.js +2 -2
  19. package/packages/dd-trace/src/appsec/channels.js +1 -0
  20. package/packages/dd-trace/src/appsec/index.js +18 -5
  21. package/packages/dd-trace/src/appsec/recommended.json +549 -24
  22. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  23. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  24. package/packages/dd-trace/src/appsec/reporter.js +7 -5
  25. package/packages/dd-trace/src/appsec/telemetry.js +2 -2
  26. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  27. package/packages/dd-trace/src/appsec/waf/waf_manager.js +5 -4
  28. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +1 -14
  29. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -13
  30. package/packages/dd-trace/src/datastreams/processor.js +6 -2
  31. package/packages/dd-trace/src/format.js +6 -1
  32. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
  33. package/packages/dd-trace/src/opentracing/tracer.js +0 -2
  34. package/packages/dd-trace/src/plugin_manager.js +1 -2
  35. package/packages/dd-trace/src/plugins/database.js +14 -4
  36. package/packages/dd-trace/src/plugins/index.js +1 -0
  37. package/packages/dd-trace/src/plugins/outbound.js +4 -3
  38. package/packages/dd-trace/src/telemetry/index.js +10 -1
@@ -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)
@@ -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
 
@@ -4,12 +4,12 @@ const log = require('../../log')
4
4
  const Reporter = require('../reporter')
5
5
 
6
6
  class WAFContextWrapper {
7
- constructor (ddwafContext, requiredAddresses, wafTimeout, rulesInfo, wafVersion) {
7
+ constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
8
8
  this.ddwafContext = ddwafContext
9
9
  this.requiredAddresses = requiredAddresses
10
10
  this.wafTimeout = wafTimeout
11
- this.rulesVersion = rulesInfo.version
12
11
  this.wafVersion = wafVersion
12
+ this.rulesVersion = rulesVersion
13
13
  }
14
14
 
15
15
  run (params) {
@@ -33,7 +33,7 @@ class WAFContextWrapper {
33
33
 
34
34
  const end = process.hrtime.bigint()
35
35
 
36
- const ruleTriggered = !!result.data && result.data !== '[]'
36
+ const ruleTriggered = !!result.events?.length
37
37
  const blockTriggered = result.actions?.includes('block')
38
38
 
39
39
  Reporter.reportMetrics({
@@ -47,7 +47,7 @@ class WAFContextWrapper {
47
47
  })
48
48
 
49
49
  if (ruleTriggered) {
50
- Reporter.reportAttack(result.data)
50
+ Reporter.reportAttack(JSON.stringify(result.events))
51
51
  }
52
52
 
53
53
  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({
@@ -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)
@@ -13,7 +13,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
13
13
  const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
14
14
  const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
15
15
  const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
16
- const MEASURED = tags.MEASURED
16
+ const { MEASURED, BASE_SERVICE } = tags
17
17
  const ORIGIN_KEY = constants.ORIGIN_KEY
18
18
  const HOSTNAME_KEY = constants.HOSTNAME_KEY
19
19
  const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
@@ -73,6 +73,11 @@ function extractTags (trace, span) {
73
73
  addTag({}, trace.metrics, MEASURED, 1)
74
74
  }
75
75
 
76
+ const tracerService = span.tracer()._service.toLowerCase()
77
+ if (tags['service.name']?.toLowerCase() !== tracerService) {
78
+ span.setTag(BASE_SERVICE, tracerService)
79
+ }
80
+
76
81
  for (const tag in tags) {
77
82
  switch (tag) {
78
83
  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
  }
@@ -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))
@@ -57,11 +57,20 @@ function appStarted () {
57
57
  return {
58
58
  integrations: getIntegrations(),
59
59
  dependencies: [],
60
- configuration: flatten(config),
60
+ configuration: flatten(formatConfig(config)),
61
61
  additional_payload: []
62
62
  }
63
63
  }
64
64
 
65
+ function formatConfig (config) {
66
+ // format peerServiceMapping from an object to a string map in order for
67
+ // telemetry intake to accept the configuration
68
+ config.peerServiceMapping = config.peerServiceMapping
69
+ ? Object.entries(config.peerServiceMapping).map(([key, value]) => `${key}:${value}`).join(',')
70
+ : ''
71
+ return config
72
+ }
73
+
65
74
  function onBeforeExit () {
66
75
  process.removeListener('beforeExit', onBeforeExit)
67
76
  sendData(config, application, host, 'app-closing')