dd-trace 4.46.0 → 4.47.1

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 (89) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +20 -8
  3. package/package.json +9 -3
  4. package/packages/datadog-instrumentations/src/cucumber.js +290 -53
  5. package/packages/datadog-instrumentations/src/jest.js +3 -1
  6. package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
  7. package/packages/datadog-instrumentations/src/microgateway-core.js +3 -1
  8. package/packages/datadog-instrumentations/src/mocha/main.js +139 -54
  9. package/packages/datadog-instrumentations/src/mocha/utils.js +35 -15
  10. package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
  11. package/packages/datadog-instrumentations/src/openai.js +4 -2
  12. package/packages/datadog-instrumentations/src/pg.js +59 -4
  13. package/packages/datadog-instrumentations/src/vitest.js +184 -9
  14. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
  15. package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
  16. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  17. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
  18. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  19. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  20. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +36 -6
  21. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  22. package/packages/datadog-plugin-http/src/client.js +1 -42
  23. package/packages/datadog-plugin-http2/src/client.js +1 -26
  24. package/packages/datadog-plugin-jest/src/index.js +17 -1
  25. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
  26. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
  27. package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
  28. package/packages/datadog-plugin-mocha/src/index.js +18 -0
  29. package/packages/datadog-plugin-openai/src/index.js +27 -18
  30. package/packages/datadog-plugin-playwright/src/index.js +9 -0
  31. package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
  32. package/packages/datadog-plugin-vitest/src/index.js +68 -3
  33. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  34. package/packages/dd-trace/src/appsec/channels.js +4 -2
  35. package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
  36. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
  37. package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
  38. package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
  39. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
  40. package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
  41. package/packages/dd-trace/src/appsec/remote_config/manager.js +89 -51
  42. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
  43. package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
  44. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
  45. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -0
  46. package/packages/dd-trace/src/config.js +61 -10
  47. package/packages/dd-trace/src/constants.js +11 -1
  48. package/packages/dd-trace/src/data_streams_context.js +3 -0
  49. package/packages/dd-trace/src/datastreams/fnv.js +23 -0
  50. package/packages/dd-trace/src/datastreams/pathway.js +12 -5
  51. package/packages/dd-trace/src/datastreams/processor.js +35 -0
  52. package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
  53. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
  54. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
  55. package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
  56. package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
  57. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
  59. package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
  60. package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
  61. package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
  62. package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
  63. package/packages/dd-trace/src/debugger/index.js +92 -0
  64. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
  65. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  66. package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
  67. package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
  68. package/packages/dd-trace/src/payload-tagging/index.js +93 -0
  69. package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
  70. package/packages/dd-trace/src/plugin_manager.js +11 -10
  71. package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
  72. package/packages/dd-trace/src/plugins/util/env.js +5 -2
  73. package/packages/dd-trace/src/plugins/util/test.js +26 -2
  74. package/packages/dd-trace/src/profiling/config.js +5 -0
  75. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  76. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
  77. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
  78. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
  79. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
  80. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
  81. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
  82. package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
  83. package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
  84. package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
  85. package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
  86. package/packages/dd-trace/src/profiling/ssi-heuristics.js +10 -2
  87. package/packages/dd-trace/src/proxy.js +10 -3
  88. package/packages/dd-trace/src/span_stats.js +4 -2
  89. package/packages/dd-trace/src/appsec/rasp.js +0 -176
@@ -6,6 +6,9 @@ const END_TIMESTAMP_LABEL = 'end_timestamp_ns'
6
6
  const THREAD_NAME_LABEL = 'thread name'
7
7
  const OS_THREAD_ID_LABEL = 'os thread id'
8
8
  const THREAD_ID_LABEL = 'thread id'
9
+ const SPAN_ID_LABEL = 'span id'
10
+ const LOCAL_ROOT_SPAN_ID_LABEL = 'local root span id'
11
+
9
12
  const threadNamePrefix = isMainThread ? 'Main' : `Worker #${threadId}`
10
13
  const eventLoopThreadName = `${threadNamePrefix} Event Loop`
11
14
 
@@ -38,6 +41,8 @@ module.exports = {
38
41
  THREAD_NAME_LABEL,
39
42
  THREAD_ID_LABEL,
40
43
  OS_THREAD_ID_LABEL,
44
+ SPAN_ID_LABEL,
45
+ LOCAL_ROOT_SPAN_ID_LABEL,
41
46
  threadNamePrefix,
42
47
  eventLoopThreadName,
43
48
  getNonJSThreadsLabels,
@@ -7,7 +7,13 @@ const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../
7
7
  const { WEB } = require('../../../../../ext/types')
8
8
  const runtimeMetrics = require('../../runtime_metrics')
9
9
  const telemetryMetrics = require('../../telemetry/metrics')
10
- const { END_TIMESTAMP_LABEL, getNonJSThreadsLabels, getThreadLabels } = require('./shared')
10
+ const {
11
+ END_TIMESTAMP_LABEL,
12
+ SPAN_ID_LABEL,
13
+ LOCAL_ROOT_SPAN_ID_LABEL,
14
+ getNonJSThreadsLabels,
15
+ getThreadLabels
16
+ } = require('./shared')
11
17
 
12
18
  const beforeCh = dc.channel('dd-trace:storage:before')
13
19
  const enterCh = dc.channel('dd-trace:storage:enter')
@@ -275,10 +281,10 @@ class NativeWallProfiler {
275
281
  }
276
282
 
277
283
  if (spanId) {
278
- labels['span id'] = spanId
284
+ labels[SPAN_ID_LABEL] = spanId
279
285
  }
280
286
  if (rootSpanId) {
281
- labels['local root span id'] = rootSpanId
287
+ labels[LOCAL_ROOT_SPAN_ID_LABEL] = rootSpanId
282
288
  }
283
289
  if (webTags && Object.keys(webTags).length !== 0) {
284
290
  labels['trace endpoint'] = endpointNameFromTags(webTags)
@@ -3,6 +3,7 @@
3
3
  const telemetryMetrics = require('../telemetry/metrics')
4
4
  const profilersNamespace = telemetryMetrics.manager.namespace('profilers')
5
5
  const dc = require('dc-polyfill')
6
+ const log = require('../log')
6
7
 
7
8
  // If the process lives for at least 30 seconds, it's considered long-lived
8
9
  const DEFAULT_LONG_LIVED_THRESHOLD = 30000
@@ -40,9 +41,14 @@ class SSIHeuristics {
40
41
 
41
42
  const longLivedThreshold = config.profiling.longLivedThreshold || DEFAULT_LONG_LIVED_THRESHOLD
42
43
  if (typeof longLivedThreshold !== 'number' || longLivedThreshold <= 0) {
43
- throw new Error('Long-lived threshold must be a positive number')
44
+ this.longLivedThreshold = DEFAULT_LONG_LIVED_THRESHOLD
45
+ log.warn(
46
+ `Invalid SSIHeuristics.longLivedThreshold value: ${config.profiling.longLivedThreshold}. ` +
47
+ `Using default value: ${DEFAULT_LONG_LIVED_THRESHOLD}`
48
+ )
49
+ } else {
50
+ this.longLivedThreshold = longLivedThreshold
44
51
  }
45
- this.longLivedThreshold = longLivedThreshold
46
52
 
47
53
  this.hasSentProfiles = false
48
54
  this.noSpan = true
@@ -94,6 +100,8 @@ class SSIHeuristics {
94
100
  })
95
101
  break
96
102
  default:
103
+ // injection hardening: only usage is internal, one call site with
104
+ // a function and another with undefined, so we can throw here.
97
105
  throw new TypeError('callback must be a function or undefined')
98
106
  }
99
107
  }
@@ -5,6 +5,7 @@ const Config = require('./config')
5
5
  const runtimeMetrics = require('./runtime_metrics')
6
6
  const log = require('./log')
7
7
  const { setStartupLogPluginManager } = require('./startup-log')
8
+ const DynamicInstrumentation = require('./debugger')
8
9
  const telemetry = require('./telemetry')
9
10
  const nomenclature = require('./service-naming')
10
11
  const PluginManager = require('./plugin_manager')
@@ -83,7 +84,7 @@ class Tracer extends NoopProxy {
83
84
  if (config.remoteConfig.enabled && !config.isCiVisibility) {
84
85
  const rc = remoteConfig.enable(config, this._modules.appsec)
85
86
 
86
- rc.on('APM_TRACING', (action, conf) => {
87
+ rc.setProductHandler('APM_TRACING', (action, conf) => {
87
88
  if (action === 'unapply') {
88
89
  config.configure({}, true)
89
90
  } else {
@@ -92,7 +93,7 @@ class Tracer extends NoopProxy {
92
93
  this._enableOrDisableTracing(config)
93
94
  })
94
95
 
95
- rc.on('AGENT_CONFIG', (action, conf) => {
96
+ rc.setProductHandler('AGENT_CONFIG', (action, conf) => {
96
97
  if (!conf?.name?.startsWith('flare-log-level.')) return
97
98
 
98
99
  if (action === 'unapply') {
@@ -103,13 +104,17 @@ class Tracer extends NoopProxy {
103
104
  }
104
105
  })
105
106
 
106
- rc.on('AGENT_TASK', (action, conf) => {
107
+ rc.setProductHandler('AGENT_TASK', (action, conf) => {
107
108
  if (action === 'unapply' || !conf) return
108
109
  if (conf.task_type !== 'tracer_flare' || !conf.args) return
109
110
 
110
111
  this._flare.enable(config)
111
112
  this._flare.module.send(conf.args)
112
113
  })
114
+
115
+ if (config.dynamicInstrumentationEnabled) {
116
+ DynamicInstrumentation.start(config, rc)
117
+ }
113
118
  }
114
119
 
115
120
  if (config.isGCPFunction || config.isAzureFunction) {
@@ -196,12 +201,14 @@ class Tracer extends NoopProxy {
196
201
  if (this._tracingInitialized) {
197
202
  this._tracer.configure(config)
198
203
  this._pluginManager.configure(config)
204
+ DynamicInstrumentation.configure(config)
199
205
  setStartupLogPluginManager(this._pluginManager)
200
206
  }
201
207
  }
202
208
 
203
209
  profilerStarted () {
204
210
  if (!this._profilerStarted) {
211
+ // injection hardening: this is only ever invoked from tests.
205
212
  throw new Error('profilerStarted() must be called after init()')
206
213
  }
207
214
  return this._profilerStarted
@@ -127,7 +127,8 @@ class SpanStatsProcessor {
127
127
  url,
128
128
  env,
129
129
  tags,
130
- appsec
130
+ appsec,
131
+ version
131
132
  } = {}) {
132
133
  this.exporter = new SpanStatsExporter({
133
134
  hostname,
@@ -143,6 +144,7 @@ class SpanStatsProcessor {
143
144
  this.env = env
144
145
  this.tags = tags || {}
145
146
  this.sequence = 0
147
+ this.version = version
146
148
 
147
149
  if (this.enabled) {
148
150
  this.timer = setInterval(this.onInterval.bind(this), interval * 1e3)
@@ -157,7 +159,7 @@ class SpanStatsProcessor {
157
159
  this.exporter.export({
158
160
  Hostname: this.hostname,
159
161
  Env: this.env,
160
- Version: version,
162
+ Version: this.version || version,
161
163
  Stats: serialized,
162
164
  Lang: 'javascript',
163
165
  TracerVersion: pkg.version,
@@ -1,176 +0,0 @@
1
- 'use strict'
2
-
3
- const { storage } = require('../../../datadog-core')
4
- const web = require('./../plugins/util/web')
5
- const addresses = require('./addresses')
6
- const { httpClientRequestStart, setUncaughtExceptionCaptureCallbackStart } = require('./channels')
7
- const { reportStackTrace } = require('./stack_trace')
8
- const waf = require('./waf')
9
- const { getBlockingAction, block } = require('./blocking')
10
- const log = require('../log')
11
-
12
- const RULE_TYPES = {
13
- SSRF: 'ssrf'
14
- }
15
-
16
- class DatadogRaspAbortError extends Error {
17
- constructor (req, res, blockingAction) {
18
- super('DatadogRaspAbortError')
19
- this.name = 'DatadogRaspAbortError'
20
- this.req = req
21
- this.res = res
22
- this.blockingAction = blockingAction
23
- }
24
- }
25
-
26
- let config, abortOnUncaughtException
27
-
28
- function removeAllListeners (emitter, event) {
29
- const listeners = emitter.listeners(event)
30
- emitter.removeAllListeners(event)
31
-
32
- let cleaned = false
33
- return function () {
34
- if (cleaned === true) {
35
- return
36
- }
37
- cleaned = true
38
-
39
- for (let i = 0; i < listeners.length; ++i) {
40
- emitter.on(event, listeners[i])
41
- }
42
- }
43
- }
44
-
45
- function findDatadogRaspAbortError (err, deep = 10) {
46
- if (err instanceof DatadogRaspAbortError) {
47
- return err
48
- }
49
-
50
- if (err.cause && deep > 0) {
51
- return findDatadogRaspAbortError(err.cause, deep - 1)
52
- }
53
- }
54
-
55
- function handleUncaughtExceptionMonitor (err) {
56
- const abortError = findDatadogRaspAbortError(err)
57
- if (!abortError) return
58
-
59
- const { req, res, blockingAction } = abortError
60
- block(req, res, web.root(req), null, blockingAction)
61
-
62
- if (!process.hasUncaughtExceptionCaptureCallback()) {
63
- const cleanUp = removeAllListeners(process, 'uncaughtException')
64
- const handler = () => {
65
- process.removeListener('uncaughtException', handler)
66
- }
67
-
68
- setTimeout(() => {
69
- process.removeListener('uncaughtException', handler)
70
- cleanUp()
71
- })
72
-
73
- process.on('uncaughtException', handler)
74
- } else {
75
- // uncaughtException event is not executed when hasUncaughtExceptionCaptureCallback is true
76
- let previousCb
77
- const cb = ({ currentCallback, abortController }) => {
78
- setUncaughtExceptionCaptureCallbackStart.unsubscribe(cb)
79
- if (!currentCallback) {
80
- abortController.abort()
81
- return
82
- }
83
-
84
- previousCb = currentCallback
85
- }
86
-
87
- setUncaughtExceptionCaptureCallbackStart.subscribe(cb)
88
-
89
- process.setUncaughtExceptionCaptureCallback(null)
90
-
91
- // For some reason, previous callback was defined before the instrumentation
92
- // We can not restore it, so we let the app decide
93
- if (previousCb) {
94
- process.setUncaughtExceptionCaptureCallback(() => {
95
- process.setUncaughtExceptionCaptureCallback(null)
96
- process.setUncaughtExceptionCaptureCallback(previousCb)
97
- })
98
- }
99
- }
100
- }
101
-
102
- function enable (_config) {
103
- config = _config
104
- httpClientRequestStart.subscribe(analyzeSsrf)
105
-
106
- process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
107
- abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
108
-
109
- if (abortOnUncaughtException) {
110
- log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
111
- }
112
- }
113
-
114
- function disable () {
115
- if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
116
-
117
- process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
118
- }
119
-
120
- function analyzeSsrf (ctx) {
121
- const store = storage.getStore()
122
- const req = store?.req
123
- const url = ctx.args.uri
124
-
125
- if (!req || !url) return
126
-
127
- const persistent = {
128
- [addresses.HTTP_OUTGOING_URL]: url
129
- }
130
-
131
- const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
132
-
133
- const res = store?.res
134
- handleResult(result, req, res, ctx.abortController)
135
- }
136
-
137
- function getGenerateStackTraceAction (actions) {
138
- return actions?.generate_stack
139
- }
140
-
141
- function handleResult (actions, req, res, abortController) {
142
- const generateStackTraceAction = getGenerateStackTraceAction(actions)
143
- if (generateStackTraceAction && config.appsec.stackTrace.enabled) {
144
- const rootSpan = web.root(req)
145
- reportStackTrace(
146
- rootSpan,
147
- generateStackTraceAction.stack_id,
148
- config.appsec.stackTrace.maxDepth,
149
- config.appsec.stackTrace.maxStackTraces
150
- )
151
- }
152
-
153
- if (!abortController || abortOnUncaughtException) return
154
-
155
- const blockingAction = getBlockingAction(actions)
156
- if (blockingAction) {
157
- const rootSpan = web.root(req)
158
- // Should block only in express
159
- if (rootSpan?.context()._name === 'express.request') {
160
- const abortError = new DatadogRaspAbortError(req, res, blockingAction)
161
- abortController.abort(abortError)
162
-
163
- // TODO Delete this when support for node 16 is removed
164
- if (!abortController.signal.reason) {
165
- abortController.signal.reason = abortError
166
- }
167
- }
168
- }
169
- }
170
-
171
- module.exports = {
172
- enable,
173
- disable,
174
- handleResult,
175
- handleUncaughtExceptionMonitor // exported only for testing purpose
176
- }