dd-trace 5.14.1 → 5.15.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 +0 -3
- package/README.md +8 -18
- package/ci/init.js +7 -0
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +2 -1
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +18 -3
- package/initialize.mjs +52 -0
- package/package.json +9 -12
- package/packages/datadog-instrumentations/src/amqplib.js +5 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
- package/packages/datadog-instrumentations/src/body-parser.js +0 -1
- package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
- package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
- package/packages/datadog-instrumentations/src/express.js +0 -1
- package/packages/datadog-instrumentations/src/graphql.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
- package/packages/datadog-instrumentations/src/http/server.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
- package/packages/datadog-instrumentations/src/mocha.js +4 -673
- package/packages/datadog-instrumentations/src/openai.js +188 -17
- package/packages/datadog-instrumentations/src/playwright.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +13 -6
- package/packages/datadog-plugin-mocha/src/index.js +82 -8
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/index.js +219 -73
- package/packages/dd-trace/src/appsec/addresses.js +4 -2
- package/packages/dd-trace/src/appsec/blocking.js +19 -25
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/graphql.js +10 -3
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/rasp.js +35 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
- package/packages/dd-trace/src/config.js +59 -16
- package/packages/dd-trace/src/encode/0.4.js +47 -8
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/flare/file.js +44 -0
- package/packages/dd-trace/src/flare/index.js +98 -0
- package/packages/dd-trace/src/log/channels.js +54 -29
- package/packages/dd-trace/src/log/writer.js +7 -49
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +6 -0
- package/packages/dd-trace/src/profiler.js +2 -1
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
- package/packages/dd-trace/src/proxy.js +49 -15
- package/packages/dd-trace/src/ritm.js +13 -1
- package/packages/dd-trace/src/startup-log.js +19 -15
- package/packages/dd-trace/src/telemetry/index.js +6 -2
- package/packages/dd-trace/src/tracer.js +3 -0
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
|
@@ -15,6 +15,7 @@ function enable (config, appsec) {
|
|
|
15
15
|
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_LOGS_INJECTION, true)
|
|
16
16
|
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)
|
|
17
17
|
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLED, true)
|
|
18
|
+
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RULES, true)
|
|
18
19
|
|
|
19
20
|
const activation = Activation.fromConfig(config)
|
|
20
21
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const waf = require('./waf')
|
|
5
5
|
const { ACKNOWLEDGED, ERROR } = require('./remote_config/apply_states')
|
|
6
|
-
const blocking = require('./blocking')
|
|
7
6
|
|
|
8
7
|
let defaultRules
|
|
9
8
|
|
|
@@ -20,10 +19,6 @@ function loadRules (config) {
|
|
|
20
19
|
: require('./recommended.json')
|
|
21
20
|
|
|
22
21
|
waf.init(defaultRules, config)
|
|
23
|
-
|
|
24
|
-
if (defaultRules.actions) {
|
|
25
|
-
blocking.updateBlockingConfiguration(defaultRules.actions.find(action => action.id === 'block'))
|
|
26
|
-
}
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
@@ -68,7 +63,7 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
68
63
|
item.apply_state = ERROR
|
|
69
64
|
item.apply_error = 'Multiple ruleset received in ASM_DD'
|
|
70
65
|
} else {
|
|
71
|
-
if (file
|
|
66
|
+
if (file?.rules?.length) {
|
|
72
67
|
const { version, metadata, rules, processors, scanners } = file
|
|
73
68
|
|
|
74
69
|
newRuleset = { version, metadata, rules, processors, scanners }
|
|
@@ -78,30 +73,23 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
78
73
|
batch.add(item)
|
|
79
74
|
}
|
|
80
75
|
} else if (product === 'ASM') {
|
|
81
|
-
|
|
82
|
-
if (file && file.rules_override && file.rules_override.length) {
|
|
83
|
-
batchConfiguration = true
|
|
76
|
+
if (file?.rules_override?.length) {
|
|
84
77
|
newRulesOverride.set(id, file.rules_override)
|
|
85
78
|
}
|
|
86
79
|
|
|
87
|
-
if (file
|
|
88
|
-
batchConfiguration = true
|
|
80
|
+
if (file?.exclusions?.length) {
|
|
89
81
|
newExclusions.set(id, file.exclusions)
|
|
90
82
|
}
|
|
91
83
|
|
|
92
|
-
if (file
|
|
93
|
-
batchConfiguration = true
|
|
84
|
+
if (file?.custom_rules?.length) {
|
|
94
85
|
newCustomRules.set(id, file.custom_rules)
|
|
95
86
|
}
|
|
96
87
|
|
|
97
|
-
if (file
|
|
88
|
+
if (file?.actions?.length) {
|
|
98
89
|
newActions.set(id, file.actions)
|
|
99
90
|
}
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
if (batchConfiguration) {
|
|
103
|
-
batch.add(item)
|
|
104
|
-
}
|
|
92
|
+
batch.add(item)
|
|
105
93
|
}
|
|
106
94
|
}
|
|
107
95
|
|
|
@@ -112,7 +100,9 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
112
100
|
newRuleset ||
|
|
113
101
|
newRulesOverride.modified ||
|
|
114
102
|
newExclusions.modified ||
|
|
115
|
-
newCustomRules.modified
|
|
103
|
+
newCustomRules.modified ||
|
|
104
|
+
newActions.modified
|
|
105
|
+
) {
|
|
116
106
|
const payload = newRuleset || {}
|
|
117
107
|
|
|
118
108
|
if (newRulesData.modified) {
|
|
@@ -127,6 +117,9 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
127
117
|
if (newCustomRules.modified) {
|
|
128
118
|
payload.custom_rules = concatArrays(newCustomRules)
|
|
129
119
|
}
|
|
120
|
+
if (newActions.modified) {
|
|
121
|
+
payload.actions = concatArrays(newActions)
|
|
122
|
+
}
|
|
130
123
|
|
|
131
124
|
try {
|
|
132
125
|
waf.update(payload)
|
|
@@ -146,6 +139,9 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
146
139
|
if (newCustomRules.modified) {
|
|
147
140
|
appliedCustomRules = newCustomRules
|
|
148
141
|
}
|
|
142
|
+
if (newActions.modified) {
|
|
143
|
+
appliedActions = newActions
|
|
144
|
+
}
|
|
149
145
|
} catch (err) {
|
|
150
146
|
newApplyState = ERROR
|
|
151
147
|
newApplyError = err.toString()
|
|
@@ -156,11 +152,6 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
|
|
|
156
152
|
config.apply_state = newApplyState
|
|
157
153
|
if (newApplyError) config.apply_error = newApplyError
|
|
158
154
|
}
|
|
159
|
-
|
|
160
|
-
if (newActions.modified) {
|
|
161
|
-
blocking.updateBlockingConfiguration(concatArrays(newActions).find(action => action.id === 'block'))
|
|
162
|
-
appliedActions = newActions
|
|
163
|
-
}
|
|
164
155
|
}
|
|
165
156
|
|
|
166
157
|
// A Map with a new prop `modified`, a bool that indicates if the Map was modified
|
|
@@ -242,7 +233,6 @@ function copyRulesData (rulesData) {
|
|
|
242
233
|
|
|
243
234
|
function clearAllRules () {
|
|
244
235
|
waf.destroy()
|
|
245
|
-
blocking.updateBlockingConfiguration(undefined)
|
|
246
236
|
|
|
247
237
|
defaultRules = undefined
|
|
248
238
|
|
|
@@ -3,17 +3,14 @@
|
|
|
3
3
|
const { USER_ID } = require('../addresses')
|
|
4
4
|
const waf = require('../waf')
|
|
5
5
|
const { getRootSpan } = require('./utils')
|
|
6
|
-
const { block } = require('../blocking')
|
|
6
|
+
const { block, getBlockingAction } = require('../blocking')
|
|
7
7
|
const { storage } = require('../../../../datadog-core')
|
|
8
8
|
const { setUserTags } = require('./set_user')
|
|
9
9
|
const log = require('../../log')
|
|
10
10
|
|
|
11
11
|
function isUserBlocked (user) {
|
|
12
12
|
const actions = waf.run({ persistent: { [USER_ID]: user.id } })
|
|
13
|
-
|
|
14
|
-
if (!actions) return false
|
|
15
|
-
|
|
16
|
-
return actions.includes('block')
|
|
13
|
+
return !!getBlockingAction(actions)
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
function checkUserAndSetUser (tracer, user) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const Reporter = require('../reporter')
|
|
5
5
|
const addresses = require('../addresses')
|
|
6
|
+
const { getBlockingAction } = require('../blocking')
|
|
6
7
|
|
|
7
8
|
// TODO: remove once ephemeral addresses are implemented
|
|
8
9
|
const preventDuplicateAddresses = new Set([
|
|
@@ -60,7 +61,8 @@ class WAFContextWrapper {
|
|
|
60
61
|
this.addressesToSkip = newAddressesToSkip
|
|
61
62
|
|
|
62
63
|
const ruleTriggered = !!result.events?.length
|
|
63
|
-
|
|
64
|
+
|
|
65
|
+
const blockTriggered = !!getBlockingAction(result.actions)
|
|
64
66
|
|
|
65
67
|
Reporter.reportMetrics({
|
|
66
68
|
duration: result.totalRuntime / 1e3,
|
|
@@ -4,7 +4,8 @@ const Writer = require('./writer')
|
|
|
4
4
|
const {
|
|
5
5
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
6
6
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
7
|
-
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE
|
|
7
|
+
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
8
|
+
MOCHA_WORKER_TRACE_PAYLOAD_CODE
|
|
8
9
|
} = require('../../../plugins/util/test')
|
|
9
10
|
|
|
10
11
|
function getInterprocessTraceCode () {
|
|
@@ -14,6 +15,9 @@ function getInterprocessTraceCode () {
|
|
|
14
15
|
if (process.env.CUCUMBER_WORKER_ID) {
|
|
15
16
|
return CUCUMBER_WORKER_TRACE_PAYLOAD_CODE
|
|
16
17
|
}
|
|
18
|
+
if (process.env.MOCHA_WORKER_ID) {
|
|
19
|
+
return MOCHA_WORKER_TRACE_PAYLOAD_CODE
|
|
20
|
+
}
|
|
17
21
|
return null
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const os = require('os')
|
|
5
|
-
const uuid = require('crypto-randomuuid')
|
|
5
|
+
const uuid = require('crypto-randomuuid') // we need to keep the old uuid dep because of cypress
|
|
6
6
|
const URL = require('url').URL
|
|
7
7
|
const log = require('./log')
|
|
8
8
|
const pkg = require('./pkg')
|
|
@@ -170,6 +170,7 @@ class Config {
|
|
|
170
170
|
// Configure the logger first so it can be used to warn about other configs
|
|
171
171
|
this.debug = isTrue(coalesce(
|
|
172
172
|
process.env.DD_TRACE_DEBUG,
|
|
173
|
+
process.env.OTEL_LOG_LEVEL && process.env.OTEL_LOG_LEVEL === 'debug',
|
|
173
174
|
false
|
|
174
175
|
))
|
|
175
176
|
this.logger = options.logger
|
|
@@ -288,15 +289,6 @@ class Config {
|
|
|
288
289
|
)
|
|
289
290
|
|
|
290
291
|
const sampler = {
|
|
291
|
-
rules: coalesce(
|
|
292
|
-
options.samplingRules,
|
|
293
|
-
safeJsonParse(process.env.DD_TRACE_SAMPLING_RULES),
|
|
294
|
-
[]
|
|
295
|
-
).map(rule => {
|
|
296
|
-
return remapify(rule, {
|
|
297
|
-
sample_rate: 'sampleRate'
|
|
298
|
-
})
|
|
299
|
-
}),
|
|
300
292
|
spanSamplingRules: coalesce(
|
|
301
293
|
options.spanSamplingRules,
|
|
302
294
|
safeJsonParse(maybeFile(process.env.DD_SPAN_SAMPLING_RULES_FILE)),
|
|
@@ -444,6 +436,7 @@ class Config {
|
|
|
444
436
|
this._setValue(defaults, 'appsec.enabled', undefined)
|
|
445
437
|
this._setValue(defaults, 'appsec.obfuscatorKeyRegex', defaultWafObfuscatorKeyRegex)
|
|
446
438
|
this._setValue(defaults, 'appsec.obfuscatorValueRegex', defaultWafObfuscatorValueRegex)
|
|
439
|
+
this._setValue(defaults, 'appsec.rasp.enabled', false)
|
|
447
440
|
this._setValue(defaults, 'appsec.rateLimit', 100)
|
|
448
441
|
this._setValue(defaults, 'appsec.rules', undefined)
|
|
449
442
|
this._setValue(defaults, 'appsec.sca.enabled', null)
|
|
@@ -485,9 +478,12 @@ class Config {
|
|
|
485
478
|
this._setValue(defaults, 'peerServiceMapping', {})
|
|
486
479
|
this._setValue(defaults, 'plugins', true)
|
|
487
480
|
this._setValue(defaults, 'port', '8126')
|
|
488
|
-
this._setValue(defaults, 'profiling.enabled',
|
|
481
|
+
this._setValue(defaults, 'profiling.enabled', undefined)
|
|
489
482
|
this._setValue(defaults, 'profiling.exporters', 'agent')
|
|
490
483
|
this._setValue(defaults, 'profiling.sourceMap', true)
|
|
484
|
+
this._setValue(defaults, 'profiling.ssi', false)
|
|
485
|
+
this._setValue(defaults, 'profiling.heuristicsEnabled', false)
|
|
486
|
+
this._setValue(defaults, 'profiling.longLivedThreshold', undefined)
|
|
491
487
|
this._setValue(defaults, 'protocolVersion', '0.4')
|
|
492
488
|
this._setValue(defaults, 'queryStringObfuscation', qsRegex)
|
|
493
489
|
this._setValue(defaults, 'remoteConfig.enabled', true)
|
|
@@ -496,6 +492,7 @@ class Config {
|
|
|
496
492
|
this._setValue(defaults, 'runtimeMetrics', false)
|
|
497
493
|
this._setValue(defaults, 'sampleRate', undefined)
|
|
498
494
|
this._setValue(defaults, 'sampler.rateLimit', undefined)
|
|
495
|
+
this._setValue(defaults, 'sampler.rules', [])
|
|
499
496
|
this._setValue(defaults, 'scope', undefined)
|
|
500
497
|
this._setValue(defaults, 'service', service)
|
|
501
498
|
this._setValue(defaults, 'site', 'datadoghq.com')
|
|
@@ -531,6 +528,7 @@ class Config {
|
|
|
531
528
|
DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
532
529
|
DD_APPSEC_RULES,
|
|
533
530
|
DD_APPSEC_SCA_ENABLED,
|
|
531
|
+
DD_APPSEC_RASP_ENABLED,
|
|
534
532
|
DD_APPSEC_TRACE_RATE_LIMIT,
|
|
535
533
|
DD_APPSEC_WAF_TIMEOUT,
|
|
536
534
|
DD_DATA_STREAMS_ENABLED,
|
|
@@ -558,6 +556,7 @@ class Config {
|
|
|
558
556
|
DD_PROFILING_ENABLED,
|
|
559
557
|
DD_PROFILING_EXPORTERS,
|
|
560
558
|
DD_PROFILING_SOURCE_MAP,
|
|
559
|
+
DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD,
|
|
561
560
|
DD_REMOTE_CONFIGURATION_ENABLED,
|
|
562
561
|
DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS,
|
|
563
562
|
DD_RUNTIME_METRICS_ENABLED,
|
|
@@ -590,6 +589,7 @@ class Config {
|
|
|
590
589
|
DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED,
|
|
591
590
|
DD_TRACE_REPORT_HOSTNAME,
|
|
592
591
|
DD_TRACE_SAMPLE_RATE,
|
|
592
|
+
DD_TRACE_SAMPLING_RULES,
|
|
593
593
|
DD_TRACE_SCOPE,
|
|
594
594
|
DD_TRACE_SPAN_ATTRIBUTE_SCHEMA,
|
|
595
595
|
DD_TRACE_STARTUP_LOGS,
|
|
@@ -618,6 +618,7 @@ class Config {
|
|
|
618
618
|
this._setBoolean(env, 'appsec.enabled', DD_APPSEC_ENABLED)
|
|
619
619
|
this._setString(env, 'appsec.obfuscatorKeyRegex', DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP)
|
|
620
620
|
this._setString(env, 'appsec.obfuscatorValueRegex', DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP)
|
|
621
|
+
this._setBoolean(env, 'appsec.rasp.enabled', DD_APPSEC_RASP_ENABLED)
|
|
621
622
|
this._setValue(env, 'appsec.rateLimit', maybeInt(DD_APPSEC_TRACE_RATE_LIMIT))
|
|
622
623
|
this._setString(env, 'appsec.rules', DD_APPSEC_RULES)
|
|
623
624
|
// DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend
|
|
@@ -663,6 +664,17 @@ class Config {
|
|
|
663
664
|
this._setBoolean(env, 'profiling.enabled', coalesce(DD_EXPERIMENTAL_PROFILING_ENABLED, DD_PROFILING_ENABLED))
|
|
664
665
|
this._setString(env, 'profiling.exporters', DD_PROFILING_EXPORTERS)
|
|
665
666
|
this._setBoolean(env, 'profiling.sourceMap', DD_PROFILING_SOURCE_MAP && !isFalse(DD_PROFILING_SOURCE_MAP))
|
|
667
|
+
if (DD_PROFILING_ENABLED === 'auto' || DD_INJECTION_ENABLED) {
|
|
668
|
+
this._setBoolean(env, 'profiling.ssi', true)
|
|
669
|
+
if (DD_PROFILING_ENABLED === 'auto' || DD_INJECTION_ENABLED.split(',').includes('profiler')) {
|
|
670
|
+
this._setBoolean(env, 'profiling.heuristicsEnabled', true)
|
|
671
|
+
}
|
|
672
|
+
if (DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) {
|
|
673
|
+
// This is only used in testing to not have to wait 30s
|
|
674
|
+
this._setValue(env, 'profiling.longLivedThreshold', Number(DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD))
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
666
678
|
this._setString(env, 'protocolVersion', DD_TRACE_AGENT_PROTOCOL_VERSION)
|
|
667
679
|
this._setString(env, 'queryStringObfuscation', DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP)
|
|
668
680
|
this._setBoolean(env, 'remoteConfig.enabled', coalesce(
|
|
@@ -687,6 +699,7 @@ class Config {
|
|
|
687
699
|
}
|
|
688
700
|
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || OTEL_TRACES_SAMPLER_MAPPING[OTEL_TRACES_SAMPLER])
|
|
689
701
|
this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
|
|
702
|
+
this._setSamplingRule(env, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES)) // example
|
|
690
703
|
this._setString(env, 'scope', DD_TRACE_SCOPE)
|
|
691
704
|
this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service || OTEL_SERVICE_NAME)
|
|
692
705
|
this._setString(env, 'site', DD_SITE)
|
|
@@ -707,9 +720,7 @@ class Config {
|
|
|
707
720
|
this._setBoolean(env, 'telemetry.dependencyCollection', DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED)
|
|
708
721
|
this._setValue(env, 'telemetry.heartbeatInterval', maybeInt(Math.floor(DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000)))
|
|
709
722
|
const hasTelemetryLogsUsingFeatures =
|
|
710
|
-
|
|
711
|
-
isTrue(DD_PROFILING_ENABLED) ||
|
|
712
|
-
(typeof DD_INJECTION_ENABLED === 'string' && DD_INJECTION_ENABLED.split(',').includes('profiling'))
|
|
723
|
+
env['iast.enabled'] || env['profiling.enabled'] || env['profiling.heuristicsEnabled']
|
|
713
724
|
? true
|
|
714
725
|
: undefined
|
|
715
726
|
this._setBoolean(env, 'telemetry.logCollection', coalesce(DD_TELEMETRY_LOG_COLLECTION_ENABLED,
|
|
@@ -734,6 +745,7 @@ class Config {
|
|
|
734
745
|
this._setBoolean(opts, 'appsec.enabled', options.appsec.enabled)
|
|
735
746
|
this._setString(opts, 'appsec.obfuscatorKeyRegex', options.appsec.obfuscatorKeyRegex)
|
|
736
747
|
this._setString(opts, 'appsec.obfuscatorValueRegex', options.appsec.obfuscatorValueRegex)
|
|
748
|
+
this._setBoolean(opts, 'appsec.rasp.enabled', options.appsec.rasp?.enabled)
|
|
737
749
|
this._setValue(opts, 'appsec.rateLimit', maybeInt(options.appsec.rateLimit))
|
|
738
750
|
this._setString(opts, 'appsec.rules', options.appsec.rules)
|
|
739
751
|
this._setValue(opts, 'appsec.wafTimeout', maybeInt(options.appsec.wafTimeout))
|
|
@@ -786,6 +798,7 @@ class Config {
|
|
|
786
798
|
this._setUnit(opts, 'sampleRate', coalesce(options.sampleRate, options.ingestion.sampleRate))
|
|
787
799
|
const ingestion = options.ingestion || {}
|
|
788
800
|
this._setValue(opts, 'sampler.rateLimit', coalesce(options.rateLimit, ingestion.rateLimit))
|
|
801
|
+
this._setSamplingRule(opts, 'sampler.rules', options.samplingRules)
|
|
789
802
|
this._setString(opts, 'service', options.service || tags.service)
|
|
790
803
|
this._setString(opts, 'site', options.site)
|
|
791
804
|
if (options.spanAttributeSchema) {
|
|
@@ -933,6 +946,17 @@ class Config {
|
|
|
933
946
|
this._setArray(opts, 'headerTags', headerTags)
|
|
934
947
|
this._setTags(opts, 'tags', tags)
|
|
935
948
|
this._setBoolean(opts, 'tracing', options.tracing_enabled)
|
|
949
|
+
// ignore tags for now since rc sampling rule tags format is not supported
|
|
950
|
+
this._setSamplingRule(opts, 'sampler.rules', this._ignoreTags(options.trace_sample_rules))
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
_ignoreTags (samplingRules) {
|
|
954
|
+
if (samplingRules) {
|
|
955
|
+
for (const rule of samplingRules) {
|
|
956
|
+
delete rule.tags
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return samplingRules
|
|
936
960
|
}
|
|
937
961
|
|
|
938
962
|
_setBoolean (obj, name, value) {
|
|
@@ -959,12 +983,12 @@ class Config {
|
|
|
959
983
|
}
|
|
960
984
|
|
|
961
985
|
_setArray (obj, name, value) {
|
|
962
|
-
if (value
|
|
986
|
+
if (value == null) {
|
|
963
987
|
return this._setValue(obj, name, null)
|
|
964
988
|
}
|
|
965
989
|
|
|
966
990
|
if (typeof value === 'string') {
|
|
967
|
-
value = value
|
|
991
|
+
value = value.split(',')
|
|
968
992
|
}
|
|
969
993
|
|
|
970
994
|
if (Array.isArray(value)) {
|
|
@@ -972,6 +996,25 @@ class Config {
|
|
|
972
996
|
}
|
|
973
997
|
}
|
|
974
998
|
|
|
999
|
+
_setSamplingRule (obj, name, value) {
|
|
1000
|
+
if (value == null) {
|
|
1001
|
+
return this._setValue(obj, name, null)
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if (typeof value === 'string') {
|
|
1005
|
+
value = value.split(',')
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (Array.isArray(value)) {
|
|
1009
|
+
value = value.map(rule => {
|
|
1010
|
+
return remapify(rule, {
|
|
1011
|
+
sample_rate: 'sampleRate'
|
|
1012
|
+
})
|
|
1013
|
+
})
|
|
1014
|
+
this._setValue(obj, name, value)
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
975
1018
|
_setString (obj, name, value) {
|
|
976
1019
|
obj[name] = value ? String(value) : undefined // unset for empty strings
|
|
977
1020
|
}
|
|
@@ -120,7 +120,7 @@ class AgentEncoder {
|
|
|
120
120
|
this._encodeMap(bytes, span.metrics)
|
|
121
121
|
if (span.meta_struct) {
|
|
122
122
|
this._encodeString(bytes, 'meta_struct')
|
|
123
|
-
this.
|
|
123
|
+
this._encodeMetaStruct(bytes, span.meta_struct)
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
}
|
|
@@ -271,12 +271,48 @@ class AgentEncoder {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
_encodeMetaStruct (bytes, value) {
|
|
275
|
+
const keys = Array.isArray(value) ? [] : Object.keys(value)
|
|
276
|
+
const validKeys = keys.filter(key => {
|
|
277
|
+
const v = value[key]
|
|
278
|
+
return typeof v === 'string' ||
|
|
279
|
+
typeof v === 'number' ||
|
|
280
|
+
(v !== null && typeof v === 'object')
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
this._encodeMapPrefix(bytes, validKeys.length)
|
|
284
|
+
|
|
285
|
+
for (const key of validKeys) {
|
|
286
|
+
const v = value[key]
|
|
287
|
+
this._encodeString(bytes, key)
|
|
288
|
+
this._encodeObjectAsByteArray(bytes, v)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_encodeObjectAsByteArray (bytes, value) {
|
|
293
|
+
const prefixLength = 5
|
|
294
|
+
const offset = bytes.length
|
|
295
|
+
|
|
296
|
+
bytes.reserve(prefixLength)
|
|
297
|
+
bytes.length += prefixLength
|
|
298
|
+
|
|
299
|
+
this._encodeObject(bytes, value)
|
|
300
|
+
|
|
301
|
+
// we should do it after encoding the object to know the real length
|
|
302
|
+
const length = bytes.length - offset - prefixLength
|
|
303
|
+
bytes.buffer[offset] = 0xc6
|
|
304
|
+
bytes.buffer[offset + 1] = length >> 24
|
|
305
|
+
bytes.buffer[offset + 2] = length >> 16
|
|
306
|
+
bytes.buffer[offset + 3] = length >> 8
|
|
307
|
+
bytes.buffer[offset + 4] = length
|
|
308
|
+
}
|
|
309
|
+
|
|
274
310
|
_encodeObject (bytes, value, circularReferencesDetector = new Set()) {
|
|
275
311
|
circularReferencesDetector.add(value)
|
|
276
312
|
if (Array.isArray(value)) {
|
|
277
|
-
|
|
313
|
+
this._encodeObjectAsArray(bytes, value, circularReferencesDetector)
|
|
278
314
|
} else if (value !== null && typeof value === 'object') {
|
|
279
|
-
|
|
315
|
+
this._encodeObjectAsMap(bytes, value, circularReferencesDetector)
|
|
280
316
|
} else if (typeof value === 'string' || typeof value === 'number') {
|
|
281
317
|
this._encodeValue(bytes, value)
|
|
282
318
|
}
|
|
@@ -284,16 +320,19 @@ class AgentEncoder {
|
|
|
284
320
|
|
|
285
321
|
_encodeObjectAsMap (bytes, value, circularReferencesDetector) {
|
|
286
322
|
const keys = Object.keys(value)
|
|
287
|
-
const validKeys = keys.filter(key =>
|
|
288
|
-
|
|
289
|
-
typeof
|
|
290
|
-
|
|
323
|
+
const validKeys = keys.filter(key => {
|
|
324
|
+
const v = value[key]
|
|
325
|
+
return typeof v === 'string' ||
|
|
326
|
+
typeof v === 'number' ||
|
|
327
|
+
(v !== null && typeof v === 'object' && !circularReferencesDetector.has(v))
|
|
328
|
+
})
|
|
291
329
|
|
|
292
330
|
this._encodeMapPrefix(bytes, validKeys.length)
|
|
293
331
|
|
|
294
332
|
for (const key of validKeys) {
|
|
333
|
+
const v = value[key]
|
|
295
334
|
this._encodeString(bytes, key)
|
|
296
|
-
this._encodeObject(bytes,
|
|
335
|
+
this._encodeObject(bytes, v, circularReferencesDetector)
|
|
297
336
|
}
|
|
298
337
|
}
|
|
299
338
|
|
|
@@ -19,6 +19,7 @@ module.exports = name => {
|
|
|
19
19
|
return require('./ci-visibility/exporters/agent-proxy')
|
|
20
20
|
case exporters.JEST_WORKER:
|
|
21
21
|
case exporters.CUCUMBER_WORKER:
|
|
22
|
+
case exporters.MOCHA_WORKER:
|
|
22
23
|
return require('./ci-visibility/exporters/test-worker')
|
|
23
24
|
default:
|
|
24
25
|
return inAWSLambda && !usingLambdaExtension ? require('./exporters/log') : require('./exporters/agent')
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Writable } = require('stream')
|
|
4
|
+
|
|
5
|
+
const INITIAL_SIZE = 64 * 1024
|
|
6
|
+
|
|
7
|
+
class FlareFile extends Writable {
|
|
8
|
+
constructor () {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this.length = 0
|
|
12
|
+
|
|
13
|
+
this._buffer = Buffer.alloc(INITIAL_SIZE)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get data () {
|
|
17
|
+
return this._buffer.subarray(0, this.length)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_write (chunk, encoding, callback) {
|
|
21
|
+
const length = Buffer.byteLength(chunk)
|
|
22
|
+
|
|
23
|
+
this._reserve(length)
|
|
24
|
+
|
|
25
|
+
if (Buffer.isBuffer(chunk)) {
|
|
26
|
+
this.length += chunk.copy(this._buffer, this.length)
|
|
27
|
+
} else {
|
|
28
|
+
this.length += this._buffer.write(chunk, encoding)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
callback()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_reserve (length) {
|
|
35
|
+
while (this.length + length > this._buffer.length) {
|
|
36
|
+
const buffer = Buffer.alloc(this.length * 2)
|
|
37
|
+
|
|
38
|
+
this._buffer.copy(buffer)
|
|
39
|
+
this._buffer = buffer
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = FlareFile
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
const startupLog = require('../startup-log')
|
|
5
|
+
const FlareFile = require('./file')
|
|
6
|
+
const { LogChannel } = require('../log/channels')
|
|
7
|
+
const request = require('../exporters/common/request')
|
|
8
|
+
const FormData = require('../exporters/common/form-data')
|
|
9
|
+
|
|
10
|
+
const MAX_LOG_SIZE = 12 * 1024 * 1024 // 12MB soft limit
|
|
11
|
+
const TIMEOUT = 20 * 1000 * 60
|
|
12
|
+
|
|
13
|
+
let logChannel = null
|
|
14
|
+
let tracerLogs = null
|
|
15
|
+
let timer
|
|
16
|
+
let tracerConfig = null
|
|
17
|
+
|
|
18
|
+
const logger = {
|
|
19
|
+
debug: (msg) => recordLog(msg),
|
|
20
|
+
info: (msg) => recordLog(msg),
|
|
21
|
+
warn: (msg) => recordLog(msg),
|
|
22
|
+
error: (err) => recordLog(err.stack)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const flare = {
|
|
26
|
+
enable (tracerConfig_) {
|
|
27
|
+
tracerConfig = tracerConfig_
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
disable () {
|
|
31
|
+
tracerConfig = null
|
|
32
|
+
|
|
33
|
+
flare.cleanup()
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
prepare (logLevel) {
|
|
37
|
+
if (!tracerConfig) return
|
|
38
|
+
|
|
39
|
+
logChannel?.unsubscribe(logger)
|
|
40
|
+
logChannel = new LogChannel(logLevel)
|
|
41
|
+
logChannel.subscribe(logger)
|
|
42
|
+
tracerLogs = tracerLogs || new FlareFile()
|
|
43
|
+
timer = timer || setTimeout(flare.cleanup, TIMEOUT)
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
send (task) {
|
|
47
|
+
if (!tracerConfig) return
|
|
48
|
+
|
|
49
|
+
const tracerInfo = new FlareFile()
|
|
50
|
+
|
|
51
|
+
tracerInfo.write(JSON.stringify(startupLog.tracerInfo(), null, 2))
|
|
52
|
+
|
|
53
|
+
flare._sendFile(task, tracerInfo, 'tracer_info.txt')
|
|
54
|
+
flare._sendFile(task, tracerLogs, 'tracer_logs.txt')
|
|
55
|
+
|
|
56
|
+
flare.cleanup()
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
cleanup () {
|
|
60
|
+
logChannel?.unsubscribe(logger)
|
|
61
|
+
timer = clearTimeout(timer)
|
|
62
|
+
logChannel = null
|
|
63
|
+
tracerLogs = null
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
_sendFile (task, file, filename) {
|
|
67
|
+
if (!file) return
|
|
68
|
+
|
|
69
|
+
const form = new FormData()
|
|
70
|
+
|
|
71
|
+
form.append('case_id', task.case_id)
|
|
72
|
+
form.append('hostname', task.hostname)
|
|
73
|
+
form.append('email', task.user_handle)
|
|
74
|
+
form.append('source', 'tracer_nodejs')
|
|
75
|
+
form.append('flare_file', file.data, { filename })
|
|
76
|
+
|
|
77
|
+
request(form, {
|
|
78
|
+
url: tracerConfig.url,
|
|
79
|
+
hostname: tracerConfig.hostname,
|
|
80
|
+
port: tracerConfig.port,
|
|
81
|
+
method: 'POST',
|
|
82
|
+
path: '/tracer_flare/v1',
|
|
83
|
+
headers: form.getHeaders()
|
|
84
|
+
}, (err) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
log.error(err)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function recordLog (msg) {
|
|
93
|
+
if (tracerLogs.length > MAX_LOG_SIZE) return
|
|
94
|
+
|
|
95
|
+
tracerLogs.write(`${msg}\n`) // TODO: gzip
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = flare
|