dd-trace 5.17.0 → 5.19.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 (93) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/ext/exporters.d.ts +1 -1
  3. package/index.d.ts +105 -37
  4. package/init.js +40 -1
  5. package/initialize.mjs +8 -5
  6. package/package.json +29 -29
  7. package/packages/datadog-core/src/storage/index.js +1 -10
  8. package/packages/datadog-esbuild/index.js +5 -1
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
  10. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  11. package/packages/datadog-instrumentations/src/cucumber.js +76 -34
  12. package/packages/datadog-instrumentations/src/fs.js +1 -1
  13. package/packages/datadog-instrumentations/src/hapi.js +1 -1
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
  17. package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
  18. package/packages/datadog-instrumentations/src/http/client.js +1 -1
  19. package/packages/datadog-instrumentations/src/jest.js +17 -2
  20. package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
  21. package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
  22. package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
  23. package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
  24. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
  25. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  26. package/packages/datadog-instrumentations/src/next.js +1 -1
  27. package/packages/datadog-instrumentations/src/pg.js +2 -2
  28. package/packages/datadog-instrumentations/src/playwright.js +47 -33
  29. package/packages/datadog-instrumentations/src/restify.js +1 -1
  30. package/packages/datadog-instrumentations/src/vitest.js +349 -0
  31. package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  34. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
  36. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  37. package/packages/datadog-plugin-child_process/src/index.js +1 -1
  38. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
  39. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  40. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +79 -42
  41. package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
  42. package/packages/datadog-plugin-fs/src/index.js +1 -1
  43. package/packages/datadog-plugin-jest/src/index.js +7 -1
  44. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  45. package/packages/datadog-plugin-mocha/src/index.js +25 -4
  46. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
  47. package/packages/datadog-plugin-openai/src/index.js +57 -35
  48. package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
  49. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  50. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  51. package/packages/datadog-plugin-vitest/src/index.js +167 -0
  52. package/packages/dd-trace/src/analytics_sampler.js +1 -1
  53. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
  54. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
  55. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  57. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
  58. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
  59. package/packages/dd-trace/src/appsec/index.js +4 -4
  60. package/packages/dd-trace/src/appsec/passport.js +1 -1
  61. package/packages/dd-trace/src/appsec/rasp.js +32 -5
  62. package/packages/dd-trace/src/appsec/recommended.json +208 -3
  63. package/packages/dd-trace/src/appsec/reporter.js +60 -20
  64. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
  65. package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
  66. package/packages/dd-trace/src/appsec/standalone.js +130 -0
  67. package/packages/dd-trace/src/appsec/telemetry.js +33 -1
  68. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  69. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -3
  70. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  71. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  72. package/packages/dd-trace/src/config.js +136 -63
  73. package/packages/dd-trace/src/constants.js +3 -1
  74. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  75. package/packages/dd-trace/src/exporters/agent/index.js +2 -2
  76. package/packages/dd-trace/src/format.js +1 -0
  77. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  78. package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
  79. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
  80. package/packages/dd-trace/src/opentracing/span.js +4 -1
  81. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  82. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
  83. package/packages/dd-trace/src/plugins/index.js +2 -0
  84. package/packages/dd-trace/src/plugins/util/test.js +5 -1
  85. package/packages/dd-trace/src/priority_sampler.js +2 -5
  86. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  87. package/packages/dd-trace/src/proxy.js +3 -1
  88. package/packages/dd-trace/src/rate_limiter.js +2 -2
  89. package/packages/dd-trace/src/span_stats.js +4 -3
  90. package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
  91. package/packages/dd-trace/src/tracer.js +2 -2
  92. package/packages/dd-trace/src/util.js +6 -1
  93. package/packages/datadog-core/src/storage/async_hooks.js +0 -49
@@ -26,50 +26,104 @@ const telemetryCounters = {
26
26
  'otel.env.invalid': {}
27
27
  }
28
28
 
29
- function getCounter (event, ddVar, otelVar, otelTracesSamplerArg) {
29
+ function getCounter (event, ddVar, otelVar) {
30
30
  const counters = telemetryCounters[event]
31
31
  const tags = []
32
+ const ddVarPrefix = 'config.datadog:'
33
+ const otelVarPrefix = 'config.opentelemetry:'
34
+ if (ddVar) {
35
+ ddVar = ddVarPrefix + ddVar
36
+ tags.push(ddVar)
37
+ }
38
+ if (otelVar) {
39
+ otelVar = otelVarPrefix + otelVar
40
+ tags.push(otelVar)
41
+ }
32
42
 
33
- if (ddVar) tags.push(ddVar)
34
- if (otelVar) tags.push(otelVar)
35
- if (otelTracesSamplerArg) tags.push(otelTracesSamplerArg)
36
-
37
- if (!(ddVar in counters)) counters[ddVar] = {}
43
+ if (!(otelVar in counters)) counters[otelVar] = {}
38
44
 
39
45
  const counter = tracerMetrics.count(event, tags)
40
- counters[ddVar][otelVar] = counter
46
+ counters[otelVar][ddVar] = counter
41
47
  return counter
42
48
  }
43
49
 
44
50
  const otelDdEnvMapping = {
45
- DD_TRACE_LOG_LEVEL: 'OTEL_LOG_LEVEL',
46
- DD_TRACE_PROPAGATION_STYLE: 'OTEL_PROPAGATORS',
47
- DD_SERVICE: 'OTEL_SERVICE_NAME',
48
- DD_TRACE_SAMPLE_RATE: 'OTEL_TRACES_SAMPLER',
49
- DD_TRACE_ENABLED: 'OTEL_TRACES_EXPORTER',
50
- DD_RUNTIME_METRICS_ENABLED: 'OTEL_METRICS_EXPORTER',
51
- DD_TAGS: 'OTEL_RESOURCE_ATTRIBUTES',
52
- DD_TRACE_OTEL_ENABLED: 'OTEL_SDK_DISABLED'
51
+ OTEL_LOG_LEVEL: 'DD_TRACE_LOG_LEVEL',
52
+ OTEL_PROPAGATORS: 'DD_TRACE_PROPAGATION_STYLE',
53
+ OTEL_SERVICE_NAME: 'DD_SERVICE',
54
+ OTEL_TRACES_SAMPLER: 'DD_TRACE_SAMPLE_RATE',
55
+ OTEL_TRACES_SAMPLER_ARG: 'DD_TRACE_SAMPLE_RATE',
56
+ OTEL_TRACES_EXPORTER: 'DD_TRACE_ENABLED',
57
+ OTEL_METRICS_EXPORTER: 'DD_RUNTIME_METRICS_ENABLED',
58
+ OTEL_RESOURCE_ATTRIBUTES: 'DD_TAGS',
59
+ OTEL_SDK_DISABLED: 'DD_TRACE_OTEL_ENABLED',
60
+ OTEL_LOGS_EXPORTER: undefined
53
61
  }
54
62
 
55
- const otelInvalidEnv = ['OTEL_LOGS_EXPORTER']
63
+ const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none'])
56
64
 
57
- function checkIfBothOtelAndDdEnvVarSet () {
58
- for (const [ddVar, otelVar] of Object.entries(otelDdEnvMapping)) {
59
- if (process.env[ddVar] && process.env[otelVar]) {
60
- log.warn(`both ${ddVar} and ${otelVar} environment variables are set`)
61
- getCounter('otel.env.hiding', ddVar, otelVar,
62
- otelVar === 'OTEL_TRACES_SAMPLER' &&
63
- process.env.OTEL_TRACES_SAMPLER_ARG
64
- ? 'OTEL_TRACES_SAMPLER_ARG'
65
- : undefined).inc()
65
+ const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
66
+
67
+ function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
68
+ const OTEL_TRACES_SAMPLER_MAPPING = {
69
+ always_on: '1.0',
70
+ always_off: '0.0',
71
+ traceidratio: otelTracesSamplerArg,
72
+ parentbased_always_on: '1.0',
73
+ parentbased_always_off: '0.0',
74
+ parentbased_traceidratio: otelTracesSamplerArg
75
+ }
76
+ return OTEL_TRACES_SAMPLER_MAPPING[otelTracesSampler]
77
+ }
78
+
79
+ function validateOtelPropagators (propagators) {
80
+ if (!process.env.PROPAGATION_STYLE_EXTRACT &&
81
+ !process.env.PROPAGATION_STYLE_INJECT &&
82
+ !process.env.DD_TRACE_PROPAGATION_STYLE &&
83
+ process.env.OTEL_PROPAGATORS) {
84
+ for (const style in propagators) {
85
+ if (!VALID_PROPAGATION_STYLES.has(style)) {
86
+ log.warn('unexpected value for OTEL_PROPAGATORS environment variable')
87
+ getCounter('otel.env.invalid', 'DD_TRACE_PROPAGATION_STYLE', 'OTEL_PROPAGATORS').inc()
88
+ }
66
89
  }
67
90
  }
91
+ }
92
+
93
+ function validateEnvVarType (envVar) {
94
+ const value = process.env[envVar]
95
+ switch (envVar) {
96
+ case 'OTEL_LOG_LEVEL':
97
+ return VALID_LOG_LEVELS.has(value)
98
+ case 'OTEL_PROPAGATORS':
99
+ case 'OTEL_RESOURCE_ATTRIBUTES':
100
+ case 'OTEL_SERVICE_NAME':
101
+ return typeof value === 'string'
102
+ case 'OTEL_TRACES_SAMPLER':
103
+ return getFromOtelSamplerMap(value, process.env.OTEL_TRACES_SAMPLER_ARG) !== undefined
104
+ case 'OTEL_TRACES_SAMPLER_ARG':
105
+ return !isNaN(parseFloat(value))
106
+ case 'OTEL_SDK_DISABLED':
107
+ return value.toLowerCase() === 'true' || value.toLowerCase() === 'false'
108
+ case 'OTEL_TRACES_EXPORTER':
109
+ case 'OTEL_METRICS_EXPORTER':
110
+ case 'OTEL_LOGS_EXPORTER':
111
+ return value.toLowerCase() === 'none'
112
+ default:
113
+ return false
114
+ }
115
+ }
116
+
117
+ function checkIfBothOtelAndDdEnvVarSet () {
118
+ for (const [otelEnvVar, ddEnvVar] of Object.entries(otelDdEnvMapping)) {
119
+ if (ddEnvVar && process.env[ddEnvVar] && process.env[otelEnvVar]) {
120
+ log.warn(`both ${ddEnvVar} and ${otelEnvVar} environment variables are set`)
121
+ getCounter('otel.env.hiding', ddEnvVar, otelEnvVar).inc()
122
+ }
68
123
 
69
- for (const otelVar of otelInvalidEnv) {
70
- if (process.env[otelVar]) {
71
- log.warn(`${otelVar} is not supported by the Datadog SDK`)
72
- getCounter('otel.env.invalid', otelVar).inc()
124
+ if (process.env[otelEnvVar] && !validateEnvVarType(otelEnvVar)) {
125
+ log.warn(`unexpected value for ${otelEnvVar} environment variable`)
126
+ getCounter('otel.env.invalid', ddEnvVar, otelEnvVar).inc()
73
127
  }
74
128
  }
75
129
  }
@@ -80,9 +134,9 @@ const fromEntries = Object.fromEntries || (entries =>
80
134
  // eslint-disable-next-line max-len
81
135
  const qsRegex = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\\s|%20)*(?::|%3A)(?:\\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\\s|%20)+[a-z0-9\\._\\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\\w=-]|%3D)+\\.ey[I-L](?:[\\w=-]|%3D)+(?:\\.(?:[\\w.+\\/=-]|%3D|%2F|%2B)+)?|[\\-]{5}BEGIN(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY[\\-]{5}[^\\-]+[\\-]{5}END(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY|ssh-rsa(?:\\s|%20)*(?:[a-z0-9\\/\\.+]|%2F|%5C|%2B){100,}'
82
136
  // eslint-disable-next-line max-len
83
- const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?)key)|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization'
137
+ const defaultWafObfuscatorKeyRegex = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt'
84
138
  // eslint-disable-next-line max-len
85
- const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}'
139
+ const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}'
86
140
  const runtimeId = uuid()
87
141
 
88
142
  function maybeFile (filepath) {
@@ -131,7 +185,7 @@ function remapify (input, mappings) {
131
185
 
132
186
  function propagationStyle (key, option, defaultValue) {
133
187
  // Extract by key if in object-form value
134
- if (typeof option === 'object' && !Array.isArray(option)) {
188
+ if (option !== null && typeof option === 'object' && !Array.isArray(option)) {
135
189
  option = option[key]
136
190
  }
137
191
 
@@ -139,7 +193,7 @@ function propagationStyle (key, option, defaultValue) {
139
193
  if (Array.isArray(option)) return option.map(v => v.toLowerCase())
140
194
 
141
195
  // If it's not an array but not undefined there's something wrong with the input
142
- if (typeof option !== 'undefined') {
196
+ if (option !== undefined) {
143
197
  log.warn('Unexpected input for config.tracePropagationStyle')
144
198
  }
145
199
 
@@ -147,7 +201,7 @@ function propagationStyle (key, option, defaultValue) {
147
201
  const envKey = `DD_TRACE_PROPAGATION_STYLE_${key.toUpperCase()}`
148
202
 
149
203
  const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE, process.env.OTEL_PROPAGATORS)
150
- if (typeof envVar !== 'undefined') {
204
+ if (envVar !== undefined) {
151
205
  return envVar.split(',')
152
206
  .filter(v => v !== '')
153
207
  .map(v => v.trim().toLowerCase())
@@ -162,7 +216,7 @@ class Config {
162
216
  options = this.options = {
163
217
  ...options,
164
218
  appsec: options.appsec != null ? options.appsec : options.experimental?.appsec,
165
- iastOptions: options.experimental?.iast
219
+ iast: options.iast != null ? options.iast : options.experimental?.iast
166
220
  }
167
221
 
168
222
  checkIfBothOtelAndDdEnvVarSet()
@@ -235,6 +289,9 @@ class Config {
235
289
  options.tracePropagationStyle,
236
290
  defaultPropagationStyle
237
291
  )
292
+
293
+ validateOtelPropagators(PROPAGATION_STYLE_INJECT)
294
+
238
295
  const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce(
239
296
  process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST,
240
297
  false
@@ -440,6 +497,10 @@ class Config {
440
497
  this._setValue(defaults, 'appsec.rateLimit', 100)
441
498
  this._setValue(defaults, 'appsec.rules', undefined)
442
499
  this._setValue(defaults, 'appsec.sca.enabled', null)
500
+ this._setValue(defaults, 'appsec.standalone.enabled', undefined)
501
+ this._setValue(defaults, 'appsec.stackTrace.enabled', true)
502
+ this._setValue(defaults, 'appsec.stackTrace.maxDepth', 32)
503
+ this._setValue(defaults, 'appsec.stackTrace.maxStackTraces', 2)
443
504
  this._setValue(defaults, 'appsec.wafTimeout', 5e3) // µs
444
505
  this._setValue(defaults, 'clientIpEnabled', false)
445
506
  this._setValue(defaults, 'clientIpHeader', null)
@@ -524,10 +585,13 @@ class Config {
524
585
  DD_APPSEC_ENABLED,
525
586
  DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
526
587
  DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
588
+ DD_APPSEC_MAX_STACK_TRACES,
589
+ DD_APPSEC_MAX_STACK_TRACE_DEPTH,
527
590
  DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
528
591
  DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
529
592
  DD_APPSEC_RULES,
530
593
  DD_APPSEC_SCA_ENABLED,
594
+ DD_APPSEC_STACK_TRACE_ENABLED,
531
595
  DD_APPSEC_RASP_ENABLED,
532
596
  DD_APPSEC_TRACE_RATE_LIMIT,
533
597
  DD_APPSEC_WAF_TIMEOUT,
@@ -536,6 +600,7 @@ class Config {
536
600
  DD_DOGSTATSD_HOSTNAME,
537
601
  DD_DOGSTATSD_PORT,
538
602
  DD_ENV,
603
+ DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
539
604
  DD_EXPERIMENTAL_PROFILING_ENABLED,
540
605
  JEST_WORKER_ID,
541
606
  DD_IAST_DEDUPLICATION_ENABLED,
@@ -627,6 +692,12 @@ class Config {
627
692
  this._setString(env, 'appsec.rules', DD_APPSEC_RULES)
628
693
  // DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend
629
694
  this._setBoolean(env, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED)
695
+ this._setBoolean(env, 'appsec.standalone.enabled', DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED)
696
+ this._setBoolean(env, 'appsec.stackTrace.enabled', DD_APPSEC_STACK_TRACE_ENABLED)
697
+ this._setValue(env, 'appsec.stackTrace.maxDepth', maybeInt(DD_APPSEC_MAX_STACK_TRACE_DEPTH))
698
+ this._envUnprocessed['appsec.stackTrace.maxDepth'] = DD_APPSEC_MAX_STACK_TRACE_DEPTH
699
+ this._setValue(env, 'appsec.stackTrace.maxStackTraces', maybeInt(DD_APPSEC_MAX_STACK_TRACES))
700
+ this._envUnprocessed['appsec.stackTrace.maxStackTraces'] = DD_APPSEC_MAX_STACK_TRACES
630
701
  this._setValue(env, 'appsec.wafTimeout', maybeInt(DD_APPSEC_WAF_TIMEOUT))
631
702
  this._envUnprocessed['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT
632
703
  this._setBoolean(env, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
@@ -701,15 +772,8 @@ class Config {
701
772
  : undefined
702
773
  this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED ||
703
774
  otelSetRuntimeMetrics)
704
- const OTEL_TRACES_SAMPLER_MAPPING = {
705
- always_on: '1.0',
706
- always_off: '0.0',
707
- traceidratio: OTEL_TRACES_SAMPLER_ARG,
708
- parentbased_always_on: '1.0',
709
- parentbased_always_off: '0.0',
710
- parentbased_traceidratio: OTEL_TRACES_SAMPLER_ARG
711
- }
712
- this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || OTEL_TRACES_SAMPLER_MAPPING[OTEL_TRACES_SAMPLER])
775
+ this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE ||
776
+ getFromOtelSamplerMap(OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG))
713
777
  this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
714
778
  this._setSamplingRule(env, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES))
715
779
  this._envUnprocessed['sampler.rules'] = DD_TRACE_SAMPLING_RULES
@@ -767,6 +831,12 @@ class Config {
767
831
  this._setValue(opts, 'appsec.rateLimit', maybeInt(options.appsec.rateLimit))
768
832
  this._optsUnprocessed['appsec.rateLimit'] = options.appsec.rateLimit
769
833
  this._setString(opts, 'appsec.rules', options.appsec.rules)
834
+ this._setBoolean(opts, 'appsec.standalone.enabled', options.experimental?.appsec?.standalone?.enabled)
835
+ this._setBoolean(opts, 'appsec.stackTrace.enabled', options.appsec.stackTrace?.enabled)
836
+ this._setValue(opts, 'appsec.stackTrace.maxDepth', maybeInt(options.appsec.stackTrace?.maxDepth))
837
+ this._optsUnprocessed['appsec.stackTrace.maxDepth'] = options.appsec.stackTrace?.maxDepth
838
+ this._setValue(opts, 'appsec.stackTrace.maxStackTraces', maybeInt(options.appsec.stackTrace?.maxStackTraces))
839
+ this._optsUnprocessed['appsec.stackTrace.maxStackTraces'] = options.appsec.stackTrace?.maxStackTraces
770
840
  this._setValue(opts, 'appsec.wafTimeout', maybeInt(options.appsec.wafTimeout))
771
841
  this._optsUnprocessed['appsec.wafTimeout'] = options.appsec.wafTimeout
772
842
  this._setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled)
@@ -788,23 +858,23 @@ class Config {
788
858
  this._optsUnprocessed.flushMinSpans = options.flushMinSpans
789
859
  this._setArray(opts, 'headerTags', options.headerTags)
790
860
  this._setString(opts, 'hostname', options.hostname)
791
- this._setBoolean(opts, 'iast.deduplicationEnabled', options.iastOptions && options.iastOptions.deduplicationEnabled)
861
+ this._setBoolean(opts, 'iast.deduplicationEnabled', options.iast && options.iast.deduplicationEnabled)
792
862
  this._setBoolean(opts, 'iast.enabled',
793
- options.iastOptions && (options.iastOptions === true || options.iastOptions.enabled === true))
863
+ options.iast && (options.iast === true || options.iast.enabled === true))
794
864
  this._setValue(opts, 'iast.maxConcurrentRequests',
795
- maybeInt(options.iastOptions?.maxConcurrentRequests))
796
- this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iastOptions?.maxConcurrentRequests
797
- this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iastOptions?.maxContextOperations))
798
- this._optsUnprocessed['iast.maxContextOperations'] = options.iastOptions?.maxContextOperations
799
- this._setBoolean(opts, 'iast.redactionEnabled', options.iastOptions?.redactionEnabled)
800
- this._setString(opts, 'iast.redactionNamePattern', options.iastOptions?.redactionNamePattern)
801
- this._setString(opts, 'iast.redactionValuePattern', options.iastOptions?.redactionValuePattern)
802
- const iastRequestSampling = maybeInt(options.iastOptions?.requestSampling)
865
+ maybeInt(options.iast?.maxConcurrentRequests))
866
+ this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iast?.maxConcurrentRequests
867
+ this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iast?.maxContextOperations))
868
+ this._optsUnprocessed['iast.maxContextOperations'] = options.iast?.maxContextOperations
869
+ this._setBoolean(opts, 'iast.redactionEnabled', options.iast?.redactionEnabled)
870
+ this._setString(opts, 'iast.redactionNamePattern', options.iast?.redactionNamePattern)
871
+ this._setString(opts, 'iast.redactionValuePattern', options.iast?.redactionValuePattern)
872
+ const iastRequestSampling = maybeInt(options.iast?.requestSampling)
803
873
  if (iastRequestSampling > -1 && iastRequestSampling < 101) {
804
874
  this._setValue(opts, 'iast.requestSampling', iastRequestSampling)
805
- this._optsUnprocessed['iast.requestSampling'] = options.iastOptions?.requestSampling
875
+ this._optsUnprocessed['iast.requestSampling'] = options.iast?.requestSampling
806
876
  }
807
- this._setString(opts, 'iast.telemetryVerbosity', options.iastOptions && options.iastOptions.telemetryVerbosity)
877
+ this._setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity)
808
878
  this._setBoolean(opts, 'isCiVisibility', options.isCiVisibility)
809
879
  this._setBoolean(opts, 'logInjection', options.logInjection)
810
880
  this._setString(opts, 'lookup', options.lookup)
@@ -834,7 +904,7 @@ class Config {
834
904
  this._setBoolean(opts, 'startupLogs', options.startupLogs)
835
905
  this._setTags(opts, 'tags', tags)
836
906
  const hasTelemetryLogsUsingFeatures =
837
- (options.iastOptions && (options.iastOptions === true || options.iastOptions?.enabled === true)) ||
907
+ (options.iast && (options.iast === true || options.iast?.enabled === true)) ||
838
908
  (options.profiling && options.profiling === true)
839
909
  this._setBoolean(opts, 'telemetry.logCollection', hasTelemetryLogsUsingFeatures)
840
910
  this._setBoolean(opts, 'traceId128BitGenerationEnabled', options.traceId128BitGenerationEnabled)
@@ -973,15 +1043,18 @@ class Config {
973
1043
  this._setArray(opts, 'headerTags', headerTags)
974
1044
  this._setTags(opts, 'tags', tags)
975
1045
  this._setBoolean(opts, 'tracing', options.tracing_enabled)
976
- // ignore tags for now since rc sampling rule tags format is not supported
977
- this._setSamplingRule(opts, 'sampler.rules', this._ignoreTags(options.tracing_sampling_rules))
978
1046
  this._remoteUnprocessed['sampler.rules'] = options.tracing_sampling_rules
1047
+ this._setSamplingRule(opts, 'sampler.rules', this._reformatTags(options.tracing_sampling_rules))
979
1048
  }
980
1049
 
981
- _ignoreTags (samplingRules) {
982
- if (samplingRules) {
983
- for (const rule of samplingRules) {
984
- delete rule.tags
1050
+ _reformatTags (samplingRules) {
1051
+ for (const rule of (samplingRules || [])) {
1052
+ const reformattedTags = {}
1053
+ if (rule.tags) {
1054
+ for (const tag of (rule.tags || {})) {
1055
+ reformattedTags[tag.key] = tag.value_glob
1056
+ }
1057
+ rule.tags = reformattedTags
985
1058
  }
986
1059
  }
987
1060
  return samplingRules
@@ -32,5 +32,7 @@ module.exports = {
32
32
  PEER_SERVICE_SOURCE_KEY: '_dd.peer.service.source',
33
33
  PEER_SERVICE_REMAP_KEY: '_dd.peer.service.remapped_from',
34
34
  SCI_REPOSITORY_URL: '_dd.git.repository_url',
35
- SCI_COMMIT_SHA: '_dd.git.commit.sha'
35
+ SCI_COMMIT_SHA: '_dd.git.commit.sha',
36
+ APM_TRACING_ENABLED_KEY: '_dd.apm.enabled',
37
+ APPSEC_PROPAGATION_KEY: '_dd.p.appsec'
36
38
  }
@@ -132,7 +132,7 @@ function getSizeOrZero (obj) {
132
132
  })
133
133
  return payloadSize
134
134
  }
135
- if (typeof obj === 'object') {
135
+ if (obj !== null && typeof obj === 'object') {
136
136
  try {
137
137
  return getHeadersSize(obj)
138
138
  } catch {
@@ -211,7 +211,8 @@ class DataStreamsProcessor {
211
211
  Stats,
212
212
  TracerVersion: pkg.version,
213
213
  Version: this.version,
214
- Lang: 'javascript'
214
+ Lang: 'javascript',
215
+ Tags: Object.entries(this.tags).map(([key, value]) => `${key}:${value}`)
215
216
  }
216
217
  this.writer.flush(payload)
217
218
  }
@@ -7,7 +7,7 @@ const Writer = require('./writer')
7
7
  class AgentExporter {
8
8
  constructor (config, prioritySampler) {
9
9
  this._config = config
10
- const { url, hostname, port, lookup, protocolVersion, stats = {} } = config
10
+ const { url, hostname, port, lookup, protocolVersion, stats = {}, appsec } = config
11
11
  this._url = url || new URL(format({
12
12
  protocol: 'http:',
13
13
  hostname: hostname || 'localhost',
@@ -15,7 +15,7 @@ class AgentExporter {
15
15
  }))
16
16
 
17
17
  const headers = {}
18
- if (stats.enabled) {
18
+ if (stats.enabled || appsec?.standalone?.enabled) {
19
19
  headers['Datadog-Client-Computed-Stats'] = 'yes'
20
20
  }
21
21
 
@@ -53,6 +53,7 @@ function formatSpan (span) {
53
53
  resource: String(spanContext._name),
54
54
  error: 0,
55
55
  meta: {},
56
+ meta_struct: span.meta_struct,
56
57
  metrics: {},
57
58
  start: Math.round(span._startTime * 1e6),
58
59
  duration: Math.round(span._duration * 1e6),
@@ -283,7 +283,7 @@ class Span {
283
283
  }
284
284
 
285
285
  get ended () {
286
- return typeof this.duration !== 'undefined'
286
+ return this.duration !== undefined
287
287
  }
288
288
  }
289
289
 
@@ -15,6 +15,7 @@ class Tracer {
15
15
  this._tracerProvider = tracerProvider
16
16
  // Is there a reason this is public?
17
17
  this.instrumentationLibrary = library
18
+ this._spanLimits = {}
18
19
  }
19
20
 
20
21
  get resource () {
@@ -118,6 +119,11 @@ class Tracer {
118
119
  getActiveSpanProcessor () {
119
120
  return this._tracerProvider.getActiveSpanProcessor()
120
121
  }
122
+
123
+ // not used in our codebase but needed for compatibility. See issue #1244
124
+ getSpanLimits () {
125
+ return this._spanLimits
126
+ }
121
127
  }
122
128
 
123
129
  module.exports = Tracer
@@ -6,9 +6,13 @@ const DatadogSpanContext = require('../span_context')
6
6
  const log = require('../../log')
7
7
  const TraceState = require('./tracestate')
8
8
  const tags = require('../../../../../ext/tags')
9
+ const { channel } = require('dc-polyfill')
9
10
 
10
11
  const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
11
12
 
13
+ const injectCh = channel('dd-trace:span:inject')
14
+ const extractCh = channel('dd-trace:span:extract')
15
+
12
16
  const traceKey = 'x-datadog-trace-id'
13
17
  const spanKey = 'x-datadog-parent-id'
14
18
  const originKey = 'x-datadog-origin'
@@ -54,6 +58,10 @@ class TextMapPropagator {
54
58
  this._injectB3SingleHeader(spanContext, carrier)
55
59
  this._injectTraceparent(spanContext, carrier)
56
60
 
61
+ if (injectCh.hasSubscribers) {
62
+ injectCh.publish({ spanContext, carrier })
63
+ }
64
+
57
65
  log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
58
66
  }
59
67
 
@@ -62,6 +70,10 @@ class TextMapPropagator {
62
70
 
63
71
  if (!spanContext) return spanContext
64
72
 
73
+ if (extractCh.hasSubscribers) {
74
+ extractCh.publish({ spanContext, carrier })
75
+ }
76
+
65
77
  log.debug(() => `Extract from carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
66
78
 
67
79
  return spanContext
@@ -99,7 +99,10 @@ class DatadogSpan {
99
99
  unfinishedRegistry.register(this, operationName, this)
100
100
  }
101
101
  spanleak.addSpan(this, operationName)
102
- startCh.publish(this)
102
+
103
+ if (startCh.hasSubscribers) {
104
+ startCh.publish({ span: this, fields })
105
+ }
103
106
  }
104
107
 
105
108
  toString () {
@@ -19,7 +19,7 @@ const REFERENCE_CHILD_OF = 'child_of'
19
19
  const REFERENCE_FOLLOWS_FROM = 'follows_from'
20
20
 
21
21
  class DatadogTracer {
22
- constructor (config) {
22
+ constructor (config, prioritySampler) {
23
23
  const Exporter = getExporter(config.experimental.exporter)
24
24
 
25
25
  this._config = config
@@ -28,7 +28,7 @@ class DatadogTracer {
28
28
  this._env = config.env
29
29
  this._logInjection = config.logInjection
30
30
  this._debug = config.debug
31
- this._prioritySampler = new PrioritySampler(config.env, config.sampler)
31
+ this._prioritySampler = prioritySampler ?? new PrioritySampler(config.env, config.sampler)
32
32
  this._exporter = new Exporter(config, this._prioritySampler)
33
33
  this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config)
34
34
  this._url = this._exporter._url
@@ -91,6 +91,13 @@ module.exports = class CiPlugin extends Plugin {
91
91
  ...testModuleSpanMetadata
92
92
  }
93
93
  })
94
+ // only for vitest
95
+ // These are added for the worker threads to use
96
+ if (this.constructor.id === 'vitest') {
97
+ process.env.DD_CIVISIBILITY_TEST_SESSION_ID = this.testSessionSpan.context().toTraceId()
98
+ process.env.DD_CIVISIBILITY_TEST_MODULE_ID = this.testModuleSpan.context().toSpanId()
99
+ }
100
+
94
101
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
95
102
  })
96
103
 
@@ -18,6 +18,7 @@ module.exports = {
18
18
  get '@opensearch-project/opensearch' () { return require('../../../datadog-plugin-opensearch/src') },
19
19
  get '@redis/client' () { return require('../../../datadog-plugin-redis/src') },
20
20
  get '@smithy/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
21
+ get '@vitest/runner' () { return require('../../../datadog-plugin-vitest/src') },
21
22
  get aerospike () { return require('../../../datadog-plugin-aerospike/src') },
22
23
  get amqp10 () { return require('../../../datadog-plugin-amqp10/src') },
23
24
  get amqplib () { return require('../../../datadog-plugin-amqplib/src') },
@@ -54,6 +55,7 @@ module.exports = {
54
55
  get 'microgateway-core' () { return require('../../../datadog-plugin-microgateway-core/src') },
55
56
  get mocha () { return require('../../../datadog-plugin-mocha/src') },
56
57
  get 'mocha-each' () { return require('../../../datadog-plugin-mocha/src') },
58
+ get vitest () { return require('../../../datadog-plugin-vitest/src') },
57
59
  get workerpool () { return require('../../../datadog-plugin-mocha/src') },
58
60
  get moleculer () { return require('../../../datadog-plugin-moleculer/src') },
59
61
  get mongodb () { return require('../../../datadog-plugin-mongodb-core/src') },
@@ -95,6 +95,9 @@ const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
95
95
  const EFD_STRING = "Retried by Datadog's Early Flake Detection"
96
96
  const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
97
97
 
98
+ // Flaky test retries
99
+ const NUM_FAILED_TEST_RETRIES = 5
100
+
98
101
  module.exports = {
99
102
  TEST_CODE_OWNERS,
100
103
  TEST_FRAMEWORK,
@@ -167,7 +170,8 @@ module.exports = {
167
170
  TEST_BROWSER_DRIVER,
168
171
  TEST_BROWSER_DRIVER_VERSION,
169
172
  TEST_BROWSER_NAME,
170
- TEST_BROWSER_VERSION
173
+ TEST_BROWSER_VERSION,
174
+ NUM_FAILED_TEST_RETRIES
171
175
  }
172
176
 
173
177
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -4,6 +4,7 @@ const RateLimiter = require('./rate_limiter')
4
4
  const Sampler = require('./sampler')
5
5
  const { setSamplingRules } = require('./startup-log')
6
6
  const SamplingRule = require('./sampling_rule')
7
+ const { hasOwn } = require('./util')
7
8
 
8
9
  const {
9
10
  SAMPLING_MECHANISM_DEFAULT,
@@ -66,7 +67,7 @@ class PrioritySampler {
66
67
  if (context._sampling.priority !== undefined) return
67
68
  if (!root) return // noop span
68
69
 
69
- const tag = this._getPriorityFromTags(context._tags)
70
+ const tag = this._getPriorityFromTags(context._tags, context)
70
71
 
71
72
  if (this.validate(tag)) {
72
73
  context._sampling.priority = tag
@@ -202,8 +203,4 @@ class PrioritySampler {
202
203
  }
203
204
  }
204
205
 
205
- function hasOwn (object, prop) {
206
- return Object.prototype.hasOwnProperty.call(object, prop)
207
- }
208
-
209
206
  module.exports = PrioritySampler
@@ -69,7 +69,7 @@ class Profiler extends EventEmitter {
69
69
  setLogger(config.logger)
70
70
 
71
71
  mapper = await maybeSourceMap(config.sourceMap, SourceMapper, config.debugSourceMaps)
72
- if (config.SourceMap && config.debugSourceMaps) {
72
+ if (config.sourceMap && config.debugSourceMaps) {
73
73
  this._logger.debug(() => {
74
74
  return mapper.infoMap.size === 0
75
75
  ? 'Found no source maps'
@@ -15,6 +15,7 @@ const NoopDogStatsDClient = require('./noop/dogstatsd')
15
15
  const spanleak = require('./spanleak')
16
16
  const { SSIHeuristics } = require('./profiling/ssi-heuristics')
17
17
  const telemetryLog = require('dc-polyfill').channel('datadog:telemetry:log')
18
+ const appsecStandalone = require('./appsec/standalone')
18
19
 
19
20
  class LazyModule {
20
21
  constructor (provider) {
@@ -178,7 +179,8 @@ class Tracer extends NoopProxy {
178
179
  this._modules.appsec.enable(config)
179
180
  }
180
181
  if (!this._tracingInitialized) {
181
- this._tracer = new DatadogTracer(config)
182
+ const prioritySampler = appsecStandalone.configure(config)
183
+ this._tracer = new DatadogTracer(config, prioritySampler)
182
184
  this.appsec = new AppsecSdk(this._tracer, config)
183
185
  this._tracingInitialized = true
184
186
  }
@@ -3,9 +3,9 @@
3
3
  const limiter = require('limiter')
4
4
 
5
5
  class RateLimiter {
6
- constructor (rateLimit) {
6
+ constructor (rateLimit, interval = 'second') {
7
7
  this._rateLimit = parseInt(rateLimit)
8
- this._limiter = new limiter.RateLimiter(this._rateLimit, 'second')
8
+ this._limiter = new limiter.RateLimiter(this._rateLimit, interval)
9
9
  this._tokensRequested = 0
10
10
  this._prevIntervalTokens = 0
11
11
  this._prevTokensRequested = 0
@@ -126,7 +126,8 @@ class SpanStatsProcessor {
126
126
  port,
127
127
  url,
128
128
  env,
129
- tags
129
+ tags,
130
+ appsec
130
131
  } = {}) {
131
132
  this.exporter = new SpanStatsExporter({
132
133
  hostname,
@@ -138,12 +139,12 @@ class SpanStatsProcessor {
138
139
  this.bucketSizeNs = interval * 1e9
139
140
  this.buckets = new TimeBuckets()
140
141
  this.hostname = os.hostname()
141
- this.enabled = enabled
142
+ this.enabled = enabled && !appsec?.standalone?.enabled
142
143
  this.env = env
143
144
  this.tags = tags || {}
144
145
  this.sequence = 0
145
146
 
146
- if (enabled) {
147
+ if (this.enabled) {
147
148
  this.timer = setInterval(this.onInterval.bind(this), interval * 1e3)
148
149
  this.timer.unref()
149
150
  }