dd-trace 6.14.0 → 6.16.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/README.md +15 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -2
- package/ci/test-optimization-validation/package-check.js +4 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -1
- package/index.d.ts +12 -0
- package/next.d.ts +27 -0
- package/next.js +3 -0
- package/package.json +17 -13
- package/packages/datadog-esbuild/index.js +14 -1
- package/packages/datadog-esbuild/src/resolver.js +284 -0
- package/packages/datadog-esbuild/src/utils.js +163 -119
- package/packages/datadog-instrumentations/src/ai.js +39 -97
- package/packages/datadog-instrumentations/src/anthropic.js +76 -134
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -1
- package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/bullmq.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
- package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +72 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -64
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +46 -14
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +1 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -70
- package/packages/datadog-instrumentations/src/jest.js +141 -25
- package/packages/datadog-instrumentations/src/langchain.js +1 -1
- package/packages/datadog-instrumentations/src/langgraph.js +1 -1
- package/packages/datadog-instrumentations/src/mercurius.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -5
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +54 -76
- package/packages/datadog-instrumentations/src/playwright.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +7 -10
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +96 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
- package/packages/datadog-plugin-graphql/src/resolve-error.js +33 -11
- package/packages/datadog-turbopack/index.js +212 -0
- package/packages/datadog-turbopack/src/constants.js +5 -0
- package/packages/datadog-turbopack/src/loader.js +500 -0
- package/packages/dd-trace/src/aiguard/index.js +2 -1
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
- package/packages/dd-trace/src/aiguard/integrations/index.js +3 -2
- package/packages/dd-trace/src/aiguard/integrations/openai.js +59 -30
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
- package/packages/dd-trace/src/aiguard/messages/utils.js +29 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -70
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
- package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +4 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +10 -2
- package/packages/dd-trace/src/config/index.js +20 -5
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +1 -6
- package/packages/dd-trace/src/evp_proxy/direct.js +2 -28
- package/packages/dd-trace/src/evp_proxy/discovery.js +3 -4
- package/packages/dd-trace/src/evp_proxy/path.js +12 -3
- package/packages/dd-trace/src/exporters/agentless/writer.js +3 -1
- package/packages/dd-trace/src/exporters/common/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/proxy.js +55 -0
- package/packages/dd-trace/src/exporters/common/request.js +11 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +106 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +8 -2
- package/packages/dd-trace/src/plugin_manager.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -1
- package/packages/dd-trace/src/priority_sampler.js +4 -0
- package/packages/dd-trace/src/sampling_rule.js +3 -1
- package/packages/dd-trace/src/span_processor.js +18 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const Module = require('node:module')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
|
|
7
|
+
const extractPackageAndModulePath = require(
|
|
8
|
+
'../../datadog-instrumentations/src/helpers/extract-package-and-module-path'
|
|
9
|
+
)
|
|
10
|
+
const hooks = require('../../datadog-instrumentations/src/helpers/hooks')
|
|
11
|
+
const instrumentations = require('../../datadog-instrumentations/src/helpers/instrumentations')
|
|
12
|
+
const { filename, matchesInstrumentation } = require('../../datadog-instrumentations/src/helpers/instrumentation-utils')
|
|
13
|
+
const { createBundlerRewriter } = require('../../datadog-instrumentations/src/helpers/rewriter')
|
|
14
|
+
const { getRewriteTarget } = require('../../datadog-instrumentations/src/helpers/rewriter/targets')
|
|
15
|
+
const { isESMFile } = require('../../datadog-esbuild/src/utils')
|
|
16
|
+
const { SYNTHETIC_EXTENSION } = require('./constants')
|
|
17
|
+
|
|
18
|
+
const CHANNEL = 'dd-trace:bundler:load'
|
|
19
|
+
const ARGUMENTS_BINDING = 1
|
|
20
|
+
const MODULE_BINDING = 2
|
|
21
|
+
const REQUIRE_BINDING = 4
|
|
22
|
+
const COMMONJS_BINDINGS = MODULE_BINDING | REQUIRE_BINDING
|
|
23
|
+
const targetPackages = new Set(Object.keys(hooks))
|
|
24
|
+
const entrypoints = new Map()
|
|
25
|
+
const loadedHooks = new Set()
|
|
26
|
+
const packageCache = new Map()
|
|
27
|
+
const rewriters = new Map()
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} LoaderContext
|
|
31
|
+
* @property {(error: Error|undefined, code?: string, sourceMap?: string|object) => void} callback
|
|
32
|
+
* @property {(warning: Error) => void} [emitWarning]
|
|
33
|
+
* @property {string} resourcePath
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {object} PackageInfo
|
|
38
|
+
* @property {string} [entrypoint]
|
|
39
|
+
* @property {boolean} esm
|
|
40
|
+
* @property {string} moduleName
|
|
41
|
+
* @property {string} name
|
|
42
|
+
* @property {string} packageJsonPath
|
|
43
|
+
* @property {string} version
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Instruments a resolved foreign Node.js module.
|
|
48
|
+
*
|
|
49
|
+
* @this {LoaderContext}
|
|
50
|
+
* @param {string} source
|
|
51
|
+
* @param {string|object} [inputSourceMap]
|
|
52
|
+
*/
|
|
53
|
+
module.exports = function loader (source, inputSourceMap) {
|
|
54
|
+
try {
|
|
55
|
+
const nativeResourcePath = fs.realpathSync(getResourcePath(this))
|
|
56
|
+
const resourcePath = nativeResourcePath.replaceAll('\\', '/')
|
|
57
|
+
const extracted = extractPackageAndModulePath(resourcePath)
|
|
58
|
+
if (!extracted.pkg || !targetPackages.has(extracted.pkg)) {
|
|
59
|
+
this.callback(undefined, source, inputSourceMap)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rewriteTarget = getRewriteTarget(resourcePath)
|
|
64
|
+
const packageInfo = getPackageInfo(
|
|
65
|
+
nativeResourcePath,
|
|
66
|
+
extracted.pkg,
|
|
67
|
+
/** @type {string} */ (extracted.path),
|
|
68
|
+
path.normalize(/** @type {string} */ (extracted.pkgJson))
|
|
69
|
+
)
|
|
70
|
+
const esm = packageInfo.esm
|
|
71
|
+
const publications = esm ? [] : getPublications(resourcePath, packageInfo)
|
|
72
|
+
if (!rewriteTarget && publications.length === 0) {
|
|
73
|
+
this.callback(undefined, source, inputSourceMap)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (publications.length > 0 && hasUnsafeCommonJsBindings(source)) {
|
|
77
|
+
this.emitWarning?.(new Error(`Skipped CommonJS publication for unsafe wrapper bindings in ${resourcePath}`))
|
|
78
|
+
this.callback(undefined, source, inputSourceMap)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const dcModule = relativeImport(path.dirname(nativeResourcePath), require.resolve('dc-polyfill'))
|
|
83
|
+
let code = source
|
|
84
|
+
let sourceMap = inputSourceMap
|
|
85
|
+
let rewritten = false
|
|
86
|
+
if (rewriteTarget) {
|
|
87
|
+
const rewrite = getRewriter(dcModule)
|
|
88
|
+
const result = rewrite(
|
|
89
|
+
source,
|
|
90
|
+
resourcePath,
|
|
91
|
+
esm ? 'module' : 'commonjs',
|
|
92
|
+
rewriteTarget,
|
|
93
|
+
inputSourceMap
|
|
94
|
+
)
|
|
95
|
+
code = /** @type {string} */ (result.code)
|
|
96
|
+
sourceMap = result.map
|
|
97
|
+
rewritten = code !== source
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (publications.length > 0) code = appendCommonJsPublications(code, publications, dcModule)
|
|
101
|
+
if (rewritten && publications.length === 0) {
|
|
102
|
+
if (!esm && hasUnsafeCommonJsBindings(code)) {
|
|
103
|
+
this.emitWarning?.(new Error(`Skipped CommonJS activation for unsafe wrapper bindings in ${resourcePath}`))
|
|
104
|
+
this.callback(undefined, source, inputSourceMap)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
code = appendActivation(code, {
|
|
108
|
+
package: rewriteTarget.moduleName,
|
|
109
|
+
path: filename(rewriteTarget.moduleName, rewriteTarget.filePath),
|
|
110
|
+
version: packageInfo.version,
|
|
111
|
+
}, dcModule, esm)
|
|
112
|
+
}
|
|
113
|
+
this.callback(undefined, code, sourceMap)
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const warning = error instanceof Error ? error : new Error(String(error))
|
|
116
|
+
this.emitWarning?.(warning)
|
|
117
|
+
this.callback(undefined, source, inputSourceMap)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {LoaderContext} context
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
function getResourcePath (context) {
|
|
126
|
+
const { resourcePath } = context
|
|
127
|
+
if (!resourcePath.endsWith(SYNTHETIC_EXTENSION) || fs.existsSync(resourcePath)) return resourcePath
|
|
128
|
+
|
|
129
|
+
const extensionlessPath = resourcePath.slice(0, -SYNTHETIC_EXTENSION.length)
|
|
130
|
+
return fs.existsSync(extensionlessPath) ? extensionlessPath : resourcePath
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} nativeResourcePath
|
|
135
|
+
* @param {string} name
|
|
136
|
+
* @param {string} modulePath
|
|
137
|
+
* @param {string} packageJsonPath
|
|
138
|
+
* @returns {PackageInfo}
|
|
139
|
+
*/
|
|
140
|
+
function getPackageInfo (nativeResourcePath, name, modulePath, packageJsonPath) {
|
|
141
|
+
let cached = packageCache.get(packageJsonPath)
|
|
142
|
+
if (!cached) {
|
|
143
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
144
|
+
cached = { packageJson, version: packageJson.version }
|
|
145
|
+
packageCache.set(packageJsonPath, cached)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
esm: isESMFile(nativeResourcePath, packageJsonPath, cached.packageJson),
|
|
150
|
+
moduleName: filename(name, modulePath),
|
|
151
|
+
name,
|
|
152
|
+
packageJsonPath,
|
|
153
|
+
version: cached.version,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {string} resourcePath
|
|
159
|
+
* @param {PackageInfo} packageInfo
|
|
160
|
+
* @returns {Array<{ package: string, path: string, version: string }>}
|
|
161
|
+
*/
|
|
162
|
+
function getPublications (resourcePath, packageInfo) {
|
|
163
|
+
loadPackageHook(packageInfo.name)
|
|
164
|
+
const entries = instrumentations[packageInfo.name]
|
|
165
|
+
if (!entries) return []
|
|
166
|
+
|
|
167
|
+
const publications = []
|
|
168
|
+
const paths = new Set()
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
if (!entry.file && !entry.filePattern) {
|
|
171
|
+
packageInfo.entrypoint ??= getCommonJsEntrypoint(packageInfo.name, packageInfo.packageJsonPath)
|
|
172
|
+
}
|
|
173
|
+
const moduleName = entry.file || entry.filePattern
|
|
174
|
+
? packageInfo.moduleName
|
|
175
|
+
: resourcePath === packageInfo.entrypoint ? packageInfo.name : undefined
|
|
176
|
+
if (!moduleName || paths.has(moduleName) ||
|
|
177
|
+
!matchesInstrumentation(packageInfo.name, packageInfo.version, moduleName, entry)) continue
|
|
178
|
+
|
|
179
|
+
paths.add(moduleName)
|
|
180
|
+
publications.push({ package: packageInfo.name, path: moduleName, version: packageInfo.version })
|
|
181
|
+
}
|
|
182
|
+
return publications
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {string} name
|
|
187
|
+
* @param {string} packageJsonPath
|
|
188
|
+
* @returns {string}
|
|
189
|
+
*/
|
|
190
|
+
function getCommonJsEntrypoint (name, packageJsonPath) {
|
|
191
|
+
const cached = entrypoints.get(packageJsonPath)
|
|
192
|
+
if (cached) return cached
|
|
193
|
+
|
|
194
|
+
const packageRequire = Module.createRequire(packageJsonPath)
|
|
195
|
+
let entrypoint
|
|
196
|
+
try {
|
|
197
|
+
entrypoint = packageRequire.resolve(name)
|
|
198
|
+
} catch {
|
|
199
|
+
entrypoint = packageRequire.resolve(path.dirname(packageJsonPath))
|
|
200
|
+
}
|
|
201
|
+
const resolved = fs.realpathSync(entrypoint).replaceAll('\\', '/')
|
|
202
|
+
entrypoints.set(packageJsonPath, resolved)
|
|
203
|
+
return resolved
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {string} name
|
|
208
|
+
*/
|
|
209
|
+
function loadPackageHook (name) {
|
|
210
|
+
if (loadedHooks.has(name)) return
|
|
211
|
+
const hook = hooks[name]
|
|
212
|
+
const load = hook?.fn ?? hook
|
|
213
|
+
if (typeof load === 'function') load()
|
|
214
|
+
loadedHooks.add(name)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {string} dcModule
|
|
219
|
+
* @returns {ReturnType<typeof createBundlerRewriter>}
|
|
220
|
+
*/
|
|
221
|
+
function getRewriter (dcModule) {
|
|
222
|
+
const cached = rewriters.get(dcModule)
|
|
223
|
+
if (cached) return cached
|
|
224
|
+
const rewrite = createBundlerRewriter(dcModule)
|
|
225
|
+
rewriters.set(dcModule, rewrite)
|
|
226
|
+
return rewrite
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {string} source
|
|
231
|
+
* @returns {boolean}
|
|
232
|
+
*/
|
|
233
|
+
function hasUnsafeCommonJsBindings (source) {
|
|
234
|
+
try {
|
|
235
|
+
const { parse } = require('../../datadog-instrumentations/src/helpers/rewriter/compiler')
|
|
236
|
+
if (source.startsWith('#!')) source = `//${source.slice(2)}`
|
|
237
|
+
const program = parse(Module.wrap(source))
|
|
238
|
+
const wrapper = program.body[0]?.expression
|
|
239
|
+
return wrapper?.type !== 'FunctionExpression' || hasUnsafeNode(wrapper.body, 0, 0)
|
|
240
|
+
} catch {
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @param {object} node
|
|
247
|
+
* @param {number} functionDepth
|
|
248
|
+
* @param {number} shadowedBindings
|
|
249
|
+
* @param {object} [parent]
|
|
250
|
+
* @param {string} [parentKey]
|
|
251
|
+
* @returns {boolean}
|
|
252
|
+
*/
|
|
253
|
+
function hasUnsafeNode (node, functionDepth, shadowedBindings, parent, parentKey) {
|
|
254
|
+
if (functionDepth === 0 && hasCommonJsDeclaration(node, parent)) return true
|
|
255
|
+
if (node.type === 'AssignmentExpression' && mutatesCommonJsBinding(node.left, shadowedBindings)) return true
|
|
256
|
+
if (node.type === 'UpdateExpression' && mutatesCommonJsBinding(node.argument, shadowedBindings)) return true
|
|
257
|
+
if ((node.type === 'ForInStatement' || node.type === 'ForOfStatement') &&
|
|
258
|
+
mutatesCommonJsBinding(node.left, shadowedBindings)) return true
|
|
259
|
+
if (node.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'eval') return true
|
|
260
|
+
if (!(shadowedBindings & ARGUMENTS_BINDING) && node.type === 'Identifier' && node.name === 'arguments' &&
|
|
261
|
+
isReferencedIdentifier(parent, parentKey)) return true
|
|
262
|
+
|
|
263
|
+
const nestedFunction = node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' ||
|
|
264
|
+
node.type === 'FunctionExpression'
|
|
265
|
+
if (nestedFunction) {
|
|
266
|
+
functionDepth++
|
|
267
|
+
shadowedBindings |= getFunctionBindings(node)
|
|
268
|
+
}
|
|
269
|
+
shadowedBindings |= getLexicalBindings(node)
|
|
270
|
+
|
|
271
|
+
for (const key of Object.keys(node)) {
|
|
272
|
+
if ((node.type === 'VariableDeclarator' && key === 'id') ||
|
|
273
|
+
(node.type === 'CatchClause' && key === 'param')) continue
|
|
274
|
+
const value = node[key]
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
for (const child of value) {
|
|
277
|
+
if (child?.type && hasUnsafeNode(child, functionDepth, shadowedBindings, node, key)) return true
|
|
278
|
+
}
|
|
279
|
+
} else if (value?.type && hasUnsafeNode(value, functionDepth, shadowedBindings, node, key)) {
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return false
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @param {object} node
|
|
288
|
+
* @param {object} [parent]
|
|
289
|
+
* @returns {boolean}
|
|
290
|
+
*/
|
|
291
|
+
function hasCommonJsDeclaration (node, parent) {
|
|
292
|
+
if (node.type === 'VariableDeclarator') {
|
|
293
|
+
return parent?.kind === 'var' && Boolean(getBindingMask(node.id) & COMMONJS_BINDINGS)
|
|
294
|
+
}
|
|
295
|
+
if (node.type === 'FunctionDeclaration') {
|
|
296
|
+
return Boolean(getBindingMask(node.id) & COMMONJS_BINDINGS)
|
|
297
|
+
}
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @param {object} node
|
|
303
|
+
* @param {number} shadowedBindings
|
|
304
|
+
* @returns {boolean}
|
|
305
|
+
*/
|
|
306
|
+
function mutatesCommonJsBinding (node, shadowedBindings) {
|
|
307
|
+
return Boolean(getBindingMask(node) & COMMONJS_BINDINGS & ~shadowedBindings)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @param {object} node
|
|
312
|
+
* @returns {number}
|
|
313
|
+
*/
|
|
314
|
+
function getBindingMask (node) {
|
|
315
|
+
if (!node) return 0
|
|
316
|
+
if (node.type === 'Identifier') {
|
|
317
|
+
if (node.name === 'arguments') return ARGUMENTS_BINDING
|
|
318
|
+
if (node.name === 'module') return MODULE_BINDING
|
|
319
|
+
return node.name === 'require' ? REQUIRE_BINDING : 0
|
|
320
|
+
}
|
|
321
|
+
if (node.type === 'AssignmentPattern') return getBindingMask(node.left)
|
|
322
|
+
if (node.type === 'RestElement') return getBindingMask(node.argument)
|
|
323
|
+
if (node.type === 'ArrayPattern') {
|
|
324
|
+
let bindings = 0
|
|
325
|
+
for (const element of node.elements) bindings |= getBindingMask(element)
|
|
326
|
+
return bindings
|
|
327
|
+
}
|
|
328
|
+
if (node.type === 'ObjectPattern') {
|
|
329
|
+
let bindings = 0
|
|
330
|
+
for (const property of node.properties) {
|
|
331
|
+
bindings |= getBindingMask(property.type === 'RestElement' ? property.argument : property.value)
|
|
332
|
+
}
|
|
333
|
+
return bindings
|
|
334
|
+
}
|
|
335
|
+
return 0
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @param {object} node
|
|
340
|
+
* @returns {number}
|
|
341
|
+
*/
|
|
342
|
+
function getFunctionBindings (node) {
|
|
343
|
+
let bindings = node.type === 'ArrowFunctionExpression' ? 0 : ARGUMENTS_BINDING
|
|
344
|
+
bindings |= getBindingMask(node.id)
|
|
345
|
+
for (const param of node.params) bindings |= getBindingMask(param)
|
|
346
|
+
return bindings | getVarBindings(node.body)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @param {object} node
|
|
351
|
+
* @returns {number}
|
|
352
|
+
*/
|
|
353
|
+
function getVarBindings (node) {
|
|
354
|
+
if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' ||
|
|
355
|
+
node.type === 'FunctionExpression' || node.type === 'ClassDeclaration' || node.type === 'ClassExpression') return 0
|
|
356
|
+
|
|
357
|
+
let bindings = 0
|
|
358
|
+
if (node.type === 'VariableDeclaration' && node.kind === 'var') {
|
|
359
|
+
for (const declaration of node.declarations) bindings |= getBindingMask(declaration.id)
|
|
360
|
+
}
|
|
361
|
+
for (const key of Object.keys(node)) {
|
|
362
|
+
const value = node[key]
|
|
363
|
+
if (Array.isArray(value)) {
|
|
364
|
+
for (const child of value) {
|
|
365
|
+
if (child?.type) bindings |= getVarBindings(child)
|
|
366
|
+
}
|
|
367
|
+
} else if (value?.type) {
|
|
368
|
+
bindings |= getVarBindings(value)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return bindings
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @param {object} node
|
|
376
|
+
* @returns {number}
|
|
377
|
+
*/
|
|
378
|
+
function getLexicalBindings (node) {
|
|
379
|
+
let bindings = 0
|
|
380
|
+
if (node.type === 'BlockStatement') {
|
|
381
|
+
for (const statement of node.body) bindings |= getStatementBindings(statement)
|
|
382
|
+
} else if (node.type === 'SwitchStatement') {
|
|
383
|
+
for (const switchCase of node.cases) {
|
|
384
|
+
for (const statement of switchCase.consequent) bindings |= getStatementBindings(statement)
|
|
385
|
+
}
|
|
386
|
+
} else if (node.type === 'CatchClause') {
|
|
387
|
+
bindings = getBindingMask(node.param)
|
|
388
|
+
} else if (node.type === 'ForStatement') {
|
|
389
|
+
bindings = getLexicalDeclarationBindings(node.init)
|
|
390
|
+
} else if (node.type === 'ForInStatement' || node.type === 'ForOfStatement') {
|
|
391
|
+
bindings = getLexicalDeclarationBindings(node.left)
|
|
392
|
+
} else if (node.type === 'ClassExpression') {
|
|
393
|
+
bindings = getBindingMask(node.id)
|
|
394
|
+
}
|
|
395
|
+
return bindings
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* @param {object} node
|
|
400
|
+
* @returns {number}
|
|
401
|
+
*/
|
|
402
|
+
function getStatementBindings (node) {
|
|
403
|
+
if (node.type === 'VariableDeclaration') return getLexicalDeclarationBindings(node)
|
|
404
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') return getBindingMask(node.id)
|
|
405
|
+
return 0
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @param {object} [node]
|
|
410
|
+
* @returns {number}
|
|
411
|
+
*/
|
|
412
|
+
function getLexicalDeclarationBindings (node) {
|
|
413
|
+
if (node?.type !== 'VariableDeclaration' || node.kind === 'var') return 0
|
|
414
|
+
let bindings = 0
|
|
415
|
+
for (const declaration of node.declarations) bindings |= getBindingMask(declaration.id)
|
|
416
|
+
return bindings
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @param {object} parent
|
|
421
|
+
* @param {string} parentKey
|
|
422
|
+
* @returns {boolean}
|
|
423
|
+
*/
|
|
424
|
+
function isReferencedIdentifier (parent, parentKey) {
|
|
425
|
+
if (parent.type === 'MemberExpression' && parentKey === 'property' && !parent.computed) return false
|
|
426
|
+
if ((parent.type === 'Property' || parent.type === 'MethodDefinition' || parent.type === 'PropertyDefinition') &&
|
|
427
|
+
parentKey === 'key' && !parent.computed) {
|
|
428
|
+
return parent.type === 'Property' && parent.shorthand
|
|
429
|
+
}
|
|
430
|
+
return parentKey !== 'label'
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @param {string} source
|
|
435
|
+
* @param {Array<{ package: string, path: string, version: string }>} payloads
|
|
436
|
+
* @param {string} dcModule
|
|
437
|
+
* @returns {string}
|
|
438
|
+
*/
|
|
439
|
+
function appendCommonJsPublications (source, payloads, dcModule) {
|
|
440
|
+
let publications = ''
|
|
441
|
+
let index = 0
|
|
442
|
+
for (const payload of payloads) {
|
|
443
|
+
publications += ` const payload${index} = {
|
|
444
|
+
module: module.exports,
|
|
445
|
+
package: ${JSON.stringify(payload.package)},
|
|
446
|
+
path: ${JSON.stringify(payload.path)},
|
|
447
|
+
version: ${JSON.stringify(payload.version)},
|
|
448
|
+
}
|
|
449
|
+
channel.publish(payload${index})
|
|
450
|
+
module.exports = payload${index++}.module
|
|
451
|
+
`
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return `${source}
|
|
455
|
+
{
|
|
456
|
+
const dc = require(${JSON.stringify(dcModule)})
|
|
457
|
+
const channel = dc.channel('${CHANNEL}')
|
|
458
|
+
if (channel.hasSubscribers) {
|
|
459
|
+
${publications} }
|
|
460
|
+
}
|
|
461
|
+
`
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* @param {string} source
|
|
466
|
+
* @param {{ package: string, path: string, version: string }} payload
|
|
467
|
+
* @param {string} dcModule
|
|
468
|
+
* @param {boolean} esm
|
|
469
|
+
* @returns {string}
|
|
470
|
+
*/
|
|
471
|
+
function appendActivation (source, payload, dcModule, esm) {
|
|
472
|
+
if (esm) {
|
|
473
|
+
return `${source}
|
|
474
|
+
import ddTraceTurbopackDc from ${JSON.stringify(dcModule)}
|
|
475
|
+
{
|
|
476
|
+
const channel = ddTraceTurbopackDc.channel('${CHANNEL}')
|
|
477
|
+
if (channel.hasSubscribers) channel.publish(${JSON.stringify({ activate: true, ...payload })})
|
|
478
|
+
}
|
|
479
|
+
`
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return `${source}
|
|
483
|
+
{
|
|
484
|
+
const dc = require(${JSON.stringify(dcModule)})
|
|
485
|
+
const channel = dc.channel('${CHANNEL}')
|
|
486
|
+
if (channel.hasSubscribers) channel.publish(${JSON.stringify({ activate: true, ...payload })})
|
|
487
|
+
}
|
|
488
|
+
`
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @param {string} from
|
|
493
|
+
* @param {string} to
|
|
494
|
+
* @returns {string}
|
|
495
|
+
*/
|
|
496
|
+
function relativeImport (from, to) {
|
|
497
|
+
let value = path.relative(from, to).replaceAll('\\', '/')
|
|
498
|
+
if (!value.startsWith('.')) value = `./${value}`
|
|
499
|
+
return value
|
|
500
|
+
}
|
|
@@ -18,9 +18,10 @@ function enable (tracer, config) {
|
|
|
18
18
|
try {
|
|
19
19
|
aiguard = new AIGuard(tracer, config)
|
|
20
20
|
const block = config.experimental?.aiguard?.block !== false
|
|
21
|
+
const analyzeStreamResponses = config.DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED
|
|
21
22
|
|
|
22
23
|
incomingHttpRequestStart.subscribe(onIncomingHttpRequestStart)
|
|
23
|
-
integrations.enable(aiguard, block)
|
|
24
|
+
integrations.enable(aiguard, block, analyzeStreamResponses)
|
|
24
25
|
|
|
25
26
|
isEnabled = true
|
|
26
27
|
} catch (err) {
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
|
-
const log = require('../../log')
|
|
6
5
|
const { getMessagesInputMessages, getMessagesOutputMessages } = require('../messages/anthropic')
|
|
6
|
+
const { decode } = require('../messages/utils')
|
|
7
7
|
const { SOURCE_AUTO } = require('../tags')
|
|
8
|
-
const {
|
|
8
|
+
const { evaluate } = require('./evaluate')
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const messagesPrepareChannel = channel('dd-trace:anthropic:messages:prepare')
|
|
11
|
+
const messagesInterceptChannel = channel('dd-trace:anthropic:messages:intercept')
|
|
12
12
|
|
|
13
13
|
let isEnabled = false
|
|
14
14
|
let aiguard
|
|
15
15
|
let opts
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Subscribes AI Guard to Anthropic
|
|
18
|
+
* Subscribes AI Guard to the Anthropic interception channels.
|
|
19
19
|
*
|
|
20
20
|
* @param {object} aiguardInstance
|
|
21
21
|
* @param {boolean} block
|
|
@@ -26,8 +26,8 @@ function enable (aiguardInstance, block) {
|
|
|
26
26
|
aiguard = aiguardInstance
|
|
27
27
|
opts = { block, source: SOURCE_AUTO, integration: 'anthropic' }
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
messagesPrepareChannel.subscribe(onMessagesPrepare)
|
|
30
|
+
messagesInterceptChannel.subscribe(onMessagesIntercept)
|
|
31
31
|
|
|
32
32
|
isEnabled = true
|
|
33
33
|
}
|
|
@@ -35,36 +35,64 @@ function enable (aiguardInstance, block) {
|
|
|
35
35
|
function disable () {
|
|
36
36
|
if (!isEnabled) return
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
messagesPrepareChannel.unsubscribe(onMessagesPrepare)
|
|
39
|
+
messagesInterceptChannel.unsubscribe(onMessagesIntercept)
|
|
40
40
|
|
|
41
41
|
aiguard = undefined
|
|
42
42
|
opts = undefined
|
|
43
43
|
isEnabled = false
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Replaces the outgoing options with a JSON snapshot of the evaluated fields, so AI Guard judges
|
|
48
|
+
* exactly what the SDK serializes and sends, immune to later caller mutation.
|
|
49
|
+
*
|
|
50
|
+
* @param {{ arguments: Array<unknown> }} ctx
|
|
51
|
+
*/
|
|
52
|
+
function onMessagesPrepare (ctx) {
|
|
53
|
+
const options = ctx.arguments[0]
|
|
54
|
+
if (!options || typeof options !== 'object') return
|
|
55
|
+
|
|
56
|
+
const evaluated = { messages: options.messages }
|
|
57
|
+
if (options.system !== undefined) evaluated.system = options.system
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// eslint-disable-next-line unicorn/prefer-structured-clone
|
|
61
|
+
ctx.arguments[0] = { ...options, ...JSON.parse(JSON.stringify(evaluated)) }
|
|
62
|
+
} catch {
|
|
63
|
+
// Unserializable input — judge the caller's options as they are.
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
function
|
|
51
|
-
const inputMessages = getMessagesInputMessages(ctx.
|
|
67
|
+
function onMessagesIntercept (ctx) {
|
|
68
|
+
const inputMessages = getMessagesInputMessages(ctx.arguments?.[0])
|
|
52
69
|
if (!inputMessages?.length) return
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return
|
|
61
|
-
}
|
|
71
|
+
// `parse` and `asResponse` are both wrapped, and either may run more than once per call.
|
|
72
|
+
let inputEvaluation
|
|
73
|
+
ctx.beforeResult = () => {
|
|
74
|
+
if (!isEnabled) return
|
|
75
|
+
inputEvaluation ??= evaluate(ctx, aiguard, [inputMessages], opts)
|
|
76
|
+
return inputEvaluation
|
|
62
77
|
}
|
|
63
78
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
// One model call has one output however many readers observe it: `parse`, `json()`, `text()`
|
|
80
|
+
// and every `clone()` of the raw response share this callback.
|
|
81
|
+
let outputEvaluation
|
|
82
|
+
ctx.onResult = body => {
|
|
83
|
+
if (!isEnabled) return body
|
|
84
|
+
|
|
85
|
+
const outputMessages = decode(
|
|
86
|
+
() => getMessagesOutputMessages(body),
|
|
87
|
+
null,
|
|
88
|
+
'AIGuard: unable to decode Anthropic response body: %s'
|
|
89
|
+
)
|
|
90
|
+
if (outputMessages === null) return body
|
|
91
|
+
if (!outputMessages.length) return body
|
|
92
|
+
|
|
93
|
+
outputEvaluation ??= evaluate(ctx, aiguard, [[...inputMessages, ...outputMessages]], opts)
|
|
94
|
+
return outputEvaluation.then(() => body)
|
|
95
|
+
}
|
|
68
96
|
}
|
|
69
97
|
|
|
70
98
|
module.exports = { enable, disable }
|