dd-trace 5.55.0 → 5.56.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 (24) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/package.json +5 -4
  3. package/packages/datadog-core/src/utils/src/set.js +8 -10
  4. package/packages/datadog-instrumentations/src/jest.js +396 -320
  5. package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
  6. package/packages/datadog-plugin-oracledb/src/index.js +2 -1
  7. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  8. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
  9. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
  10. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
  11. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
  12. package/packages/dd-trace/src/appsec/index.js +12 -1
  13. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  14. package/packages/dd-trace/src/baggage.js +2 -2
  15. package/packages/dd-trace/src/config.js +58 -52
  16. package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
  17. package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
  18. package/packages/dd-trace/src/exporters/agent/writer.js +3 -1
  19. package/packages/dd-trace/src/opentracing/propagation/text_map.js +18 -24
  20. package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
  21. package/packages/dd-trace/src/supported-configurations.json +1 -0
  22. package/packages/dd-trace/src/telemetry/telemetry.js +6 -3
  23. package/packages/datadog-core/src/utils/src/get.js +0 -11
  24. package/packages/datadog-core/src/utils/src/has.js +0 -14
@@ -19,14 +19,15 @@ class AzureFunctionsPlugin extends TracingPlugin {
19
19
  static get operation () { return 'invoke' }
20
20
  static get kind () { return 'server' }
21
21
  static get type () { return 'serverless' }
22
-
23
22
  static get prefix () { return 'tracing:datadog:azure:functions:invoke' }
24
23
 
25
24
  bindStart (ctx) {
26
- const { functionName, methodName } = ctx
25
+ const { functionName, methodName, httpRequest } = ctx
27
26
  const store = storage('legacy').getStore()
28
-
27
+ // httpRequest.headers is a map
28
+ const childOf = this._tracer.extract('http_headers', Object.fromEntries(httpRequest.headers))
29
29
  const span = this.startSpan(this.operationName(), {
30
+ childOf,
30
31
  service: this.serviceName(),
31
32
  type: 'serverless',
32
33
  meta: {
@@ -52,7 +53,7 @@ class AzureFunctionsPlugin extends TracingPlugin {
52
53
  const path = (new URL(httpRequest.url)).pathname
53
54
  const req = {
54
55
  method: httpRequest.method,
55
- headers: Object.fromEntries(httpRequest.headers.entries()),
56
+ headers: Object.fromEntries(httpRequest.headers),
56
57
  url: path
57
58
  }
58
59
 
@@ -11,7 +11,8 @@ class OracledbPlugin extends DatabasePlugin {
11
11
 
12
12
  start ({ query, connAttrs }) {
13
13
  const service = this.serviceName({ pluginConfig: this.config, params: connAttrs })
14
- const url = getUrl(connAttrs.connectString)
14
+ // Users can pass either connectString or connectionString
15
+ const url = getUrl(connAttrs.connectString || connAttrs.connectionString)
15
16
 
16
17
  this.startSpan(this.operationName(), {
17
18
  service,
@@ -15,7 +15,7 @@ class SqlInjectionAnalyzer extends StoredInjectionAnalyzer {
15
15
 
16
16
  onConfigure () {
17
17
  this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
18
- this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
18
+ this.addSub('datadog:mysql2:outerquery:start', ({ sql }) => this.analyze(sql, undefined, 'MYSQL'))
19
19
  this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, undefined, 'POSTGRES'))
20
20
 
21
21
  this.addSub(
@@ -85,7 +85,7 @@ function hookModule (filename, module, controlsByFile) {
85
85
  }
86
86
  })
87
87
  } catch (e) {
88
- log.error('[ASM] Error initializing IAST security control for %', filename, e)
88
+ log.error('[ASM] Error initializing IAST security control for %s', filename, e)
89
89
  }
90
90
 
91
91
  return module
@@ -2,8 +2,11 @@
2
2
 
3
3
  const TaintedUtils = require('@datadog/native-iast-taint-tracking')
4
4
  const { IAST_TRANSACTION_ID } = require('../iast-context')
5
+ const { HTTP_REQUEST_PARAMETER } = require('./source-types')
5
6
  const log = require('../../../log')
6
7
 
8
+ const SEPARATOR = '\u0000' // Unit Separator (cannot be in URL keys)
9
+
7
10
  function taintObject (iastContext, object, type) {
8
11
  let result = object
9
12
  const transactionId = iastContext?.[IAST_TRANSACTION_ID]
@@ -40,6 +43,46 @@ function taintObject (iastContext, object, type) {
40
43
  return result
41
44
  }
42
45
 
46
+ function taintQueryWithCache (iastContext, query) {
47
+ const transactionId = iastContext?.[IAST_TRANSACTION_ID]
48
+ if (!transactionId || !query) return query
49
+
50
+ iastContext.queryCache ??= new Map() // key: "a.b.c", value: tainted string
51
+
52
+ traverseAndTaint(query, '', iastContext.queryCache, transactionId)
53
+ return query
54
+ }
55
+
56
+ function traverseAndTaint (node, path, cache, transactionId) {
57
+ if (node == null) return node
58
+
59
+ if (typeof node === 'string') {
60
+ const cachedValue = cache.get(path)
61
+
62
+ if (cachedValue === node) {
63
+ return cachedValue
64
+ }
65
+
66
+ const tainted = TaintedUtils.newTaintedString(transactionId, node, path, HTTP_REQUEST_PARAMETER)
67
+ cache.set(path, tainted)
68
+
69
+ return tainted
70
+ }
71
+
72
+ if (typeof node === 'object') {
73
+ const keys = Array.isArray(node) ? node.keys() : Object.keys(node)
74
+
75
+ for (const key of keys) {
76
+ const childPath = path ? `${path}${SEPARATOR}${key}` : String(key)
77
+ const tainted = traverseAndTaint(node[key], childPath, cache, transactionId)
78
+ node[key] = tainted
79
+ }
80
+ }
81
+
82
+ return node
83
+ }
84
+
43
85
  module.exports = {
44
- taintObject
86
+ taintObject,
87
+ taintQueryWithCache
45
88
  }
@@ -11,7 +11,7 @@ const {
11
11
  getTaintTrackingNoop,
12
12
  lodashTaintTrackingHandler
13
13
  } = require('./taint-tracking-impl')
14
- const { taintObject } = require('./operations-taint-object')
14
+ const { taintObject, taintQueryWithCache } = require('./operations-taint-object')
15
15
 
16
16
  const lodashOperationCh = dc.channel('datadog:lodash:operation')
17
17
 
@@ -98,6 +98,7 @@ module.exports = {
98
98
  newTaintedString,
99
99
  newTaintedObject,
100
100
  taintObject,
101
+ taintQueryWithCache,
101
102
  isTainted,
102
103
  getRanges,
103
104
  enableTaintOperations,
@@ -3,7 +3,7 @@
3
3
  const { SourceIastPlugin } = require('../iast-plugin')
4
4
  const { getIastContext } = require('../iast-context')
5
5
  const { storage } = require('../../../../../datadog-core')
6
- const { taintObject, newTaintedString, getRanges } = require('./operations')
6
+ const { taintObject, newTaintedString, getRanges, taintQueryWithCache } = require('./operations')
7
7
  const {
8
8
  HTTP_REQUEST_BODY,
9
9
  HTTP_REQUEST_COOKIE_VALUE,
@@ -63,7 +63,12 @@ class TaintTrackingPlugin extends SourceIastPlugin {
63
63
 
64
64
  this.addSub(
65
65
  { channelName: 'datadog:express:query:finish', tag: HTTP_REQUEST_PARAMETER },
66
- ({ query }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
66
+ ({ query }) => {
67
+ const iastContext = getIastContext(storage('legacy').getStore())
68
+ if (!iastContext || !query) return
69
+
70
+ taintQueryWithCache(iastContext, query)
71
+ }
67
72
  )
68
73
 
69
74
  this.addSub(
@@ -37,6 +37,7 @@ const rasp = require('./rasp')
37
37
  const { isInServerlessEnvironment } = require('../serverless')
38
38
 
39
39
  const responseAnalyzedSet = new WeakSet()
40
+ const storedResponseHeaders = new WeakMap()
40
41
 
41
42
  let isEnabled = false
42
43
  let config
@@ -187,7 +188,13 @@ function incomingHttpEndTranslator ({ req, res }) {
187
188
 
188
189
  waf.disposeContext(req)
189
190
 
190
- Reporter.finishRequest(req, res)
191
+ const storedHeaders = storedResponseHeaders.get(req) || {}
192
+
193
+ Reporter.finishRequest(req, res, storedHeaders)
194
+
195
+ if (storedHeaders) {
196
+ storedResponseHeaders.delete(req)
197
+ }
191
198
  }
192
199
 
193
200
  function onPassportVerify ({ framework, login, user, success, abortController }) {
@@ -285,6 +292,10 @@ function onResponseBody ({ req, res, body }) {
285
292
  }
286
293
 
287
294
  function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
295
+ if (Object.keys(responseHeaders).length) {
296
+ storedResponseHeaders.set(req, responseHeaders)
297
+ }
298
+
288
299
  // avoid "write after end" error
289
300
  if (isBlocked(res)) {
290
301
  abortController?.abort()
@@ -140,14 +140,16 @@ function filterExtendedHeaders (headers, excludedHeaderNames, tagPrefix, limit =
140
140
  return result
141
141
  }
142
142
 
143
- function getCollectedHeaders (req, res, shouldCollectEventHeaders) {
143
+ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedResponseHeaders = {}) {
144
144
  // Mandatory
145
145
  const mandatoryCollectedHeaders = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
146
146
 
147
147
  // Basic collection
148
148
  if (!shouldCollectEventHeaders) return mandatoryCollectedHeaders
149
149
 
150
- const responseHeaders = res.getHeaders()
150
+ const responseHeaders = Object.keys(storedResponseHeaders).length === 0
151
+ ? res.getHeaders()
152
+ : { ...storedResponseHeaders, ...res.getHeaders() }
151
153
 
152
154
  const requestEventCollectedHeaders = filterHeaders(req.headers, EVENT_HEADERS_MAP)
153
155
  const responseEventCollectedHeaders = filterHeaders(responseHeaders, RESPONSE_HEADERS_MAP)
@@ -399,7 +401,7 @@ function reportDerivatives (derivatives) {
399
401
  rootSpan.addTags(tags)
400
402
  }
401
403
 
402
- function finishRequest (req, res) {
404
+ function finishRequest (req, res, storedResponseHeaders) {
403
405
  const rootSpan = web.root(req)
404
406
  if (!rootSpan) return
405
407
 
@@ -453,7 +455,7 @@ function finishRequest (req, res) {
453
455
 
454
456
  const tags = rootSpan.context()._tags
455
457
 
456
- const newTags = getCollectedHeaders(req, res, shouldCollectEventHeaders(tags))
458
+ const newTags = getCollectedHeaders(req, res, shouldCollectEventHeaders(tags), storedResponseHeaders)
457
459
 
458
460
  if (tags['appsec.event'] === 'true' && typeof req.route?.path === 'string') {
459
461
  newTags['http.endpoint'] = req.route.path
@@ -13,7 +13,7 @@ function getBaggageItem (key) {
13
13
  }
14
14
 
15
15
  function getAllBaggageItems () {
16
- return storage('baggage').getStore()
16
+ return storage('baggage').getStore() ?? {}
17
17
  }
18
18
 
19
19
  function removeBaggageItem (keyToRemove) {
@@ -23,7 +23,7 @@ function removeBaggageItem (keyToRemove) {
23
23
  }
24
24
 
25
25
  function removeAllBaggageItems () {
26
- storage('baggage').enterWith({})
26
+ storage('baggage').enterWith()
27
27
  return storage('baggage').getStore()
28
28
  }
29
29
 
@@ -8,8 +8,6 @@ const log = require('./log')
8
8
  const pkg = require('./pkg')
9
9
  const coalesce = require('koalas')
10
10
  const tagger = require('./tagger')
11
- const get = require('../../datadog-core/src/utils/src/get')
12
- const has = require('../../datadog-core/src/utils/src/has')
13
11
  const set = require('../../datadog-core/src/utils/src/set')
14
12
  const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('./util')
15
13
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
@@ -23,6 +21,8 @@ const { getEnvironmentVariable, getEnvironmentVariables } = require('./config-he
23
21
 
24
22
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
25
23
 
24
+ const changeTracker = {}
25
+
26
26
  const telemetryCounters = {
27
27
  'otel.env.hiding': {},
28
28
  'otel.env.invalid': {}
@@ -231,6 +231,16 @@ function reformatSpanSamplingRules (rules) {
231
231
  })
232
232
  }
233
233
 
234
+ const sourcesOrder = [
235
+ { containerProperty: '_remote', origin: 'remote_config', unprocessedProperty: '_remoteUnprocessed' },
236
+ { containerProperty: '_options', origin: 'code', unprocessedProperty: '_optsUnprocessed' },
237
+ { containerProperty: '_fleetStableConfig', origin: 'fleet_stable_config' },
238
+ { containerProperty: '_env', origin: 'env_var', unprocessedProperty: '_envUnprocessed' },
239
+ { containerProperty: '_localStableConfig', origin: 'local_stable_config' },
240
+ { containerProperty: '_calculated', origin: 'calculated' },
241
+ { containerProperty: '_defaults', origin: 'default' }
242
+ ]
243
+
234
244
  class Config {
235
245
  constructor (options = {}) {
236
246
  if (!isInServerlessEnvironment()) {
@@ -487,6 +497,7 @@ class Config {
487
497
  this._setValue(defaults, 'dynamicInstrumentation.enabled', false)
488
498
  this._setValue(defaults, 'dynamicInstrumentation.redactedIdentifiers', [])
489
499
  this._setValue(defaults, 'dynamicInstrumentation.redactionExcludedIdentifiers', [])
500
+ this._setValue(defaults, 'dynamicInstrumentation.uploadIntervalSeconds', 1)
490
501
  this._setValue(defaults, 'env')
491
502
  this._setValue(defaults, 'experimental.enableGetRumData', false)
492
503
  this._setValue(defaults, 'experimental.exporter')
@@ -670,6 +681,7 @@ class Config {
670
681
  DD_DYNAMIC_INSTRUMENTATION_ENABLED,
671
682
  DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS,
672
683
  DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS,
684
+ DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS,
673
685
  DD_ENV,
674
686
  DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
675
687
  DD_PROFILING_ENABLED,
@@ -843,6 +855,12 @@ class Config {
843
855
  'dynamicInstrumentation.redactionExcludedIdentifiers',
844
856
  DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS
845
857
  )
858
+ this._setValue(
859
+ env,
860
+ 'dynamicInstrumentation.uploadIntervalSeconds',
861
+ maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
862
+ )
863
+ this._envUnprocessed['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
846
864
  this._setString(env, 'env', DD_ENV || tags.env)
847
865
  this._setBoolean(env, 'traceEnabled', DD_TRACE_ENABLED)
848
866
  this._setBoolean(env, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED)
@@ -1072,6 +1090,13 @@ class Config {
1072
1090
  'dynamicInstrumentation.redactionExcludedIdentifiers',
1073
1091
  options.dynamicInstrumentation?.redactionExcludedIdentifiers
1074
1092
  )
1093
+ this._setValue(
1094
+ opts,
1095
+ 'dynamicInstrumentation.uploadIntervalSeconds',
1096
+ maybeFloat(options.dynamicInstrumentation?.uploadIntervalSeconds)
1097
+ )
1098
+ this._optsUnprocessed['dynamicInstrumentation.uploadIntervalSeconds'] =
1099
+ options.dynamicInstrumentation?.uploadIntervalSeconds
1075
1100
  this._setString(opts, 'env', options.env || tags.env)
1076
1101
  this._setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
1077
1102
  this._setString(opts, 'experimental.exporter', options.experimental?.exporter)
@@ -1436,27 +1461,24 @@ class Config {
1436
1461
  obj[name] = value
1437
1462
  }
1438
1463
 
1439
- _getContainersAndOriginsOrdered () {
1440
- const containers = [
1441
- this._remote,
1442
- this._options,
1443
- this._fleetStableConfig,
1444
- this._env,
1445
- this._localStableConfig,
1446
- this._calculated,
1447
- this._defaults
1448
- ]
1449
- const origins = [
1450
- 'remote_config',
1451
- 'code',
1452
- 'fleet_stable_config',
1453
- 'env_var',
1454
- 'local_stable_config',
1455
- 'calculated',
1456
- 'default'
1457
- ]
1458
-
1459
- return { containers, origins }
1464
+ _setAndTrackChange ({ name, value, origin, unprocessedValue, changes }) {
1465
+ set(this, name, value)
1466
+
1467
+ if (!changeTracker[name]) {
1468
+ changeTracker[name] = {}
1469
+ }
1470
+
1471
+ const originExists = origin in changeTracker[name]
1472
+ const oldValue = changeTracker[name][origin]
1473
+
1474
+ if (!originExists || oldValue !== value) {
1475
+ changeTracker[name][origin] = value
1476
+ changes.push({
1477
+ name,
1478
+ value: unprocessedValue || value,
1479
+ origin
1480
+ })
1481
+ }
1460
1482
  }
1461
1483
 
1462
1484
  // TODO: Report origin changes and errors to telemetry.
@@ -1465,51 +1487,35 @@ class Config {
1465
1487
  // for telemetry reporting, `name`s in `containers` need to be keys from:
1466
1488
  // https://github.com/DataDog/dd-go/blob/prod/trace/apps/tracer-telemetry-intake/telemetry-payload/static/config_norm_rules.json
1467
1489
  _merge () {
1468
- const { containers, origins } = this._getContainersAndOriginsOrdered()
1469
- const unprocessedValues = [
1470
- this._remoteUnprocessed,
1471
- this._optsUnprocessed,
1472
- {},
1473
- this._envUnprocessed,
1474
- {},
1475
- {},
1476
- {}
1477
- ]
1478
1490
  const changes = []
1479
1491
 
1480
1492
  for (const name in this._defaults) {
1481
- for (let i = 0; i < containers.length; i++) {
1482
- const container = containers[i]
1493
+ // Use reverse order for merge (lowest priority first)
1494
+ for (let i = sourcesOrder.length - 1; i >= 0; i--) {
1495
+ const { containerProperty, origin, unprocessedProperty } = sourcesOrder[i]
1496
+ const container = this[containerProperty]
1483
1497
  const value = container[name]
1484
-
1485
- if ((value !== null && value !== undefined) || container === this._defaults) {
1486
- if (get(this, name) === value && has(this, name)) break
1487
-
1488
- set(this, name, value)
1489
-
1490
- changes.push({
1498
+ if (value != null || container === this._defaults) {
1499
+ this._setAndTrackChange({
1491
1500
  name,
1492
- value: unprocessedValues[i][name] || value,
1493
- origin: origins[i]
1501
+ value,
1502
+ origin,
1503
+ unprocessedValue: unprocessedProperty === undefined ? undefined : this[unprocessedProperty][name],
1504
+ changes
1494
1505
  })
1495
-
1496
- break
1497
1506
  }
1498
1507
  }
1499
1508
  }
1500
-
1501
1509
  this.sampler.sampleRate = this.sampleRate
1502
1510
  updateConfig(changes, this)
1503
1511
  }
1504
1512
 
1505
1513
  getOrigin (name) {
1506
- const { containers, origins } = this._getContainersAndOriginsOrdered()
1507
-
1508
- for (let i = 0; i < containers.length; i++) {
1509
- const container = containers[i]
1514
+ for (const { containerProperty, origin } of sourcesOrder) {
1515
+ const container = this[containerProperty]
1510
1516
  const value = container[name]
1511
1517
  if (value != null || container === this._defaults) {
1512
- return origins[i]
1518
+ return origin
1513
1519
  }
1514
1520
  }
1515
1521
  }
@@ -32,7 +32,11 @@ const ddtags = [
32
32
 
33
33
  const path = `/debugger/v1/input?${stringify({ ddtags })}`
34
34
 
35
- const jsonBuffer = new JSONBuffer({ size: config.maxTotalPayloadSize, timeout: 1000, onFlush })
35
+ const jsonBuffer = new JSONBuffer({
36
+ size: config.maxTotalPayloadSize,
37
+ timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
38
+ onFlush
39
+ })
36
40
 
37
41
  function send (message, logger, dd, snapshot) {
38
42
  const payload = {
@@ -20,7 +20,11 @@ const runtimeId = config.runtimeId
20
20
 
21
21
  const cache = new TTLSet(60 * 60 * 1000) // 1 hour
22
22
 
23
- const jsonBuffer = new JSONBuffer({ size: config.maxTotalPayloadSize, timeout: 1000, onFlush })
23
+ const jsonBuffer = new JSONBuffer({
24
+ size: config.maxTotalPayloadSize,
25
+ timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
26
+ onFlush
27
+ })
24
28
 
25
29
  const STATUSES = {
26
30
  RECEIVED: 'RECEIVED',
@@ -69,7 +69,9 @@ function setHeader (headers, key, value) {
69
69
  }
70
70
 
71
71
  function getEncoder (protocolVersion) {
72
- return require(`../../encode/${protocolVersion === '0.5' ? '0.5' : '0.4'}`).AgentEncoder
72
+ return protocolVersion === '0.5'
73
+ ? require('../../encode/0.5').AgentEncoder
74
+ : require('../../encode/0.4').AgentEncoder
73
75
  }
74
76
 
75
77
  function makeRequest (version, data, count, url, headers, lookup, needsStartupLog, cb) {
@@ -136,7 +136,7 @@ class TextMapPropagator {
136
136
  let itemCounter = 0
137
137
  let byteCounter = 0
138
138
 
139
- const baggageItems = spanContext ? spanContext._baggageItems : getAllBaggageItems()
139
+ const baggageItems = getAllBaggageItems()
140
140
  if (!baggageItems) return
141
141
  for (const [key, value] of Object.entries(baggageItems)) {
142
142
  const item = `${this._encodeOtelBaggageKey(String(key).trim())}=${encodeURIComponent(String(value).trim())},`
@@ -355,19 +355,20 @@ class TextMapPropagator {
355
355
  }
356
356
  }
357
357
 
358
- this._extractBaggageItems(carrier, context)
359
-
360
358
  if (this._config.tracePropagationBehaviorExtract === 'ignore') {
361
359
  context._links = []
362
- } else if (this._config.tracePropagationBehaviorExtract === 'restart') {
363
- context._links = []
364
- context._links.push({
365
- context,
366
- attributes:
367
- {
368
- reason: 'propagation_behavior_extract', context_headers: style
369
- }
370
- })
360
+ } else {
361
+ if (this._config.tracePropagationBehaviorExtract === 'restart') {
362
+ context._links = []
363
+ context._links.push({
364
+ context,
365
+ attributes:
366
+ {
367
+ reason: 'propagation_behavior_extract', context_headers: style
368
+ }
369
+ })
370
+ }
371
+ this._extractBaggageItems(carrier, context)
371
372
  }
372
373
 
373
374
  return context || this._extractSqsdContext(carrier)
@@ -628,33 +629,26 @@ class TextMapPropagator {
628
629
  _extractBaggageItems (carrier, spanContext) {
629
630
  if (!this._hasPropagationStyle('extract', 'baggage')) return
630
631
  if (!carrier || !carrier.baggage) return
631
- if (!spanContext) removeAllBaggageItems()
632
632
  const baggages = carrier.baggage.split(',')
633
633
  const keysToSpanTag = this._config.baggageTagKeys === '*'
634
634
  ? undefined
635
635
  : new Set(this._config.baggageTagKeys.split(','))
636
636
  for (const keyValue of baggages) {
637
637
  if (!keyValue.includes('=')) {
638
- if (spanContext) spanContext._baggageItems = {}
638
+ removeAllBaggageItems()
639
639
  return
640
640
  }
641
641
  let [key, value] = keyValue.split('=')
642
642
  key = this._decodeOtelBaggageKey(key.trim())
643
643
  value = decodeURIComponent(value.trim())
644
644
  if (!key || !value) {
645
- if (spanContext) spanContext._baggageItems = {}
645
+ removeAllBaggageItems()
646
646
  return
647
647
  }
648
- // the current code assumes precedence of ot-baggage- (legacy opentracing baggage) over baggage
649
- if (spanContext) {
650
- if (Object.hasOwn(spanContext._baggageItems, key)) continue
651
- spanContext._baggageItems[key] = value
652
- if (this._config.baggageTagKeys === '*' || keysToSpanTag.has(key)) {
653
- spanContext._trace.tags['baggage.' + key] = value
654
- }
655
- } else {
656
- setBaggageItem(key, value)
648
+ if (spanContext && (this._config.baggageTagKeys === '*' || keysToSpanTag.has(key))) {
649
+ spanContext._trace.tags['baggage.' + key] = value
657
650
  }
651
+ setBaggageItem(key, value)
658
652
  }
659
653
  }
660
654
 
@@ -291,8 +291,16 @@ class NodeApiEventSource {
291
291
 
292
292
  class DatadogInstrumentationEventSource {
293
293
  constructor (eventHandler, eventFilter) {
294
- this.plugins = ['dns_lookup', 'dns_lookupservice', 'dns_resolve', 'dns_reverse', 'fs', 'net'].map(m => {
295
- const Plugin = require(`./event_plugins/${m}`)
294
+ // List all entries explicitly for bundlers to pick up the require calls correctly.
295
+ const plugins = [
296
+ require('./event_plugins/dns_lookup'),
297
+ require('./event_plugins/dns_lookupservice'),
298
+ require('./event_plugins/dns_resolve'),
299
+ require('./event_plugins/dns_reverse'),
300
+ require('./event_plugins/fs'),
301
+ require('./event_plugins/net')
302
+ ]
303
+ this.plugins = plugins.map((Plugin) => {
296
304
  return new Plugin(eventHandler, eventFilter)
297
305
  })
298
306
 
@@ -56,6 +56,7 @@
56
56
  "DD_DYNAMIC_INSTRUMENTATION_ENABLED": ["A"],
57
57
  "DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS": ["A"],
58
58
  "DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS": ["A"],
59
+ "DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS": ["A"],
59
60
  "DD_ENV": ["A"],
60
61
  "DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED": ["A"],
61
62
  "DD_EXTERNAL_ENV": ["A"],
@@ -28,6 +28,8 @@ const extendedHeartbeatPayload = {}
28
28
 
29
29
  const sentIntegrations = new Set()
30
30
 
31
+ let seqId = 0
32
+
31
33
  function getRetryData () {
32
34
  return retryData
33
35
  }
@@ -337,9 +339,8 @@ function updateConfig (changes, config) {
337
339
 
338
340
  for (const change of changes) {
339
341
  const name = nameMapping[change.name] || change.name
340
-
341
342
  const { origin, value } = change
342
- const entry = { name, value, origin }
343
+ const entry = { name, value, origin, seq_id: seqId++ }
343
344
 
344
345
  if (namesNeedFormatting.has(entry.name)) {
345
346
  entry.value = formatMapForTelemetry(entry.value)
@@ -352,7 +353,9 @@ function updateConfig (changes, config) {
352
353
  } else if (Array.isArray(entry.value)) {
353
354
  entry.value = value.join(',')
354
355
  }
355
- configWithOrigin.set(name, entry)
356
+
357
+ // Use composite key to support multiple origins for same config name
358
+ configWithOrigin.set(`${name}|${origin}`, entry)
356
359
  }
357
360
 
358
361
  if (changed) {
@@ -1,11 +0,0 @@
1
- 'use strict'
2
-
3
- module.exports = function get (object, path) {
4
- const pathArr = path.split('.')
5
- let val = object
6
- for (const p of pathArr) {
7
- if (val === undefined) return val
8
- val = val[p]
9
- }
10
- return val
11
- }
@@ -1,14 +0,0 @@
1
- 'use strict'
2
-
3
- module.exports = function has (object, path) {
4
- const pathArr = path.split('.')
5
- let property = object
6
- for (const n of pathArr) {
7
- if (property.hasOwnProperty(n)) {
8
- property = property[n]
9
- } else {
10
- return false
11
- }
12
- }
13
- return true
14
- }