dd-trace 3.55.0 → 3.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +15 -0
- package/package.json +2 -1
- package/packages/datadog-instrumentations/src/fetch.js +6 -45
- package/packages/datadog-instrumentations/src/helpers/fetch.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +77 -10
- package/packages/datadog-instrumentations/src/mongoose.js +2 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +6 -1
- package/packages/datadog-instrumentations/src/selenium.js +69 -0
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-cypress/src/support.js +19 -3
- package/packages/datadog-plugin-fetch/src/index.js +17 -11
- package/packages/datadog-plugin-jest/src/index.js +7 -2
- package/packages/datadog-plugin-mocha/src/index.js +4 -5
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-playwright/src/index.js +2 -2
- package/packages/datadog-plugin-selenium/src/index.js +71 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +70 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +14 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +12 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-rule-type.js +6 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +5 -50
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +742 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +539 -66
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/reporter.js +11 -10
- package/packages/dd-trace/src/appsec/telemetry.js +36 -7
- 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 -1
- package/packages/dd-trace/src/config.js +94 -9
- package/packages/dd-trace/src/dogstatsd.js +13 -11
- package/packages/dd-trace/src/index.js +5 -1
- package/packages/dd-trace/src/noop/dogstatsd.js +11 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +10 -4
- package/packages/dd-trace/src/opentracing/span.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +34 -3
- package/packages/dd-trace/src/profiling/config.js +8 -4
- package/packages/dd-trace/src/profiling/profiler.js +4 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +33 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry.js +167 -0
- package/packages/dd-trace/src/proxy.js +7 -1
- package/packages/dd-trace/src/tagger.js +13 -3
- package/packages/dd-trace/src/telemetry/index.js +5 -4
- package/packages/dd-trace/src/telemetry/metrics.js +2 -2
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const telemetryMetrics = require('../telemetry/metrics')
|
|
4
|
+
const profilersNamespace = telemetryMetrics.manager.namespace('profilers')
|
|
5
|
+
const performance = require('perf_hooks').performance
|
|
6
|
+
const dc = require('dc-polyfill')
|
|
7
|
+
const { isTrue, isFalse } = require('../util')
|
|
8
|
+
|
|
9
|
+
// If the process lived for less than 30 seconds, it's considered short-lived
|
|
10
|
+
const DEFAULT_SHORT_LIVED_THRESHOLD = 30000
|
|
11
|
+
|
|
12
|
+
const EnablementChoice = {
|
|
13
|
+
MANUALLY_ENABLED: Symbol('SSITelemetry.EnablementChoice.MANUALLY_ENABLED'),
|
|
14
|
+
SSI_ENABLED: Symbol('SSITelemetry.EnablementChoice.SSI_ENABLED'),
|
|
15
|
+
SSI_NOT_ENABLED: Symbol('SSITelemetry.EnablementChoice.SSI_NOT_ENABLED'),
|
|
16
|
+
DISABLED: Symbol('SSITelemetry.EnablementChoice.MANUALLY_DISABLED')
|
|
17
|
+
}
|
|
18
|
+
Object.freeze(EnablementChoice)
|
|
19
|
+
|
|
20
|
+
function getEnablementChoiceFromEnv () {
|
|
21
|
+
const { DD_PROFILING_ENABLED, DD_INJECTION_ENABLED } = process.env
|
|
22
|
+
if (DD_INJECTION_ENABLED === undefined || isFalse(DD_PROFILING_ENABLED)) {
|
|
23
|
+
return EnablementChoice.DISABLED
|
|
24
|
+
} else if (DD_INJECTION_ENABLED.split(',').includes('profiling')) {
|
|
25
|
+
return EnablementChoice.SSI_ENABLED
|
|
26
|
+
} else if (isTrue(DD_PROFILING_ENABLED)) {
|
|
27
|
+
return EnablementChoice.MANUALLY_ENABLED
|
|
28
|
+
} else {
|
|
29
|
+
return EnablementChoice.SSI_NOT_ENABLED
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function enablementChoiceToTagValue (enablementChoice) {
|
|
34
|
+
switch (enablementChoice) {
|
|
35
|
+
case EnablementChoice.MANUALLY_ENABLED:
|
|
36
|
+
return 'manually_enabled'
|
|
37
|
+
case EnablementChoice.SSI_ENABLED:
|
|
38
|
+
return 'ssi_enabled'
|
|
39
|
+
case EnablementChoice.SSI_NOT_ENABLED:
|
|
40
|
+
return 'not_enabled'
|
|
41
|
+
case EnablementChoice.MANUALLY_DISABLED:
|
|
42
|
+
// Can't emit this one as a tag
|
|
43
|
+
throw new Error('Invalid enablement choice')
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* This class emits telemetry metrics about the profiler behavior under SSI. It will only emit metrics
|
|
49
|
+
* when the application closes, and will emit the following metrics:
|
|
50
|
+
* - `number_of_profiles`: The number of profiles that were submitted
|
|
51
|
+
* - `number_of_runtime_id`: The number of runtime IDs in the app (always 1 for Node.js)
|
|
52
|
+
* It will also add tags describing the state of heuristics triggers, the enablement choice, and whether
|
|
53
|
+
* actual profiles were sent (as opposed to mock profiles). There is a mock profiler that is activated
|
|
54
|
+
* when the profiler is not enabled, and it will emit mock profile submission events at the same cadence
|
|
55
|
+
* the profiler would, providing insight into how many profiles would've been emitted if SSI enabled
|
|
56
|
+
* profiling. Note that telemetry is per tracer instance, and each worker thread will have its own instance.
|
|
57
|
+
*/
|
|
58
|
+
class SSITelemetry {
|
|
59
|
+
constructor ({
|
|
60
|
+
enablementChoice = getEnablementChoiceFromEnv(),
|
|
61
|
+
shortLivedThreshold = DEFAULT_SHORT_LIVED_THRESHOLD
|
|
62
|
+
} = {}) {
|
|
63
|
+
if (!Object.values(EnablementChoice).includes(enablementChoice)) {
|
|
64
|
+
throw new Error('Invalid enablement choice')
|
|
65
|
+
}
|
|
66
|
+
if (typeof shortLivedThreshold !== 'number' || shortLivedThreshold <= 0) {
|
|
67
|
+
throw new Error('Short-lived threshold must be a positive number')
|
|
68
|
+
}
|
|
69
|
+
this.enablementChoice = enablementChoice
|
|
70
|
+
this.shortLivedThreshold = shortLivedThreshold
|
|
71
|
+
|
|
72
|
+
this.hasSentProfiles = false
|
|
73
|
+
this.noSpan = true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
enabled () {
|
|
77
|
+
return this.enablementChoice !== EnablementChoice.DISABLED
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
start () {
|
|
81
|
+
if (this.enabled()) {
|
|
82
|
+
// Used to determine short-livedness of the process. We could use the process start time as the
|
|
83
|
+
// reference point, but the tracer initialization point is more relevant, as we couldn't be
|
|
84
|
+
// collecting profiles earlier anyway. The difference is not particularly significant if the
|
|
85
|
+
// tracer is initialized early in the process lifetime.
|
|
86
|
+
this.startTime = performance.now()
|
|
87
|
+
|
|
88
|
+
this._onSpanCreated = this._onSpanCreated.bind(this)
|
|
89
|
+
this._onProfileSubmitted = this._onProfileSubmitted.bind(this)
|
|
90
|
+
this._onMockProfileSubmitted = this._onMockProfileSubmitted.bind(this)
|
|
91
|
+
this._onAppClosing = this._onAppClosing.bind(this)
|
|
92
|
+
|
|
93
|
+
dc.subscribe('dd-trace:span:start', this._onSpanCreated)
|
|
94
|
+
dc.subscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
|
|
95
|
+
dc.subscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
|
|
96
|
+
dc.subscribe('datadog:telemetry:app-closing', this._onAppClosing)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_onSpanCreated () {
|
|
101
|
+
this.noSpan = false
|
|
102
|
+
dc.unsubscribe('dd-trace:span:start', this._onSpanCreated)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
_onProfileSubmitted () {
|
|
106
|
+
this.hasSentProfiles = true
|
|
107
|
+
this._incProfileCount()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_onMockProfileSubmitted () {
|
|
111
|
+
this._incProfileCount()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_incProfileCount () {
|
|
115
|
+
this._ensureProfileMetrics()
|
|
116
|
+
this._profileCount.inc()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_ensureProfileMetrics () {
|
|
120
|
+
const decision = []
|
|
121
|
+
if (this.noSpan) {
|
|
122
|
+
decision.push('no_span')
|
|
123
|
+
}
|
|
124
|
+
if (performance.now() - this.startTime < this.shortLivedThreshold) {
|
|
125
|
+
decision.push('short_lived')
|
|
126
|
+
}
|
|
127
|
+
if (decision.length === 0) {
|
|
128
|
+
decision.push('triggered')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const tags = [
|
|
132
|
+
'installation:ssi',
|
|
133
|
+
`enablement_choice:${enablementChoiceToTagValue(this.enablementChoice)}`,
|
|
134
|
+
`has_sent_profiles:${this.hasSentProfiles}`,
|
|
135
|
+
`heuristic_hypothetical_decision:${decision.join('_')}`
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
this._profileCount = profilersNamespace.count('ssi_heuristic.number_of_profiles', tags)
|
|
139
|
+
this._runtimeIdCount = profilersNamespace.count('ssi_heuristic.number_of_runtime_id', tags)
|
|
140
|
+
|
|
141
|
+
if (!this._emittedRuntimeId && decision[0] === 'triggered') {
|
|
142
|
+
// Tags won't change anymore, so we can emit the runtime ID metric now
|
|
143
|
+
this._emittedRuntimeId = true
|
|
144
|
+
this._runtimeIdCount.inc()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_onAppClosing () {
|
|
149
|
+
this._ensureProfileMetrics()
|
|
150
|
+
// Last ditch effort to emit a runtime ID count metric
|
|
151
|
+
if (!this._emittedRuntimeId) {
|
|
152
|
+
this._emittedRuntimeId = true
|
|
153
|
+
this._runtimeIdCount.inc()
|
|
154
|
+
}
|
|
155
|
+
// So we have the metrics in the final state
|
|
156
|
+
this._profileCount.inc(0)
|
|
157
|
+
|
|
158
|
+
dc.unsubscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
|
|
159
|
+
dc.unsubscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
|
|
160
|
+
dc.unsubscribe('datadog:telemetry:app-closing', this._onAppClosing)
|
|
161
|
+
if (this.noSpan) {
|
|
162
|
+
dc.unsubscribe('dd-trace:span:start', this._onSpanCreated)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { SSITelemetry, EnablementChoice }
|
|
@@ -11,7 +11,9 @@ const PluginManager = require('./plugin_manager')
|
|
|
11
11
|
const remoteConfig = require('./appsec/remote_config')
|
|
12
12
|
const AppsecSdk = require('./appsec/sdk')
|
|
13
13
|
const dogstatsd = require('./dogstatsd')
|
|
14
|
+
const NoopDogStatsDClient = require('./noop/dogstatsd')
|
|
14
15
|
const spanleak = require('./spanleak')
|
|
16
|
+
const { SSITelemetry } = require('./profiling/ssi-telemetry')
|
|
15
17
|
|
|
16
18
|
class Tracer extends NoopProxy {
|
|
17
19
|
constructor () {
|
|
@@ -20,7 +22,7 @@ class Tracer extends NoopProxy {
|
|
|
20
22
|
this._initialized = false
|
|
21
23
|
this._nomenclature = nomenclature
|
|
22
24
|
this._pluginManager = new PluginManager(this)
|
|
23
|
-
this.dogstatsd = new
|
|
25
|
+
this.dogstatsd = new NoopDogStatsDClient()
|
|
24
26
|
this._tracingInitialized = false
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -72,6 +74,8 @@ class Tracer extends NoopProxy {
|
|
|
72
74
|
require('./serverless').maybeStartServerlessMiniAgent(config)
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
const ssiTelemetry = new SSITelemetry()
|
|
78
|
+
ssiTelemetry.start()
|
|
75
79
|
if (config.profiling.enabled) {
|
|
76
80
|
// do not stop tracer initialization if the profiler fails to be imported
|
|
77
81
|
try {
|
|
@@ -80,6 +84,8 @@ class Tracer extends NoopProxy {
|
|
|
80
84
|
} catch (e) {
|
|
81
85
|
log.error(e)
|
|
82
86
|
}
|
|
87
|
+
} else if (ssiTelemetry.enabled()) {
|
|
88
|
+
require('./profiling/ssi-telemetry-mock-profiler').start(config)
|
|
83
89
|
}
|
|
84
90
|
if (!this._profilerStarted) {
|
|
85
91
|
this._profilerStarted = Promise.resolve(false)
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('./log')
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const otelTagMap = {
|
|
6
|
+
'deployment.environment': 'env',
|
|
7
|
+
'service.name': 'service',
|
|
8
|
+
'service.version': 'version'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function add (carrier, keyValuePairs, parseOtelTags = false) {
|
|
6
12
|
if (!carrier || !keyValuePairs) return
|
|
7
13
|
|
|
8
14
|
if (Array.isArray(keyValuePairs)) {
|
|
@@ -13,12 +19,16 @@ function add (carrier, keyValuePairs) {
|
|
|
13
19
|
if (typeof keyValuePairs === 'string') {
|
|
14
20
|
const segments = keyValuePairs.split(',')
|
|
15
21
|
for (const segment of segments) {
|
|
16
|
-
const separatorIndex = segment.indexOf(':')
|
|
22
|
+
const separatorIndex = parseOtelTags ? segment.indexOf('=') : segment.indexOf(':')
|
|
17
23
|
if (separatorIndex === -1) continue
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
let key = segment.slice(0, separatorIndex)
|
|
20
26
|
const value = segment.slice(separatorIndex + 1)
|
|
21
27
|
|
|
28
|
+
if (parseOtelTags && key in otelTagMap) {
|
|
29
|
+
key = otelTagMap[key]
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
carrier[key.trim()] = value.trim()
|
|
23
33
|
}
|
|
24
34
|
} else {
|
|
@@ -10,6 +10,7 @@ const logs = require('./logs')
|
|
|
10
10
|
|
|
11
11
|
const telemetryStartChannel = dc.channel('datadog:telemetry:start')
|
|
12
12
|
const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
|
|
13
|
+
const telemetryAppClosingChannel = dc.channel('datadog:telemetry:app-closing')
|
|
13
14
|
|
|
14
15
|
let config
|
|
15
16
|
let pluginManager
|
|
@@ -129,12 +130,12 @@ function appClosing () {
|
|
|
129
130
|
if (!config?.telemetry?.enabled) {
|
|
130
131
|
return
|
|
131
132
|
}
|
|
133
|
+
// Give chance to listeners to update metrics before shutting down.
|
|
134
|
+
telemetryAppClosingChannel.publish()
|
|
132
135
|
const { reqType, payload } = createPayload('app-closing')
|
|
133
136
|
sendData(config, application, host, reqType, payload)
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
metricsManager.send(config, application, host)
|
|
137
|
-
}
|
|
137
|
+
// We flush before shutting down.
|
|
138
|
+
metricsManager.send(config, application, host)
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
function onBeforeExit () {
|