dd-trace 5.46.0 → 5.48.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 -2
- package/index.d.ts +39 -0
- package/package.json +8 -9
- package/packages/datadog-instrumentations/orchestrion.yml +52 -0
- package/packages/datadog-instrumentations/src/cucumber.js +2 -1
- package/packages/datadog-instrumentations/src/jest.js +11 -2
- package/packages/datadog-instrumentations/src/langchain.js +49 -53
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +11 -3
- package/packages/datadog-instrumentations/src/orchestrion-config/index.js +5 -0
- package/packages/datadog-instrumentations/src/playwright.js +14 -2
- package/packages/datadog-instrumentations/src/vitest.js +11 -3
- package/packages/datadog-plugin-cucumber/src/index.js +11 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -5
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-langchain/src/index.js +18 -12
- package/packages/datadog-plugin-langchain/src/tracing.js +66 -6
- package/packages/datadog-plugin-mocha/src/index.js +17 -5
- package/packages/datadog-plugin-mongodb-core/src/index.js +5 -1
- package/packages/datadog-plugin-playwright/src/index.js +10 -3
- package/packages/datadog-plugin-vitest/src/index.js +13 -8
- package/packages/datadog-shimmer/src/shimmer.js +3 -42
- package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +0 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +24 -11
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +3 -32
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +98 -56
- package/packages/dd-trace/src/appsec/sdk/index.js +23 -1
- package/packages/dd-trace/src/appsec/sdk/noop.js +10 -0
- package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +129 -8
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +0 -1
- package/packages/dd-trace/src/appsec/telemetry/index.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/user.js +2 -2
- package/packages/dd-trace/src/config.js +9 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +36 -2
- package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -33
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -1
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/exporters/common/docker.js +37 -7
- package/packages/dd-trace/src/exporters/common/request.js +1 -4
- package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +62 -3
- package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -0
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +2 -1
- package/packages/dd-trace/src/log/index.js +2 -0
- package/packages/dd-trace/src/log/writer.js +19 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -3
- package/packages/dd-trace/src/opentracing/span.js +10 -0
- package/packages/dd-trace/src/plugins/util/test.js +7 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -5
- package/packages/dd-trace/src/profiling/profilers/wall.js +3 -1
- package/packages/dd-trace/src/proxy.js +5 -1
|
@@ -4,44 +4,48 @@ const Module = require('module')
|
|
|
4
4
|
const { pathToFileURL } = require('url')
|
|
5
5
|
const { MessageChannel } = require('worker_threads')
|
|
6
6
|
const shimmer = require('../../../../../datadog-shimmer')
|
|
7
|
-
const { isPrivateModule,
|
|
7
|
+
const { isPrivateModule, isDdTrace } = require('./filter')
|
|
8
8
|
const { csiMethods } = require('./csi-methods')
|
|
9
9
|
const { getName } = require('../telemetry/verbosity')
|
|
10
|
-
const
|
|
10
|
+
const telemetry = require('../telemetry')
|
|
11
|
+
const { incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
|
|
11
12
|
const dc = require('dc-polyfill')
|
|
12
13
|
const log = require('../../../log')
|
|
13
14
|
const { isMainThread } = require('worker_threads')
|
|
14
15
|
const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
|
|
16
|
+
const orchestrionConfig = require('../../../../../datadog-instrumentations/src/orchestrion-config')
|
|
15
17
|
|
|
18
|
+
let config
|
|
16
19
|
const hardcodedSecretCh = dc.channel('datadog:secrets:result')
|
|
17
20
|
let rewriter
|
|
21
|
+
let unwrapCompile = () => {}
|
|
18
22
|
let getPrepareStackTrace, cacheRewrittenSourceMap
|
|
19
23
|
let kSymbolPrepareStackTrace
|
|
20
24
|
let esmRewriterEnabled = false
|
|
21
25
|
|
|
22
|
-
let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
|
|
23
|
-
return { path, line, column }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
26
|
function isFlagPresent (flag) {
|
|
27
27
|
return process.env.NODE_OPTIONS?.includes(flag) ||
|
|
28
28
|
process.execArgv?.some(arg => arg.includes(flag))
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
|
|
32
|
+
return { path, line, column }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function setGetOriginalPathAndLineFromSourceMapFunction (chainSourceMap, { getOriginalPathAndLineFromSourceMap }) {
|
|
36
|
+
if (!getOriginalPathAndLineFromSourceMap) return
|
|
37
|
+
|
|
38
|
+
getRewriterOriginalPathAndLineFromSourceMap = chainSourceMap
|
|
39
|
+
? (path, line, column) => {
|
|
34
40
|
// if --enable-source-maps is present stacktraces of the rewritten files contain the original path, file and
|
|
35
41
|
// column because the sourcemap chaining is done during the rewriting process so we can skip it
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if (isPrivateModule(path) && !isDdTrace(path)) {
|
|
43
|
+
return { path, line, column }
|
|
44
|
+
} else {
|
|
45
|
+
return getOriginalPathAndLineFromSourceMap(path, line, column)
|
|
46
|
+
}
|
|
40
47
|
}
|
|
41
|
-
|
|
42
|
-
} else {
|
|
43
|
-
return getOriginalPathAndLineFromSourceMap
|
|
44
|
-
}
|
|
48
|
+
: getOriginalPathAndLineFromSourceMap
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
function getRewriter (telemetryVerbosity) {
|
|
@@ -54,19 +58,16 @@ function getRewriter (telemetryVerbosity) {
|
|
|
54
58
|
cacheRewrittenSourceMap = iastRewriter.cacheRewrittenSourceMap
|
|
55
59
|
|
|
56
60
|
const chainSourceMap = isFlagPresent('--enable-source-maps')
|
|
57
|
-
|
|
58
|
-
if (getOriginalPathAndLineFromSourceMap) {
|
|
59
|
-
getRewriterOriginalPathAndLineFromSourceMap =
|
|
60
|
-
getGetOriginalPathAndLineFromSourceMapFunction(chainSourceMap, getOriginalPathAndLineFromSourceMap)
|
|
61
|
-
}
|
|
61
|
+
setGetOriginalPathAndLineFromSourceMapFunction(chainSourceMap, iastRewriter)
|
|
62
62
|
|
|
63
63
|
rewriter = new Rewriter({
|
|
64
64
|
csiMethods,
|
|
65
65
|
telemetryVerbosity: getName(telemetryVerbosity),
|
|
66
|
-
chainSourceMap
|
|
66
|
+
chainSourceMap,
|
|
67
|
+
orchestrion: orchestrionConfig
|
|
67
68
|
})
|
|
68
69
|
} catch (e) {
|
|
69
|
-
log.error('
|
|
70
|
+
log.error('Unable to initialize Rewriter', e)
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
return rewriter
|
|
@@ -74,6 +75,9 @@ function getRewriter (telemetryVerbosity) {
|
|
|
74
75
|
|
|
75
76
|
let originalPrepareStackTrace
|
|
76
77
|
function getPrepareStackTraceAccessor () {
|
|
78
|
+
if (!getPrepareStackTrace) {
|
|
79
|
+
getPrepareStackTrace = require('@datadog/wasm-js-rewriter/js/stack-trace').getPrepareStackTrace
|
|
80
|
+
}
|
|
77
81
|
originalPrepareStackTrace = Error.prepareStackTrace
|
|
78
82
|
let actual = getPrepareStackTrace(originalPrepareStackTrace)
|
|
79
83
|
return {
|
|
@@ -89,25 +93,42 @@ function getPrepareStackTraceAccessor () {
|
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
function getCompileMethodFn (compileMethod) {
|
|
92
|
-
|
|
93
|
-
return function (content, filename) {
|
|
96
|
+
let delegate = function (content, filename) {
|
|
94
97
|
try {
|
|
95
|
-
if (
|
|
96
|
-
|
|
98
|
+
if (isDdTrace(filename)) {
|
|
99
|
+
return compileMethod.apply(this, [content, filename])
|
|
100
|
+
}
|
|
101
|
+
if (!isPrivateModule(filename) || !config.iast?.enabled) {
|
|
102
|
+
return compileMethod.apply(this, [content, filename])
|
|
103
|
+
}
|
|
104
|
+
// TODO when we have CJS support for orchestrion and taint-tracking, add
|
|
105
|
+
// them here as appropriate
|
|
106
|
+
const rewritten = rewriter.rewrite(content, filename, ['iast'])
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
hardcodedSecretCh.publish(rewritten.literalsResult)
|
|
100
|
-
}
|
|
108
|
+
incrementTelemetryIfNeeded(rewritten.metrics)
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
if (rewritten?.literalsResult && hardcodedSecretCh.hasSubscribers) {
|
|
111
|
+
hardcodedSecretCh.publish(rewritten.literalsResult)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (rewritten?.content) {
|
|
115
|
+
return compileMethod.apply(this, [rewritten.content, filename])
|
|
105
116
|
}
|
|
106
117
|
} catch (e) {
|
|
107
|
-
log.error('
|
|
118
|
+
log.error('Error rewriting file %s', filename, e)
|
|
108
119
|
}
|
|
109
120
|
return compileMethod.apply(this, [content, filename])
|
|
110
121
|
}
|
|
122
|
+
|
|
123
|
+
const shim = function () {
|
|
124
|
+
return delegate.apply(this, arguments)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
unwrapCompile = function () {
|
|
128
|
+
delegate = compileMethod
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return shim
|
|
111
132
|
}
|
|
112
133
|
|
|
113
134
|
function esmRewritePostProcess (rewritten, filename) {
|
|
@@ -128,20 +149,31 @@ function esmRewritePostProcess (rewritten, filename) {
|
|
|
128
149
|
}
|
|
129
150
|
}
|
|
130
151
|
|
|
152
|
+
let shimmedPrepareStackTrace = false
|
|
153
|
+
function shimPrepareStackTrace () {
|
|
154
|
+
if (shimmedPrepareStackTrace) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
|
|
158
|
+
if (!pstDescriptor || pstDescriptor.configurable) {
|
|
159
|
+
Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
|
|
160
|
+
}
|
|
161
|
+
shimmedPrepareStackTrace = true
|
|
162
|
+
}
|
|
163
|
+
|
|
131
164
|
function enableRewriter (telemetryVerbosity) {
|
|
132
165
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
166
|
+
if (config.iast?.enabled) {
|
|
167
|
+
const rewriter = getRewriter(telemetryVerbosity)
|
|
168
|
+
if (rewriter) {
|
|
169
|
+
shimPrepareStackTrace()
|
|
170
|
+
shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
|
|
138
171
|
}
|
|
139
|
-
shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
|
|
140
172
|
}
|
|
141
173
|
|
|
142
174
|
enableEsmRewriter(telemetryVerbosity)
|
|
143
175
|
} catch (e) {
|
|
144
|
-
log.error('
|
|
176
|
+
log.error('Error enabling Rewriter', e)
|
|
145
177
|
}
|
|
146
178
|
}
|
|
147
179
|
|
|
@@ -155,6 +187,8 @@ function isEsmConfigured () {
|
|
|
155
187
|
|
|
156
188
|
function enableEsmRewriter (telemetryVerbosity) {
|
|
157
189
|
if (isMainThread && Module.register && !esmRewriterEnabled && isEsmConfigured()) {
|
|
190
|
+
shimPrepareStackTrace()
|
|
191
|
+
|
|
158
192
|
esmRewriterEnabled = true
|
|
159
193
|
|
|
160
194
|
const { port1, port2 } = new MessageChannel()
|
|
@@ -175,30 +209,31 @@ function enableEsmRewriter (telemetryVerbosity) {
|
|
|
175
209
|
port1.unref()
|
|
176
210
|
port2.unref()
|
|
177
211
|
|
|
178
|
-
const chainSourceMap = isFlagPresent('--enable-source-maps')
|
|
179
|
-
const data = {
|
|
180
|
-
port: port2,
|
|
181
|
-
csiMethods,
|
|
182
|
-
telemetryVerbosity,
|
|
183
|
-
chainSourceMap
|
|
184
|
-
}
|
|
185
|
-
|
|
186
212
|
try {
|
|
187
213
|
Module.register('./rewriter-esm.mjs', {
|
|
188
214
|
parentURL: pathToFileURL(__filename),
|
|
189
215
|
transferList: [port2],
|
|
190
|
-
data
|
|
216
|
+
data: {
|
|
217
|
+
port: port2,
|
|
218
|
+
csiMethods,
|
|
219
|
+
telemetryVerbosity,
|
|
220
|
+
chainSourceMap: isFlagPresent('--enable-source-maps'),
|
|
221
|
+
orchestrionConfig,
|
|
222
|
+
iastEnabled: config?.iast?.enabled
|
|
223
|
+
}
|
|
191
224
|
})
|
|
192
225
|
} catch (e) {
|
|
193
|
-
log.error('
|
|
226
|
+
log.error('Error enabling ESM Rewriter', e)
|
|
194
227
|
port1.close()
|
|
195
228
|
port2.close()
|
|
196
229
|
}
|
|
230
|
+
|
|
231
|
+
cacheRewrittenSourceMap = require('@datadog/wasm-js-rewriter/js/source-map').cacheRewrittenSourceMap
|
|
197
232
|
}
|
|
198
233
|
}
|
|
199
234
|
|
|
200
|
-
function
|
|
201
|
-
|
|
235
|
+
function disable () {
|
|
236
|
+
unwrapCompile()
|
|
202
237
|
|
|
203
238
|
if (!Error.prepareStackTrace?.[kSymbolPrepareStackTrace]) return
|
|
204
239
|
|
|
@@ -206,8 +241,10 @@ function disableRewriter () {
|
|
|
206
241
|
delete Error.prepareStackTrace
|
|
207
242
|
|
|
208
243
|
Error.prepareStackTrace = originalPrepareStackTrace
|
|
244
|
+
|
|
245
|
+
shimmedPrepareStackTrace = false
|
|
209
246
|
} catch (e) {
|
|
210
|
-
log.warn('
|
|
247
|
+
log.warn('Error disabling Rewriter', e)
|
|
211
248
|
}
|
|
212
249
|
}
|
|
213
250
|
|
|
@@ -215,6 +252,11 @@ function getOriginalPathAndLineFromSourceMap ({ path, line, column }) {
|
|
|
215
252
|
return getRewriterOriginalPathAndLineFromSourceMap(path, line, column)
|
|
216
253
|
}
|
|
217
254
|
|
|
255
|
+
function enable (configArg) {
|
|
256
|
+
config = configArg
|
|
257
|
+
enableRewriter(telemetry.verbosity || 'OFF')
|
|
258
|
+
}
|
|
259
|
+
|
|
218
260
|
module.exports = {
|
|
219
|
-
|
|
261
|
+
enable, disable, getOriginalPathAndLineFromSourceMap
|
|
220
262
|
}
|
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
trackUserLoginSuccessEvent,
|
|
5
|
+
trackUserLoginFailureEvent,
|
|
6
|
+
trackCustomEvent,
|
|
7
|
+
trackUserLoginSuccessV2,
|
|
8
|
+
trackUserLoginFailureV2
|
|
9
|
+
} = require('./track_event')
|
|
4
10
|
const { checkUserAndSetUser, blockRequest } = require('./user_blocking')
|
|
5
11
|
const { setTemplates } = require('../blocking')
|
|
6
12
|
const { setUser } = require('./set_user')
|
|
7
13
|
|
|
14
|
+
class EventTrackingV2 {
|
|
15
|
+
constructor (tracer) {
|
|
16
|
+
this._tracer = tracer
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
trackUserLoginSuccess (login, user, metadata) {
|
|
20
|
+
trackUserLoginSuccessV2(this._tracer, login, user, metadata)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
trackUserLoginFailure (login, exists, metadata) {
|
|
24
|
+
trackUserLoginFailureV2(this._tracer, login, exists, metadata)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
class AppsecSdk {
|
|
9
29
|
constructor (tracer, config) {
|
|
10
30
|
this._tracer = tracer
|
|
11
31
|
if (config) {
|
|
12
32
|
setTemplates(config)
|
|
13
33
|
}
|
|
34
|
+
|
|
35
|
+
this.eventTrackingV2 = new EventTrackingV2(tracer)
|
|
14
36
|
}
|
|
15
37
|
|
|
16
38
|
trackUserLoginSuccessEvent (user, metadata) {
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
class NoopEventTrackingV2 {
|
|
4
|
+
trackUserLoginSuccess () {}
|
|
5
|
+
|
|
6
|
+
trackUserLoginFailure () {}
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
class NoopAppsecSdk {
|
|
10
|
+
constructor () {
|
|
11
|
+
this.eventTrackingV2 = new NoopEventTrackingV2()
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
trackUserLoginSuccessEvent () {}
|
|
5
15
|
|
|
6
16
|
trackUserLoginFailureEvent () {}
|
|
@@ -9,6 +9,8 @@ function setUserTags (user, rootSpan) {
|
|
|
9
9
|
for (const k of Object.keys(user)) {
|
|
10
10
|
rootSpan.setTag(`usr.${k}`, '' + user[k])
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
rootSpan.setTag('_dd.appsec.user.collection_mode', 'sdk')
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
function setUser (tracer, user) {
|
|
@@ -24,7 +26,6 @@ function setUser (tracer, user) {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
setUserTags(user, rootSpan)
|
|
27
|
-
rootSpan.setTag('_dd.appsec.user.collection_mode', 'sdk')
|
|
28
29
|
|
|
29
30
|
const persistent = {
|
|
30
31
|
[addresses.USER_ID]: '' + user.id
|
|
@@ -9,6 +9,9 @@ const addresses = require('../addresses')
|
|
|
9
9
|
const { ASM } = require('../../standalone/product')
|
|
10
10
|
const { incrementSdkEventMetric } = require('../telemetry')
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated in favor of trackUserLoginSuccessV2
|
|
14
|
+
*/
|
|
12
15
|
function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
13
16
|
// TODO: better user check here and in _setUser() ?
|
|
14
17
|
if (!user || !user.id) {
|
|
@@ -16,7 +19,7 @@ function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
|
16
19
|
return
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
incrementSdkEventMetric('login_success')
|
|
22
|
+
incrementSdkEventMetric('login_success', 'v1')
|
|
20
23
|
|
|
21
24
|
const rootSpan = getRootSpan(tracer)
|
|
22
25
|
if (!rootSpan) {
|
|
@@ -35,6 +38,9 @@ function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
|
35
38
|
runWaf('users.login.success', { id: user.id, login })
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated in favor of trackUserLoginFailureV2
|
|
43
|
+
*/
|
|
38
44
|
function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
|
|
39
45
|
if (!userId || typeof userId !== 'string') {
|
|
40
46
|
log.warn('[ASM] Invalid userId provided to trackUserLoginFailureEvent')
|
|
@@ -52,7 +58,7 @@ function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
|
|
|
52
58
|
|
|
53
59
|
runWaf('users.login.failure', { login: userId })
|
|
54
60
|
|
|
55
|
-
incrementSdkEventMetric('login_failure')
|
|
61
|
+
incrementSdkEventMetric('login_failure', 'v1')
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
function trackCustomEvent (tracer, eventName, metadata) {
|
|
@@ -63,7 +69,112 @@ function trackCustomEvent (tracer, eventName, metadata) {
|
|
|
63
69
|
|
|
64
70
|
trackEvent(eventName, metadata, 'trackCustomEvent', getRootSpan(tracer))
|
|
65
71
|
|
|
66
|
-
incrementSdkEventMetric('custom')
|
|
72
|
+
incrementSdkEventMetric('custom', 'v1')
|
|
73
|
+
|
|
74
|
+
if (eventName === 'users.login.success' || eventName === 'users.login.failure') {
|
|
75
|
+
runWaf(eventName)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function trackUserLoginSuccessV2 (tracer, login, user, metadata) {
|
|
80
|
+
if (!login || typeof login !== 'string') {
|
|
81
|
+
log.warn('[ASM] Invalid login provided to eventTrackingV2.trackUserLoginSuccess')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
incrementSdkEventMetric('login_success', 'v2')
|
|
86
|
+
|
|
87
|
+
const rootSpan = getRootSpan(tracer)
|
|
88
|
+
if (!rootSpan) {
|
|
89
|
+
log.warn('[ASM] Root span not available in eventTrackingV2.trackUserLoginSuccess')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const wafData = { login }
|
|
94
|
+
|
|
95
|
+
metadata = {
|
|
96
|
+
'usr.login': login,
|
|
97
|
+
...metadata
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (user) {
|
|
101
|
+
if (typeof user !== 'object') {
|
|
102
|
+
user = { id: user }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (user.id) {
|
|
106
|
+
wafData.id = user.id
|
|
107
|
+
setUserTags(user, rootSpan)
|
|
108
|
+
metadata.usr = user
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
trackEvent('users.login.success', metadata, 'eventTrackingV2.trackUserLoginSuccess', rootSpan)
|
|
113
|
+
|
|
114
|
+
runWaf('users.login.success', wafData)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function trackUserLoginFailureV2 (tracer, login, exists, metadata) {
|
|
118
|
+
if (!login || typeof login !== 'string') {
|
|
119
|
+
log.warn('[ASM] Invalid login provided to eventTrackingV2.trackUserLoginFailure')
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
incrementSdkEventMetric('login_failure', 'v2')
|
|
124
|
+
|
|
125
|
+
const rootSpan = getRootSpan(tracer)
|
|
126
|
+
if (!rootSpan) {
|
|
127
|
+
log.warn('[ASM] Root span not available in eventTrackingV2.trackUserLoginFailure')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const wafData = { login }
|
|
132
|
+
|
|
133
|
+
if (typeof exists === 'object' && metadata === undefined) {
|
|
134
|
+
metadata = exists
|
|
135
|
+
exists = false
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
metadata = {
|
|
139
|
+
'usr.login': login,
|
|
140
|
+
'usr.exists': exists ? 'true' : 'false',
|
|
141
|
+
...metadata
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
trackEvent('users.login.failure', metadata, 'eventTrackingV2.trackUserLoginFailure', rootSpan)
|
|
145
|
+
|
|
146
|
+
runWaf('users.login.failure', wafData)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function flattenFields (fields, depth = 0) {
|
|
150
|
+
if (depth > 4) {
|
|
151
|
+
return {
|
|
152
|
+
truncated: true
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const result = {}
|
|
157
|
+
let truncated = false
|
|
158
|
+
for (const key of Object.keys(fields)) {
|
|
159
|
+
const value = fields[key]
|
|
160
|
+
|
|
161
|
+
if (value && typeof value === 'object') {
|
|
162
|
+
const { result: flatValue, truncated: inheritTruncated } = flattenFields(value, depth + 1)
|
|
163
|
+
truncated = truncated || inheritTruncated
|
|
164
|
+
|
|
165
|
+
if (flatValue) {
|
|
166
|
+
for (const flatKey of Object.keys(flatValue)) {
|
|
167
|
+
result[`${key}.${flatKey}`] = flatValue[flatKey]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
if (value !== undefined) {
|
|
172
|
+
result[key] = value
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { result, truncated }
|
|
67
178
|
}
|
|
68
179
|
|
|
69
180
|
function trackEvent (eventName, fields, sdkMethodName, rootSpan) {
|
|
@@ -78,8 +189,14 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan) {
|
|
|
78
189
|
}
|
|
79
190
|
|
|
80
191
|
if (fields) {
|
|
81
|
-
|
|
82
|
-
|
|
192
|
+
const { result: flatFields, truncated } = flattenFields(fields)
|
|
193
|
+
|
|
194
|
+
if (truncated) {
|
|
195
|
+
log.warn('[ASM] Too deep object provided in the SDK method %s, object truncated', sdkMethodName)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (const metadataKey of Object.keys(flatFields)) {
|
|
199
|
+
tags[`appsec.events.${eventName}.${metadataKey}`] = '' + flatFields[metadataKey]
|
|
83
200
|
}
|
|
84
201
|
}
|
|
85
202
|
|
|
@@ -93,11 +210,11 @@ function runWaf (eventName, user) {
|
|
|
93
210
|
[`server.business_logic.${eventName}`]: null
|
|
94
211
|
}
|
|
95
212
|
|
|
96
|
-
if (user
|
|
213
|
+
if (user?.id) {
|
|
97
214
|
persistent[addresses.USER_ID] = '' + user.id
|
|
98
215
|
}
|
|
99
216
|
|
|
100
|
-
if (user
|
|
217
|
+
if (user?.login) {
|
|
101
218
|
persistent[addresses.USER_LOGIN] = '' + user.login
|
|
102
219
|
}
|
|
103
220
|
|
|
@@ -107,5 +224,9 @@ function runWaf (eventName, user) {
|
|
|
107
224
|
module.exports = {
|
|
108
225
|
trackUserLoginSuccessEvent,
|
|
109
226
|
trackUserLoginFailureEvent,
|
|
110
|
-
trackCustomEvent
|
|
227
|
+
trackCustomEvent,
|
|
228
|
+
trackUserLoginSuccessV2,
|
|
229
|
+
trackUserLoginFailureV2,
|
|
230
|
+
trackEvent,
|
|
231
|
+
runWaf
|
|
111
232
|
}
|
|
@@ -23,7 +23,6 @@ function checkUserAndSetUser (tracer, user) {
|
|
|
23
23
|
if (rootSpan) {
|
|
24
24
|
if (!rootSpan.context()._tags['usr.id']) {
|
|
25
25
|
setUserTags(user, rootSpan)
|
|
26
|
-
rootSpan.setTag('_dd.appsec.user.collection_mode', 'sdk')
|
|
27
26
|
}
|
|
28
27
|
} else {
|
|
29
28
|
log.warn('[ASM] Root span not available in isUserBlocked')
|
|
@@ -143,10 +143,10 @@ function incrementMissingUserIdMetric (framework, eventType) {
|
|
|
143
143
|
incrementMissingUserId(framework, eventType)
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function incrementSdkEventMetric (
|
|
146
|
+
function incrementSdkEventMetric (eventType, sdkVersion) {
|
|
147
147
|
if (!enabled) return
|
|
148
148
|
|
|
149
|
-
incrementSdkEvent(
|
|
149
|
+
incrementSdkEvent(eventType, sdkVersion)
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
function getRequestMetrics (req) {
|
|
@@ -18,10 +18,10 @@ function incrementMissingUserId (framework, eventType) {
|
|
|
18
18
|
}).inc()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function incrementSdkEvent (eventType) {
|
|
21
|
+
function incrementSdkEvent (eventType, sdkVersion = 'v1') {
|
|
22
22
|
appsecMetrics.count('sdk.event', {
|
|
23
23
|
event_type: eventType,
|
|
24
|
-
sdk_version:
|
|
24
|
+
sdk_version: sdkVersion
|
|
25
25
|
}).inc()
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -63,6 +63,8 @@ const otelDdEnvMapping = {
|
|
|
63
63
|
|
|
64
64
|
const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none'])
|
|
65
65
|
|
|
66
|
+
const VALID_PROPAGATION_BEHAVIOR_EXTRACT = new Set(['continue', 'restart', 'ignore'])
|
|
67
|
+
|
|
66
68
|
const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
|
|
67
69
|
|
|
68
70
|
function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
|
|
@@ -584,6 +586,7 @@ class Config {
|
|
|
584
586
|
this._setValue(defaults, 'traceId128BitGenerationEnabled', true)
|
|
585
587
|
this._setValue(defaults, 'traceId128BitLoggingEnabled', true)
|
|
586
588
|
this._setValue(defaults, 'tracePropagationExtractFirst', false)
|
|
589
|
+
this._setValue(defaults, 'tracePropagationBehaviorExtract', 'continue')
|
|
587
590
|
this._setValue(defaults, 'tracePropagationStyle.inject', ['datadog', 'tracecontext', 'baggage'])
|
|
588
591
|
this._setValue(defaults, 'tracePropagationStyle.extract', ['datadog', 'tracecontext', 'baggage'])
|
|
589
592
|
this._setValue(defaults, 'tracePropagationStyle.otelPropagators', false)
|
|
@@ -746,6 +749,7 @@ class Config {
|
|
|
746
749
|
DD_TRACE_PARTIAL_FLUSH_MIN_SPANS,
|
|
747
750
|
DD_TRACE_PEER_SERVICE_MAPPING,
|
|
748
751
|
DD_TRACE_PROPAGATION_EXTRACT_FIRST,
|
|
752
|
+
DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT,
|
|
749
753
|
DD_TRACE_PROPAGATION_STYLE,
|
|
750
754
|
DD_TRACE_PROPAGATION_STYLE_INJECT,
|
|
751
755
|
DD_TRACE_PROPAGATION_STYLE_EXTRACT,
|
|
@@ -967,6 +971,11 @@ class Config {
|
|
|
967
971
|
this._setBoolean(env, 'traceId128BitGenerationEnabled', DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
|
|
968
972
|
this._setBoolean(env, 'traceId128BitLoggingEnabled', DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)
|
|
969
973
|
this._setBoolean(env, 'tracePropagationExtractFirst', DD_TRACE_PROPAGATION_EXTRACT_FIRST)
|
|
974
|
+
const stringPropagationBehaviorExtract = String(DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT)
|
|
975
|
+
this._setValue(env, 'tracePropagationBehaviorExtract',
|
|
976
|
+
VALID_PROPAGATION_BEHAVIOR_EXTRACT.has(stringPropagationBehaviorExtract)
|
|
977
|
+
? stringPropagationBehaviorExtract
|
|
978
|
+
: 'continue')
|
|
970
979
|
this._setBoolean(env, 'tracePropagationStyle.otelPropagators',
|
|
971
980
|
DD_TRACE_PROPAGATION_STYLE ||
|
|
972
981
|
DD_TRACE_PROPAGATION_STYLE_INJECT ||
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { getGeneratedPosition } = require('./source-maps')
|
|
4
4
|
const lock = require('./lock')()
|
|
5
5
|
const session = require('./session')
|
|
6
|
-
const compileCondition = require('./condition')
|
|
6
|
+
const { compile: compileCondition, compileSegments, templateRequiresEvaluation } = require('./condition')
|
|
7
7
|
const { MAX_SNAPSHOTS_PER_SECOND_PER_PROBE, MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE } = require('./defaults')
|
|
8
8
|
const { findScriptFromPartialPath, locationToBreakpoint, breakpointToProbes, probeToLocation } = require('./state')
|
|
9
9
|
const log = require('../../log')
|
|
@@ -26,6 +26,13 @@ async function addBreakpoint (probe) {
|
|
|
26
26
|
probe.location = { file, lines: [String(lineNumber)] }
|
|
27
27
|
delete probe.where
|
|
28
28
|
|
|
29
|
+
// Optimize for fast calculations when probe is hit
|
|
30
|
+
probe.templateRequiresEvaluation = templateRequiresEvaluation(probe.segments)
|
|
31
|
+
if (probe.templateRequiresEvaluation) {
|
|
32
|
+
probe.template = compileSegments(probe.segments)
|
|
33
|
+
}
|
|
34
|
+
delete probe.segments
|
|
35
|
+
|
|
29
36
|
// Optimize for fast calculations when probe is hit
|
|
30
37
|
const snapshotsPerSecond = probe.sampling?.snapshotsPerSecond ?? (probe.captureSnapshot
|
|
31
38
|
? MAX_SNAPSHOTS_PER_SECOND_PER_PROBE
|
|
@@ -41,6 +48,10 @@ async function addBreakpoint (probe) {
|
|
|
41
48
|
const { url, scriptId, sourceMapURL, source } = script
|
|
42
49
|
|
|
43
50
|
if (sourceMapURL) {
|
|
51
|
+
log.debug(
|
|
52
|
+
'[debugger:devtools_client] Translating location using source map for %s:%d:%d (probe: %s, version: %d)',
|
|
53
|
+
file, lineNumber, columnNumber, probe.id, probe.version
|
|
54
|
+
);
|
|
44
55
|
({ line: lineNumber, column: columnNumber } = await getGeneratedPosition(url, source, lineNumber, sourceMapURL))
|
|
45
56
|
}
|
|
46
57
|
|
|
@@ -156,6 +167,8 @@ function stop () {
|
|
|
156
167
|
|
|
157
168
|
// Only if all probes have a condition can we use a compound condition.
|
|
158
169
|
// Otherwise, we need to evaluate each probe individually once the breakpoint is hit.
|
|
170
|
+
// TODO: Handle errors - if there's 2 conditons, and one fails but the other returns true, we should still pause the
|
|
171
|
+
// breakpoint
|
|
159
172
|
function compileCompoundCondition (probes) {
|
|
160
173
|
return probes.every(p => p.condition)
|
|
161
174
|
? probes.map(p => p.condition).filter(Boolean).join(' || ')
|