dd-trace 5.17.0 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +1 -0
- package/ext/exporters.d.ts +1 -1
- package/index.d.ts +47 -1
- package/init.js +40 -1
- package/initialize.mjs +8 -5
- package/package.json +24 -20
- package/packages/datadog-core/src/storage/index.js +1 -10
- package/packages/datadog-esbuild/index.js +5 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +76 -34
- package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
- package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +303 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
- package/packages/datadog-plugin-child_process/src/index.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-mocha/src/index.js +25 -4
- package/packages/datadog-plugin-openai/src/index.js +52 -30
- package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
- package/packages/datadog-plugin-vitest/src/index.js +156 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/rasp.js +32 -5
- package/packages/dd-trace/src/appsec/recommended.json +208 -3
- package/packages/dd-trace/src/appsec/reporter.js +64 -20
- package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
- package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
- package/packages/dd-trace/src/appsec/standalone.js +130 -0
- package/packages/dd-trace/src/appsec/telemetry.js +33 -1
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +110 -40
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -1
- package/packages/dd-trace/src/exporters/agent/index.js +2 -2
- package/packages/dd-trace/src/format.js +1 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
- package/packages/dd-trace/src/opentracing/span.js +4 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +5 -1
- package/packages/dd-trace/src/priority_sampler.js +2 -5
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -1
- package/packages/dd-trace/src/rate_limiter.js +2 -2
- package/packages/dd-trace/src/span_stats.js +4 -3
- package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
- package/packages/dd-trace/src/tracer.js +2 -2
- package/packages/dd-trace/src/util.js +6 -1
- package/packages/datadog-core/src/storage/async_hooks.js +0 -49
|
@@ -31,7 +31,10 @@ function newStore () {
|
|
|
31
31
|
return {
|
|
32
32
|
[DD_TELEMETRY_REQUEST_METRICS]: {
|
|
33
33
|
duration: 0,
|
|
34
|
-
durationExt: 0
|
|
34
|
+
durationExt: 0,
|
|
35
|
+
raspDuration: 0,
|
|
36
|
+
raspDurationExt: 0,
|
|
37
|
+
raspEvalCount: 0
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -76,6 +79,28 @@ function getOrCreateMetricTags (store, versionsTags) {
|
|
|
76
79
|
return metricTags
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
|
|
83
|
+
if (!req) return
|
|
84
|
+
|
|
85
|
+
const store = getStore(req)
|
|
86
|
+
|
|
87
|
+
// it does not depend on whether telemetry is enabled or not
|
|
88
|
+
addRaspRequestMetrics(store, metrics)
|
|
89
|
+
|
|
90
|
+
if (!enabled) return
|
|
91
|
+
|
|
92
|
+
const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
|
|
93
|
+
appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1)
|
|
94
|
+
|
|
95
|
+
if (metrics.wafTimeout) {
|
|
96
|
+
appsecMetrics.count('appsec.rasp.timeout', tags).inc(1)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (metrics.ruleTriggered) {
|
|
100
|
+
appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
function updateWafRequestsMetricTags (metrics, req) {
|
|
80
105
|
if (!req) return
|
|
81
106
|
|
|
@@ -141,6 +166,12 @@ function addRequestMetrics (store, { duration, durationExt }) {
|
|
|
141
166
|
store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
|
|
142
167
|
}
|
|
143
168
|
|
|
169
|
+
function addRaspRequestMetrics (store, { duration, durationExt }) {
|
|
170
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDuration += duration || 0
|
|
171
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDurationExt += durationExt || 0
|
|
172
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspEvalCount++
|
|
173
|
+
}
|
|
174
|
+
|
|
144
175
|
function getRequestMetrics (req) {
|
|
145
176
|
if (req) {
|
|
146
177
|
const store = getStore(req)
|
|
@@ -153,6 +184,7 @@ module.exports = {
|
|
|
153
184
|
disable,
|
|
154
185
|
|
|
155
186
|
updateWafRequestsMetricTags,
|
|
187
|
+
updateRaspRequestsMetricTags,
|
|
156
188
|
incrementWafInitMetric,
|
|
157
189
|
incrementWafUpdatesMetric,
|
|
158
190
|
incrementWafRequestsMetric,
|
|
@@ -46,7 +46,7 @@ function update (newRules) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function run (data, req) {
|
|
49
|
+
function run (data, req, raspRuleType) {
|
|
50
50
|
if (!req) {
|
|
51
51
|
const store = storage.getStore()
|
|
52
52
|
if (!store || !store.req) {
|
|
@@ -59,7 +59,7 @@ function run (data, req) {
|
|
|
59
59
|
|
|
60
60
|
const wafContext = waf.wafManager.getWAFContext(req)
|
|
61
61
|
|
|
62
|
-
return wafContext.run(data)
|
|
62
|
+
return wafContext.run(data, raspRuleType)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function disposeContext (req) {
|
|
@@ -19,7 +19,7 @@ class WAFContextWrapper {
|
|
|
19
19
|
this.addressesToSkip = new Set()
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
run ({ persistent, ephemeral }) {
|
|
22
|
+
run ({ persistent, ephemeral }, raspRuleType) {
|
|
23
23
|
const payload = {}
|
|
24
24
|
let payloadHasData = false
|
|
25
25
|
const inputs = {}
|
|
@@ -72,7 +72,7 @@ class WAFContextWrapper {
|
|
|
72
72
|
blockTriggered,
|
|
73
73
|
wafVersion: this.wafVersion,
|
|
74
74
|
wafTimeout: result.timeout
|
|
75
|
-
})
|
|
75
|
+
}, raspRuleType)
|
|
76
76
|
|
|
77
77
|
if (ruleTriggered) {
|
|
78
78
|
Reporter.reportAttack(JSON.stringify(result.events))
|
|
@@ -190,7 +190,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
190
190
|
requireGit,
|
|
191
191
|
isEarlyFlakeDetectionEnabled,
|
|
192
192
|
earlyFlakeDetectionNumRetries,
|
|
193
|
-
earlyFlakeDetectionFaultyThreshold
|
|
193
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
194
|
+
isFlakyTestRetriesEnabled
|
|
194
195
|
} = remoteConfiguration
|
|
195
196
|
return {
|
|
196
197
|
isCodeCoverageEnabled,
|
|
@@ -199,7 +200,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
199
200
|
requireGit,
|
|
200
201
|
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
|
|
201
202
|
earlyFlakeDetectionNumRetries,
|
|
202
|
-
earlyFlakeDetectionFaultyThreshold
|
|
203
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
204
|
+
isFlakyTestRetriesEnabled
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
@@ -93,7 +93,8 @@ function getLibraryConfiguration ({
|
|
|
93
93
|
tests_skipping: isSuitesSkippingEnabled,
|
|
94
94
|
itr_enabled: isItrEnabled,
|
|
95
95
|
require_git: requireGit,
|
|
96
|
-
early_flake_detection: earlyFlakeDetectionConfig
|
|
96
|
+
early_flake_detection: earlyFlakeDetectionConfig,
|
|
97
|
+
flaky_test_retries_enabled: isFlakyTestRetriesEnabled
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
} = JSON.parse(res)
|
|
@@ -107,7 +108,8 @@ function getLibraryConfiguration ({
|
|
|
107
108
|
earlyFlakeDetectionNumRetries:
|
|
108
109
|
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
109
110
|
earlyFlakeDetectionFaultyThreshold:
|
|
110
|
-
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
|
|
111
|
+
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
112
|
+
isFlakyTestRetriesEnabled
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
|
|
@@ -26,50 +26,104 @@ const telemetryCounters = {
|
|
|
26
26
|
'otel.env.invalid': {}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function getCounter (event, ddVar, otelVar
|
|
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 (
|
|
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[
|
|
46
|
+
counters[otelVar][ddVar] = counter
|
|
41
47
|
return counter
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
const otelDdEnvMapping = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
63
|
+
const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none'])
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
}
|
|
68
92
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
}
|
|
123
|
+
|
|
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
|
|
137
|
+
const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key)|(?:(?:auth|access|id|refresh)[_-]?)?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
|
|
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) {
|
|
@@ -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
|
-
|
|
705
|
-
|
|
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)
|
|
@@ -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
|
}
|
|
@@ -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
|
|
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
}
|