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.
Files changed (54) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/index.d.ts +39 -0
  3. package/package.json +8 -9
  4. package/packages/datadog-instrumentations/orchestrion.yml +52 -0
  5. package/packages/datadog-instrumentations/src/cucumber.js +2 -1
  6. package/packages/datadog-instrumentations/src/jest.js +11 -2
  7. package/packages/datadog-instrumentations/src/langchain.js +49 -53
  8. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  9. package/packages/datadog-instrumentations/src/mocha/utils.js +11 -3
  10. package/packages/datadog-instrumentations/src/orchestrion-config/index.js +5 -0
  11. package/packages/datadog-instrumentations/src/playwright.js +14 -2
  12. package/packages/datadog-instrumentations/src/vitest.js +11 -3
  13. package/packages/datadog-plugin-cucumber/src/index.js +11 -4
  14. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -5
  15. package/packages/datadog-plugin-jest/src/index.js +11 -4
  16. package/packages/datadog-plugin-langchain/src/index.js +18 -12
  17. package/packages/datadog-plugin-langchain/src/tracing.js +66 -6
  18. package/packages/datadog-plugin-mocha/src/index.js +17 -5
  19. package/packages/datadog-plugin-mongodb-core/src/index.js +5 -1
  20. package/packages/datadog-plugin-playwright/src/index.js +10 -3
  21. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  22. package/packages/datadog-shimmer/src/shimmer.js +3 -42
  23. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +3 -3
  24. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +0 -3
  25. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +24 -11
  26. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +3 -32
  27. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +98 -56
  28. package/packages/dd-trace/src/appsec/sdk/index.js +23 -1
  29. package/packages/dd-trace/src/appsec/sdk/noop.js +10 -0
  30. package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -1
  31. package/packages/dd-trace/src/appsec/sdk/track_event.js +129 -8
  32. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +0 -1
  33. package/packages/dd-trace/src/appsec/telemetry/index.js +2 -2
  34. package/packages/dd-trace/src/appsec/telemetry/user.js +2 -2
  35. package/packages/dd-trace/src/config.js +9 -0
  36. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -1
  37. package/packages/dd-trace/src/debugger/devtools_client/condition.js +36 -2
  38. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -33
  39. package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -1
  40. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  41. package/packages/dd-trace/src/exporters/common/docker.js +37 -7
  42. package/packages/dd-trace/src/exporters/common/request.js +1 -4
  43. package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
  44. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +62 -3
  45. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -0
  46. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +2 -1
  47. package/packages/dd-trace/src/log/index.js +2 -0
  48. package/packages/dd-trace/src/log/writer.js +19 -2
  49. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -3
  50. package/packages/dd-trace/src/opentracing/span.js +10 -0
  51. package/packages/dd-trace/src/plugins/util/test.js +7 -0
  52. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -5
  53. package/packages/dd-trace/src/profiling/profilers/wall.js +3 -1
  54. 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, isNotLibraryFile } = require('./filter')
7
+ const { isPrivateModule, isDdTrace } = require('./filter')
8
8
  const { csiMethods } = require('./csi-methods')
9
9
  const { getName } = require('../telemetry/verbosity')
10
- const { getRewriteFunction, incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
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 getGetOriginalPathAndLineFromSourceMapFunction (chainSourceMap, getOriginalPathAndLineFromSourceMap) {
32
- if (chainSourceMap) {
33
- return function (path, line, column) {
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
- if (isPrivateModule(path) && isNotLibraryFile(path)) {
37
- return { path, line, column }
38
- } else {
39
- return getOriginalPathAndLineFromSourceMap(path, line, column)
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
- const getOriginalPathAndLineFromSourceMap = iastRewriter.getOriginalPathAndLineFromSourceMap
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('[ASM] Unable to initialize TaintTracking Rewriter', e)
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
- const rewriteFn = getRewriteFunction(rewriter)
93
- return function (content, filename) {
96
+ let delegate = function (content, filename) {
94
97
  try {
95
- if (isPrivateModule(filename) && isNotLibraryFile(filename)) {
96
- const rewritten = rewriteFn(content, filename)
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
- if (rewritten?.literalsResult && hardcodedSecretCh.hasSubscribers) {
99
- hardcodedSecretCh.publish(rewritten.literalsResult)
100
- }
108
+ incrementTelemetryIfNeeded(rewritten.metrics)
101
109
 
102
- if (rewritten?.content) {
103
- return compileMethod.apply(this, [rewritten.content, filename])
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('[ASM] Error rewriting file %s', filename, e)
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
- const rewriter = getRewriter(telemetryVerbosity)
134
- if (rewriter) {
135
- const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
136
- if (!pstDescriptor || pstDescriptor.configurable) {
137
- Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
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('[ASM] Error enabling TaintTracking Rewriter', e)
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('[ASM] Error enabling ESM Rewriter', e)
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 disableRewriter () {
201
- shimmer.unwrap(Module.prototype, '_compile')
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('[ASM] Error disabling TaintTracking rewriter', e)
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
- enableRewriter, disableRewriter, getOriginalPathAndLineFromSourceMap
261
+ enable, disable, getOriginalPathAndLineFromSourceMap
220
262
  }
@@ -1,16 +1,38 @@
1
1
  'use strict'
2
2
 
3
- const { trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent } = require('./track_event')
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
- for (const metadataKey of Object.keys(fields)) {
82
- tags[`appsec.events.${eventName}.${metadataKey}`] = '' + fields[metadataKey]
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.id) {
213
+ if (user?.id) {
97
214
  persistent[addresses.USER_ID] = '' + user.id
98
215
  }
99
216
 
100
- if (user.login) {
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 (framework, eventType) {
146
+ function incrementSdkEventMetric (eventType, sdkVersion) {
147
147
  if (!enabled) return
148
148
 
149
- incrementSdkEvent(framework, eventType)
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: 'v1'
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(' || ')