dd-trace 5.51.0 → 5.53.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 -6
- package/README.md +5 -0
- package/index.d.ts +88 -6
- package/package.json +3 -9
- package/packages/datadog-instrumentations/src/amqplib.js +8 -5
- package/packages/datadog-instrumentations/src/child_process.js +2 -1
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +406 -0
- package/packages/datadog-instrumentations/src/couchbase.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +43 -45
- package/packages/datadog-instrumentations/src/dns.js +16 -14
- package/packages/datadog-instrumentations/src/express.js +2 -6
- package/packages/datadog-instrumentations/src/fs.js +43 -51
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
- package/packages/datadog-instrumentations/src/http/client.js +2 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
- package/packages/datadog-instrumentations/src/jest.js +53 -40
- package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
- package/packages/datadog-instrumentations/src/mocha/utils.js +76 -74
- package/packages/datadog-instrumentations/src/mysql2.js +3 -1
- package/packages/datadog-instrumentations/src/net.js +27 -29
- package/packages/datadog-instrumentations/src/next.js +6 -14
- package/packages/datadog-instrumentations/src/pg.js +15 -7
- package/packages/datadog-instrumentations/src/playwright.js +64 -67
- package/packages/datadog-instrumentations/src/url.js +9 -17
- package/packages/datadog-instrumentations/src/vitest.js +66 -72
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +11 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +11 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +19 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +11 -0
- package/packages/datadog-plugin-cucumber/src/index.js +32 -18
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -0
- package/packages/datadog-plugin-dns/src/lookup.js +10 -5
- package/packages/datadog-plugin-dns/src/lookup_service.js +6 -2
- package/packages/datadog-plugin-dns/src/resolve.js +5 -2
- package/packages/datadog-plugin-dns/src/reverse.js +6 -2
- package/packages/datadog-plugin-fs/src/index.js +9 -2
- package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
- package/packages/datadog-plugin-jest/src/index.js +17 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +12 -21
- package/packages/datadog-plugin-kafkajs/src/producer.js +12 -5
- package/packages/datadog-plugin-kafkajs/src/utils.js +27 -0
- package/packages/datadog-plugin-langchain/src/index.js +0 -1
- package/packages/datadog-plugin-mocha/src/index.js +58 -35
- package/packages/datadog-plugin-net/src/ipc.js +6 -4
- package/packages/datadog-plugin-net/src/tcp.js +15 -9
- package/packages/datadog-plugin-pg/src/index.js +5 -1
- package/packages/datadog-plugin-playwright/src/index.js +29 -20
- package/packages/datadog-plugin-redis/src/index.js +8 -3
- package/packages/datadog-plugin-vitest/src/index.js +67 -44
- package/packages/datadog-shimmer/src/shimmer.js +164 -33
- package/packages/dd-trace/src/appsec/api_security_sampler.js +20 -12
- package/packages/dd-trace/src/appsec/graphql.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +14 -9
- package/packages/dd-trace/src/appsec/index.js +15 -12
- package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
- package/packages/dd-trace/src/baggage.js +36 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
- package/packages/dd-trace/src/config.js +14 -2
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +61 -7
- package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
- package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
- package/packages/dd-trace/src/debugger/devtools_client/state.js +22 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -0
- package/packages/dd-trace/src/exporters/common/docker.js +13 -31
- package/packages/dd-trace/src/guardrails/telemetry.js +2 -5
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +33 -12
- package/packages/dd-trace/src/noop/proxy.js +5 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -9
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +9 -20
- package/packages/dd-trace/src/plugins/outbound.js +11 -3
- package/packages/dd-trace/src/plugins/tracing.js +8 -4
- package/packages/dd-trace/src/plugins/util/test.js +1 -1
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +15 -14
- package/packages/dd-trace/src/proxy.js +12 -4
- package/packages/dd-trace/src/serverless.js +0 -48
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/standalone/product.js +3 -5
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @type {Set<string | symbol>}
|
|
5
|
+
*/
|
|
3
6
|
const skipMethods = new Set([
|
|
4
7
|
'caller',
|
|
5
8
|
'arguments',
|
|
6
9
|
'name',
|
|
7
10
|
'length'
|
|
8
11
|
])
|
|
12
|
+
const skipMethodSize = skipMethods.size
|
|
9
13
|
|
|
14
|
+
const nonConfigurableModuleExports = new WeakMap()
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Copies properties from the original function to the wrapped function.
|
|
18
|
+
*
|
|
19
|
+
* @param {Function} original - The original function.
|
|
20
|
+
* @param {Function} wrapped - The wrapped function.
|
|
21
|
+
*/
|
|
10
22
|
function copyProperties (original, wrapped) {
|
|
11
23
|
if (original.constructor !== wrapped.constructor) {
|
|
12
24
|
const proto = Object.getPrototypeOf(original)
|
|
@@ -23,7 +35,7 @@ function copyProperties (original, wrapped) {
|
|
|
23
35
|
if (ownKeys.length !== 2) {
|
|
24
36
|
for (const key of ownKeys) {
|
|
25
37
|
if (skipMethods.has(key)) continue
|
|
26
|
-
const descriptor = Object.getOwnPropertyDescriptor(original, key)
|
|
38
|
+
const descriptor = /** @type {PropertyDescriptor} */ (Object.getOwnPropertyDescriptor(original, key))
|
|
27
39
|
if (descriptor.writable && descriptor.enumerable && descriptor.configurable) {
|
|
28
40
|
wrapped[key] = original[key]
|
|
29
41
|
} else if (descriptor.writable || descriptor.configurable || !Object.hasOwn(wrapped, key)) {
|
|
@@ -33,6 +45,33 @@ function copyProperties (original, wrapped) {
|
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Copies properties from the original object to the wrapped object, skipping a specific key.
|
|
50
|
+
*
|
|
51
|
+
* @param {Record<string | symbol, unknown>} original - The original object.
|
|
52
|
+
* @param {Record<string | symbol, unknown>} wrapped - The wrapped object.
|
|
53
|
+
* @param {string | symbol} skipKey - The key to skip during copying.
|
|
54
|
+
*/
|
|
55
|
+
function copyObjectProperties (original, wrapped, skipKey) {
|
|
56
|
+
const ownKeys = Reflect.ownKeys(original)
|
|
57
|
+
for (const key of ownKeys) {
|
|
58
|
+
if (key === skipKey) continue
|
|
59
|
+
const descriptor = /** @type {PropertyDescriptor} */ (Object.getOwnPropertyDescriptor(original, key))
|
|
60
|
+
if (descriptor.writable && descriptor.enumerable && descriptor.configurable) {
|
|
61
|
+
wrapped[key] = original[key]
|
|
62
|
+
} else if (descriptor.writable || descriptor.configurable || !Object.hasOwn(wrapped, key)) {
|
|
63
|
+
Object.defineProperty(wrapped, key, descriptor)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Wraps a function with a wrapper function.
|
|
70
|
+
*
|
|
71
|
+
* @param {Function} original - The original function to wrap.
|
|
72
|
+
* @param {(original: Function) => Function} wrapper - The wrapper function.
|
|
73
|
+
* @returns {Function} The wrapped function.
|
|
74
|
+
*/
|
|
36
75
|
function wrapFunction (original, wrapper) {
|
|
37
76
|
const wrapped = wrapper(original)
|
|
38
77
|
|
|
@@ -44,28 +83,55 @@ function wrapFunction (original, wrapper) {
|
|
|
44
83
|
return wrapped
|
|
45
84
|
}
|
|
46
85
|
|
|
47
|
-
|
|
48
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Wraps a method of an object with a wrapper function.
|
|
88
|
+
*
|
|
89
|
+
* @param {Record<string | symbol, unknown> | Function} target - The target
|
|
90
|
+
* object.
|
|
91
|
+
* @param {string | symbol} name - The property key of the method to wrap.
|
|
92
|
+
* @param {(original: Function) => (...args) => any} wrapper - The wrapper function.
|
|
93
|
+
* @param {{ replaceGetter?: boolean }} [options] - If `replaceGetter` is set to
|
|
94
|
+
* true, the getter is accessed and the getter is replaced with one that just
|
|
95
|
+
* returns the earlier retrieved value. Use with care! This may only be done in
|
|
96
|
+
* case the getter absolutely has no side effect and no setter is defined for the
|
|
97
|
+
* property.
|
|
98
|
+
* @returns {Record<string | symbol, unknown> | Function} The target object with
|
|
99
|
+
* the wrapped method.
|
|
100
|
+
*/
|
|
101
|
+
function wrap (target, name, wrapper, options) {
|
|
49
102
|
if (typeof wrapper !== 'function') {
|
|
50
103
|
throw new Error(wrapper ? 'Target is not a function' : 'No function provided')
|
|
51
104
|
}
|
|
52
105
|
|
|
53
|
-
|
|
54
|
-
|
|
106
|
+
// No descriptor means original was on the prototype. This is not totally
|
|
107
|
+
// safe, since we define the property on the target. That could have an impact
|
|
108
|
+
// in case e.g., the own keys are checks.
|
|
109
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, name) ?? {
|
|
110
|
+
value: target[name],
|
|
111
|
+
writable: true,
|
|
112
|
+
configurable: true,
|
|
113
|
+
enumerable: false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (descriptor.set && (!descriptor.get || options?.replaceGetter)) {
|
|
117
|
+
// It is possible to support these cases by instrumenting both the getter
|
|
118
|
+
// and setter (or only the setter, in case that is a use case).
|
|
119
|
+
// For now, this is not supported due to the complexity and the fact that
|
|
120
|
+
// this is not a common use case.
|
|
121
|
+
throw new Error(options?.replaceGetter
|
|
122
|
+
? 'Replacing a getter/setter pair is not supported. Implement if required.'
|
|
123
|
+
: 'Replacing setters is not supported. Implement if required.')
|
|
124
|
+
}
|
|
55
125
|
|
|
56
|
-
|
|
126
|
+
const original = descriptor.value ?? options?.replaceGetter ? target[name] : descriptor.get
|
|
57
127
|
|
|
58
|
-
|
|
128
|
+
assertMethod(target, name, original)
|
|
59
129
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
configurable: true,
|
|
66
|
-
enumerable: false
|
|
67
|
-
}
|
|
68
|
-
} else if (descriptor.writable) {
|
|
130
|
+
const wrapped = wrapper(original)
|
|
131
|
+
|
|
132
|
+
copyProperties(original, wrapped)
|
|
133
|
+
|
|
134
|
+
if (descriptor.writable) {
|
|
69
135
|
// Fast path for assigned properties.
|
|
70
136
|
if (descriptor.configurable && descriptor.enumerable) {
|
|
71
137
|
target[name] = wrapped
|
|
@@ -73,25 +139,58 @@ function wrap (target, name, wrapper) {
|
|
|
73
139
|
}
|
|
74
140
|
descriptor.value = wrapped
|
|
75
141
|
} else {
|
|
76
|
-
if (descriptor.get
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
142
|
+
if (descriptor.get) {
|
|
143
|
+
// `replaceGetter` may only be used when the getter has no side effect.
|
|
144
|
+
if (options?.replaceGetter) {
|
|
145
|
+
descriptor.get = () => wrapped
|
|
146
|
+
} else {
|
|
147
|
+
descriptor.get = wrapped
|
|
148
|
+
}
|
|
81
149
|
} else {
|
|
82
150
|
descriptor.value = wrapped
|
|
83
151
|
}
|
|
84
152
|
|
|
85
153
|
if (descriptor.configurable === false) {
|
|
86
|
-
// TODO(BridgeAR):
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
154
|
+
// TODO(BridgeAR): This currently only works on the most outer part. The
|
|
155
|
+
// moduleExports object.
|
|
156
|
+
//
|
|
157
|
+
// It would be possible to also implement it for non moduleExports objects
|
|
158
|
+
// by passing through the moduleExports object and the property names that
|
|
159
|
+
// are accessed. That way it would be possible to redefine the complete
|
|
160
|
+
// property chain. Example:
|
|
161
|
+
//
|
|
162
|
+
// shimmer.wrap(hapi.Server.prototype, 'start', wrapStart)
|
|
163
|
+
// shimmer.wrap(hapi.Server.prototype, 'ext', wrapExt)
|
|
164
|
+
//
|
|
165
|
+
// shimmer.wrap(hapi, 'Server', 'prototype', 'start', wrapStart)
|
|
166
|
+
// shimmer.wrap(hapi, 'Server', 'prototype', 'ext', wrapExt)
|
|
167
|
+
//
|
|
168
|
+
// That would however still not resolve the issue about the user replacing
|
|
169
|
+
// the return value so that the hook picks up the new hapi moduleExports
|
|
170
|
+
// object. To safely fix that, we would have to couple the register helper
|
|
171
|
+
// with this code. That way it would be possible to directly pass through
|
|
172
|
+
// the entries.
|
|
173
|
+
|
|
174
|
+
// In case more than a single property is not configurable and writable,
|
|
175
|
+
// Just reuse the already created object.
|
|
176
|
+
let moduleExports = nonConfigurableModuleExports.get(target)
|
|
177
|
+
if (!moduleExports) {
|
|
178
|
+
if (typeof target === 'function') {
|
|
179
|
+
const original = target
|
|
180
|
+
moduleExports = function (...args) { return original.apply(original, args) }
|
|
181
|
+
// This is a rare case. Accept the slight performance hit.
|
|
182
|
+
skipMethods.add(name)
|
|
183
|
+
copyProperties(target, moduleExports)
|
|
184
|
+
if (skipMethods.size === skipMethodSize + 1) {
|
|
185
|
+
skipMethods.delete(name)
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
moduleExports = Object.create(target)
|
|
189
|
+
copyObjectProperties(target, moduleExports, name)
|
|
190
|
+
}
|
|
191
|
+
nonConfigurableModuleExports.set(target, moduleExports)
|
|
192
|
+
}
|
|
193
|
+
target = moduleExports
|
|
95
194
|
}
|
|
96
195
|
}
|
|
97
196
|
|
|
@@ -100,6 +199,16 @@ function wrap (target, name, wrapper) {
|
|
|
100
199
|
return target
|
|
101
200
|
}
|
|
102
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Wraps multiple methods and or multiple objects with a wrapper function.
|
|
204
|
+
* May also receive a single method or object or a single method name.
|
|
205
|
+
*
|
|
206
|
+
* @param {Array<Record<string | symbol, unknown> | Function> |
|
|
207
|
+
* Record<string | symbol, unknown> |
|
|
208
|
+
* Function} targets - The target objects.
|
|
209
|
+
* @param {Array<string | symbol> | string | symbol} names - The property keys of the methods to wrap.
|
|
210
|
+
* @param {(original: Function) => (...args) => any} wrapper - The wrapper function.
|
|
211
|
+
*/
|
|
103
212
|
function massWrap (targets, names, wrapper) {
|
|
104
213
|
targets = toArray(targets)
|
|
105
214
|
names = toArray(names)
|
|
@@ -111,19 +220,35 @@ function massWrap (targets, names, wrapper) {
|
|
|
111
220
|
}
|
|
112
221
|
}
|
|
113
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Converts a value to an array if it is not already an array.
|
|
225
|
+
*
|
|
226
|
+
* @template T
|
|
227
|
+
* @param {T | T[]} maybeArray - The value to convert.
|
|
228
|
+
* @returns {T[]} The value as an array.
|
|
229
|
+
*/
|
|
114
230
|
function toArray (maybeArray) {
|
|
115
231
|
return Array.isArray(maybeArray) ? maybeArray : [maybeArray]
|
|
116
232
|
}
|
|
117
233
|
|
|
118
|
-
|
|
119
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Asserts that a method is a function.
|
|
236
|
+
*
|
|
237
|
+
* @param {Record<string | symbol, unknown> | Function} target - The target object.
|
|
238
|
+
* @param {string | symbol} name - The property key of the method.
|
|
239
|
+
* @param {unknown} method - The method to assert.
|
|
240
|
+
* @throws {Error} If the method is not a function.
|
|
241
|
+
*/
|
|
242
|
+
function assertMethod (target, name, method) {
|
|
243
|
+
if (typeof method !== 'function') {
|
|
120
244
|
let message = 'No target object provided'
|
|
121
245
|
|
|
122
246
|
if (target) {
|
|
123
247
|
if (typeof target !== 'object' && typeof target !== 'function') {
|
|
124
248
|
message = 'Invalid target'
|
|
125
249
|
} else {
|
|
126
|
-
|
|
250
|
+
name = String(name)
|
|
251
|
+
message = method ? `Original method ${name} is not a function` : `No original method ${name}`
|
|
127
252
|
}
|
|
128
253
|
}
|
|
129
254
|
|
|
@@ -131,6 +256,12 @@ function assertMethod (target, name) {
|
|
|
131
256
|
}
|
|
132
257
|
}
|
|
133
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Asserts that a target is not a class constructor.
|
|
261
|
+
*
|
|
262
|
+
* @param {Function} target - The target function.
|
|
263
|
+
* @throws {Error} If the target is a class constructor.
|
|
264
|
+
*/
|
|
134
265
|
function assertNotClass (target) {
|
|
135
266
|
if (Function.prototype.toString.call(target).startsWith('class')) {
|
|
136
267
|
throw new Error('Target is a native class constructor and cannot be wrapped.')
|
|
@@ -4,10 +4,13 @@ const TTLCache = require('@isaacs/ttlcache')
|
|
|
4
4
|
const web = require('../plugins/util/web')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const { AUTO_REJECT, USER_REJECT } = require('../../../../ext/priority')
|
|
7
|
+
const { keepTrace } = require('../priority_sampler')
|
|
8
|
+
const { ASM } = require('../standalone/product')
|
|
7
9
|
|
|
8
10
|
const MAX_SIZE = 4096
|
|
9
11
|
|
|
10
12
|
let enabled
|
|
13
|
+
let asmStandaloneEnabled
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* @type {TTLCache}
|
|
@@ -20,11 +23,12 @@ class NoopTTLCache {
|
|
|
20
23
|
has (_key) { return false }
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
function configure ({
|
|
24
|
-
enabled = apiSecurity.enabled
|
|
25
|
-
|
|
26
|
+
function configure ({ appsec, apmTracingEnabled }) {
|
|
27
|
+
enabled = appsec.apiSecurity.enabled
|
|
28
|
+
asmStandaloneEnabled = apmTracingEnabled === false
|
|
29
|
+
sampledRequests = appsec.apiSecurity.sampleDelay === 0
|
|
26
30
|
? new NoopTTLCache()
|
|
27
|
-
: new TTLCache({ max: MAX_SIZE, ttl: apiSecurity.sampleDelay * 1000 })
|
|
31
|
+
: new TTLCache({ max: MAX_SIZE, ttl: appsec.apiSecurity.sampleDelay * 1000 })
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
function disable () {
|
|
@@ -41,14 +45,18 @@ function sampleRequest (req, res, force = false) {
|
|
|
41
45
|
const rootSpan = web.root(req)
|
|
42
46
|
if (!rootSpan) return false
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
priority = getSpanPriority(rootSpan)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if (asmStandaloneEnabled) {
|
|
49
|
+
keepTrace(rootSpan, ASM)
|
|
50
|
+
} else {
|
|
51
|
+
let priority = getSpanPriority(rootSpan)
|
|
52
|
+
if (!priority) {
|
|
53
|
+
rootSpan._prioritySampler?.sample(rootSpan)
|
|
54
|
+
priority = getSpanPriority(rootSpan)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (priority === AUTO_REJECT || priority === USER_REJECT) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
if (force) {
|
|
@@ -38,8 +38,8 @@ function onGraphqlStartResolve ({ context, resolverInfo }) {
|
|
|
38
38
|
|
|
39
39
|
if (!resolverInfo || typeof resolverInfo !== 'object') return
|
|
40
40
|
|
|
41
|
-
const
|
|
42
|
-
const blockingAction = getBlockingAction(actions)
|
|
41
|
+
const result = waf.run({ ephemeral: { [addresses.HTTP_INCOMING_GRAPHQL_RESOLVER]: resolverInfo } }, req)
|
|
42
|
+
const blockingAction = getBlockingAction(result?.actions)
|
|
43
43
|
if (blockingAction) {
|
|
44
44
|
const requestData = graphqlRequestData.get(req)
|
|
45
45
|
if (requestData?.isInGraphqlRequest) {
|
|
@@ -19,9 +19,11 @@ let config
|
|
|
19
19
|
const hardcodedSecretCh = dc.channel('datadog:secrets:result')
|
|
20
20
|
let rewriter
|
|
21
21
|
let unwrapCompile = () => {}
|
|
22
|
-
let getPrepareStackTrace
|
|
22
|
+
let getPrepareStackTrace
|
|
23
|
+
let cacheRewrittenSourceMap
|
|
23
24
|
let kSymbolPrepareStackTrace
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
function noop () {}
|
|
25
27
|
|
|
26
28
|
function isFlagPresent (flag) {
|
|
27
29
|
return process.env.NODE_OPTIONS?.includes(flag) ||
|
|
@@ -155,7 +157,7 @@ function shimPrepareStackTrace () {
|
|
|
155
157
|
return
|
|
156
158
|
}
|
|
157
159
|
const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
|
|
158
|
-
if (
|
|
160
|
+
if (pstDescriptor?.configurable || pstDescriptor?.writable) {
|
|
159
161
|
Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
|
|
160
162
|
}
|
|
161
163
|
shimmedPrepareStackTrace = true
|
|
@@ -181,16 +183,17 @@ function isEsmConfigured () {
|
|
|
181
183
|
const hasLoaderArg = isFlagPresent('--loader') || isFlagPresent('--experimental-loader')
|
|
182
184
|
if (hasLoaderArg) return true
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
// Fast path for common case when enabled
|
|
187
|
+
if (require.cache[`${process.cwd()}/node_modules/import-in-the-middle/hook.js`]) {
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
return Object.keys(require.cache).some(file => file.endsWith('import-in-the-middle/hook.js'))
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
|
|
189
|
-
if (isMainThread && Module.register &&
|
|
193
|
+
let enableEsmRewriter = function (telemetryVerbosity) {
|
|
194
|
+
if (isMainThread && Module.register && isEsmConfigured()) {
|
|
190
195
|
shimPrepareStackTrace()
|
|
191
196
|
|
|
192
|
-
esmRewriterEnabled = true
|
|
193
|
-
|
|
194
197
|
const { port1, port2 } = new MessageChannel()
|
|
195
198
|
|
|
196
199
|
port1.on('message', (message) => {
|
|
@@ -229,6 +232,8 @@ function enableEsmRewriter (telemetryVerbosity) {
|
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
cacheRewrittenSourceMap = require('@datadog/wasm-js-rewriter/js/source-map').cacheRewrittenSourceMap
|
|
235
|
+
|
|
236
|
+
enableEsmRewriter = noop
|
|
232
237
|
}
|
|
233
238
|
}
|
|
234
239
|
|
|
@@ -34,6 +34,7 @@ const UserTracking = require('./user_tracking')
|
|
|
34
34
|
const { storage } = require('../../../datadog-core')
|
|
35
35
|
const graphql = require('./graphql')
|
|
36
36
|
const rasp = require('./rasp')
|
|
37
|
+
const { isInServerlessEnvironment } = require('../serverless')
|
|
37
38
|
|
|
38
39
|
const responseAnalyzedSet = new WeakSet()
|
|
39
40
|
|
|
@@ -59,7 +60,7 @@ function enable (_config) {
|
|
|
59
60
|
|
|
60
61
|
Reporter.setRateLimit(_config.appsec.rateLimit)
|
|
61
62
|
|
|
62
|
-
apiSecuritySampler.configure(_config
|
|
63
|
+
apiSecuritySampler.configure(_config)
|
|
63
64
|
|
|
64
65
|
UserTracking.setCollectionMode(_config.appsec.eventTracking.mode, false)
|
|
65
66
|
|
|
@@ -83,7 +84,9 @@ function enable (_config) {
|
|
|
83
84
|
isEnabled = true
|
|
84
85
|
config = _config
|
|
85
86
|
} catch (err) {
|
|
86
|
-
|
|
87
|
+
if (!isInServerlessEnvironment()) {
|
|
88
|
+
log.error('[ASM] Unable to start AppSec', err)
|
|
89
|
+
}
|
|
87
90
|
|
|
88
91
|
disable()
|
|
89
92
|
}
|
|
@@ -106,7 +109,7 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
|
106
109
|
}
|
|
107
110
|
}, req)
|
|
108
111
|
|
|
109
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
112
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
function onRequestCookieParser ({ req, res, abortController, cookies }) {
|
|
@@ -121,7 +124,7 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) {
|
|
|
121
124
|
}
|
|
122
125
|
}, req)
|
|
123
126
|
|
|
124
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
127
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
@@ -149,9 +152,9 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
|
|
|
149
152
|
persistent[addresses.HTTP_CLIENT_IP] = clientIp
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
const
|
|
155
|
+
const results = waf.run({ persistent }, req)
|
|
153
156
|
|
|
154
|
-
handleResults(actions, req, res, rootSpan, abortController)
|
|
157
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
function incomingHttpEndTranslator ({ req, res }) {
|
|
@@ -198,7 +201,7 @@ function onPassportVerify ({ framework, login, user, success, abortController })
|
|
|
198
201
|
|
|
199
202
|
const results = UserTracking.trackLogin(framework, login, user, success, rootSpan)
|
|
200
203
|
|
|
201
|
-
handleResults(results, store.req, store.req.res, rootSpan, abortController)
|
|
204
|
+
handleResults(results?.actions, store.req, store.req.res, rootSpan, abortController)
|
|
202
205
|
}
|
|
203
206
|
|
|
204
207
|
function onPassportDeserializeUser ({ user, abortController }) {
|
|
@@ -212,7 +215,7 @@ function onPassportDeserializeUser ({ user, abortController }) {
|
|
|
212
215
|
|
|
213
216
|
const results = UserTracking.trackUser(user, rootSpan)
|
|
214
217
|
|
|
215
|
-
handleResults(results, store.req, store.req.res, rootSpan, abortController)
|
|
218
|
+
handleResults(results?.actions, store.req, store.req.res, rootSpan, abortController)
|
|
216
219
|
}
|
|
217
220
|
|
|
218
221
|
function onExpressSession ({ req, res, sessionId, abortController }) {
|
|
@@ -231,7 +234,7 @@ function onExpressSession ({ req, res, sessionId, abortController }) {
|
|
|
231
234
|
}
|
|
232
235
|
}, req)
|
|
233
236
|
|
|
234
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
237
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
@@ -251,7 +254,7 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
|
251
254
|
}
|
|
252
255
|
}, req)
|
|
253
256
|
|
|
254
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
257
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
function onRequestProcessParams ({ req, res, abortController, params }) {
|
|
@@ -266,7 +269,7 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
|
|
|
266
269
|
}
|
|
267
270
|
}, req)
|
|
268
271
|
|
|
269
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
272
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
270
273
|
}
|
|
271
274
|
|
|
272
275
|
function onResponseBody ({ req, res, body }) {
|
|
@@ -308,7 +311,7 @@ function onResponseWriteHead ({ req, res, abortController, statusCode, responseH
|
|
|
308
311
|
|
|
309
312
|
responseAnalyzedSet.add(res)
|
|
310
313
|
|
|
311
|
-
handleResults(results, req, res, rootSpan, abortController)
|
|
314
|
+
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
312
315
|
}
|
|
313
316
|
|
|
314
317
|
function onResponseSetHeader ({ res, abortController }) {
|
|
@@ -85,10 +85,12 @@ function blockOnDatadogRaspAbortError ({ error }) {
|
|
|
85
85
|
const abortError = findDatadogRaspAbortError(error)
|
|
86
86
|
if (!abortError) return false
|
|
87
87
|
|
|
88
|
-
const { req, res, blockingAction, raspRule } = abortError
|
|
88
|
+
const { req, res, blockingAction, raspRule, ruleTriggered } = abortError
|
|
89
89
|
if (!isBlocked(res)) {
|
|
90
90
|
const blocked = block(req, res, web.root(req), null, blockingAction)
|
|
91
|
-
|
|
91
|
+
if (ruleTriggered) {
|
|
92
|
+
updateRaspRuleMatchMetricTags(req, raspRule, true, blocked)
|
|
93
|
+
}
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
return true
|
|
@@ -20,23 +20,26 @@ const RULE_TYPES = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
class DatadogRaspAbortError extends Error {
|
|
23
|
-
constructor (req, res, blockingAction, raspRule) {
|
|
23
|
+
constructor (req, res, blockingAction, raspRule, ruleTriggered) {
|
|
24
24
|
super('DatadogRaspAbortError')
|
|
25
25
|
this.name = 'DatadogRaspAbortError'
|
|
26
26
|
this.req = req
|
|
27
27
|
this.res = res
|
|
28
28
|
this.blockingAction = blockingAction
|
|
29
29
|
this.raspRule = raspRule
|
|
30
|
+
this.ruleTriggered = ruleTriggered
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
function handleResult (
|
|
34
|
-
const generateStackTraceAction = actions?.generate_stack
|
|
34
|
+
function handleResult (result, req, res, abortController, config, raspRule) {
|
|
35
|
+
const generateStackTraceAction = result?.actions?.generate_stack
|
|
35
36
|
|
|
36
37
|
const { enabled, maxDepth, maxStackTraces } = config.appsec.stackTrace
|
|
37
38
|
|
|
38
39
|
const rootSpan = web.root(req)
|
|
39
40
|
|
|
41
|
+
const ruleTriggered = !!result?.events?.length
|
|
42
|
+
|
|
40
43
|
if (generateStackTraceAction && enabled && canReportStackTrace(rootSpan, maxStackTraces)) {
|
|
41
44
|
const frames = getCallsiteFrames(maxDepth)
|
|
42
45
|
|
|
@@ -48,11 +51,11 @@ function handleResult (actions, req, res, abortController, config, raspRule) {
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
if (abortController && !abortOnUncaughtException) {
|
|
51
|
-
const blockingAction = getBlockingAction(actions)
|
|
54
|
+
const blockingAction = getBlockingAction(result?.actions)
|
|
52
55
|
|
|
53
56
|
// Should block only in express
|
|
54
57
|
if (blockingAction && rootSpan?.context()._name === 'express.request') {
|
|
55
|
-
const abortError = new DatadogRaspAbortError(req, res, blockingAction, raspRule)
|
|
58
|
+
const abortError = new DatadogRaspAbortError(req, res, blockingAction, raspRule, ruleTriggered)
|
|
56
59
|
abortController.abort(abortError)
|
|
57
60
|
|
|
58
61
|
// TODO Delete this when support for node 16 is removed
|
|
@@ -64,7 +67,9 @@ function handleResult (actions, req, res, abortController, config, raspRule) {
|
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
if (ruleTriggered) {
|
|
71
|
+
updateRaspRuleMatchMetricTags(req, raspRule, false, false)
|
|
72
|
+
}
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
module.exports = {
|
|
@@ -9,8 +9,8 @@ const { setUserTags } = require('./set_user')
|
|
|
9
9
|
const log = require('../../log')
|
|
10
10
|
|
|
11
11
|
function isUserBlocked (user) {
|
|
12
|
-
const
|
|
13
|
-
return !!getBlockingAction(actions)
|
|
12
|
+
const results = waf.run({ persistent: { [USER_ID]: user.id } })
|
|
13
|
+
return !!getBlockingAction(results?.actions)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function checkUserAndSetUser (tracer, user) {
|
|
@@ -49,10 +49,6 @@ function trackRaspMetrics (store, metrics, raspRule) {
|
|
|
49
49
|
telemetryMetrics.rulesVersion = metrics.rulesVersion
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
if (metrics.ruleTriggered) {
|
|
53
|
-
telemetryMetrics.ruleTriggered = true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
52
|
appsecMetrics.count('rasp.rule.eval', tags).inc(1)
|
|
57
53
|
|
|
58
54
|
if (metrics.errorCode) {
|
|
@@ -68,7 +64,6 @@ function trackRaspMetrics (store, metrics, raspRule) {
|
|
|
68
64
|
|
|
69
65
|
function trackRaspRuleMatch (store, raspRule, blockTriggered, blocked) {
|
|
70
66
|
const telemetryMetrics = store[DD_TELEMETRY_REQUEST_METRICS]
|
|
71
|
-
if (!telemetryMetrics.ruleTriggered) return
|
|
72
67
|
|
|
73
68
|
const tags = {
|
|
74
69
|
waf_version: telemetryMetrics.wafVersion,
|
|
@@ -82,10 +77,6 @@ function trackRaspRuleMatch (store, raspRule, blockTriggered, blocked) {
|
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
appsecMetrics.count('rasp.rule.match', tags).inc(1)
|
|
85
|
-
|
|
86
|
-
// this is needed to not count it twice for the same match
|
|
87
|
-
// but it also means it can only be called once per waf call even if there are multiple rasp match
|
|
88
|
-
telemetryMetrics.ruleTriggered = null
|
|
89
80
|
}
|
|
90
81
|
|
|
91
82
|
function trackRaspRuleSkipped (raspRule, reason) {
|
|
@@ -19,7 +19,7 @@ class WAFContextWrapper {
|
|
|
19
19
|
this.rulesVersion = rulesVersion
|
|
20
20
|
this.knownAddresses = knownAddresses
|
|
21
21
|
this.addressesToSkip = new Set()
|
|
22
|
-
this.
|
|
22
|
+
this.cachedUserIdResults = new Map()
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
run ({ persistent, ephemeral }, raspRule) {
|
|
@@ -36,9 +36,9 @@ class WAFContextWrapper {
|
|
|
36
36
|
// TODO: make this universal
|
|
37
37
|
const userId = persistent?.[addresses.USER_ID] || ephemeral?.[addresses.USER_ID]
|
|
38
38
|
if (userId) {
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
return
|
|
39
|
+
const cachedResults = this.cachedUserIdResults.get(userId)
|
|
40
|
+
if (cachedResults) {
|
|
41
|
+
return cachedResults
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -142,7 +142,7 @@ class WAFContextWrapper {
|
|
|
142
142
|
|
|
143
143
|
Reporter.reportDerivatives(result.derivatives)
|
|
144
144
|
|
|
145
|
-
return result
|
|
145
|
+
return result
|
|
146
146
|
} catch (err) {
|
|
147
147
|
log.error('[ASM] Error while running the AppSec WAF', err)
|
|
148
148
|
|
|
@@ -168,7 +168,7 @@ class WAFContextWrapper {
|
|
|
168
168
|
const parameter = match.parameters[k]
|
|
169
169
|
|
|
170
170
|
if (parameter?.address === addresses.USER_ID) {
|
|
171
|
-
this.
|
|
171
|
+
this.cachedUserIdResults.set(userId, result)
|
|
172
172
|
return
|
|
173
173
|
}
|
|
174
174
|
}
|