dd-trace 5.132.0 → 5.134.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/ci/diagnose.js +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-runner-setup.mjs +27 -0
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +41 -19
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +25 -22
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +65 -41
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +28 -9
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +45 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +89 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/jest.js +12 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/vitest.js +8 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +5 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +72 -67
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +38 -4
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +492 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/playwright.js +2 -0
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/stripe.js +23 -12
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +16 -18
- package/packages/datadog-instrumentations/src/vitest-util.js +7 -4
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +9 -1
- package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +4 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +3 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +7 -10
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/evaluation.js +143 -1
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +55 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +44 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -5
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -5
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +4 -6
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/debugger/unsupported-runtime.js +16 -0
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -5
- package/packages/dd-trace/src/id.js +14 -1
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/agent-manifest.js +19 -2
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +15 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +5 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +64 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +15 -4
- package/packages/dd-trace/src/llmobs/span_processor.js +13 -2
- package/packages/dd-trace/src/llmobs/tagger.js +20 -10
- package/packages/dd-trace/src/llmobs/util.js +11 -6
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
- package/packages/dd-trace/src/openfeature/index.js +6 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +4 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +24 -24
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
- package/packages/dd-trace/src/plugin_manager.js +1 -2
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/tracing.js +0 -1
- package/packages/dd-trace/src/plugins/util/git.js +99 -59
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -1
- package/packages/dd-trace/src/plugins/util/test.js +50 -4
- package/packages/dd-trace/src/plugins/util/web.js +90 -20
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/proxy.js +1 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +5 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/span_stats.js +4 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +1 -1
- package/packages/dd-trace/src/util.js +37 -4
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +8 -8
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
|
@@ -64,8 +64,7 @@ class DDOpenAIAgentsProcessor {
|
|
|
64
64
|
|
|
65
65
|
onSpanStart (oaiSpan) {
|
|
66
66
|
const integration = this._getIntegration()
|
|
67
|
-
if (!integration?.enabled) return RESOLVED
|
|
68
|
-
if (!oaiSpan?.spanData) return RESOLVED // guard NoopSpan
|
|
67
|
+
if (!integration?.enabled || !oaiSpan?.spanData) return RESOLVED // guard NoopSpan
|
|
69
68
|
const type = oaiSpan.spanData.type
|
|
70
69
|
const kind = SPAN_KIND_BY_TYPE[type]
|
|
71
70
|
try {
|
|
@@ -84,8 +83,7 @@ class DDOpenAIAgentsProcessor {
|
|
|
84
83
|
|
|
85
84
|
onSpanEnd (oaiSpan) {
|
|
86
85
|
const integration = this._getIntegration()
|
|
87
|
-
if (!integration?.enabled) return RESOLVED
|
|
88
|
-
if (!oaiSpan?.spanData) return RESOLVED
|
|
86
|
+
if (!integration?.enabled || !oaiSpan?.spanData) return RESOLVED
|
|
89
87
|
const type = oaiSpan.spanData.type
|
|
90
88
|
const kind = SPAN_KIND_BY_TYPE[type]
|
|
91
89
|
try {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { performance } = require('node:perf_hooks')
|
|
4
|
+
|
|
3
5
|
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
4
6
|
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
5
7
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
6
8
|
|
|
9
|
+
const ACQUIRE_START_CHANNEL = 'tracing:orchestrion:postgres:query:acquire:start'
|
|
7
10
|
const PREPARE_START_CHANNEL = 'tracing:orchestrion:postgres:query:prepare:start'
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -14,6 +17,8 @@ const PREPARE_START_CHANNEL = 'tracing:orchestrion:postgres:query:prepare:start'
|
|
|
14
17
|
* @property {string} [host]
|
|
15
18
|
* @property {number} [pid]
|
|
16
19
|
* @property {number} [port]
|
|
20
|
+
* @property {boolean} [poolWaitFinished]
|
|
21
|
+
* @property {number} [poolWaitStartMs]
|
|
17
22
|
* @property {PostgresQuery} query
|
|
18
23
|
* @property {string} [statement]
|
|
19
24
|
* @property {string} user
|
|
@@ -63,12 +68,16 @@ class PostgresPlugin extends DatabasePlugin {
|
|
|
63
68
|
/** @type {WeakMap<PostgresQuery, import('../../..').Span>} */
|
|
64
69
|
#spans = new WeakMap()
|
|
65
70
|
|
|
71
|
+
/** @type {WeakMap<PostgresQuery, PostgresContext>} */
|
|
72
|
+
#waitContexts = new WeakMap()
|
|
73
|
+
|
|
66
74
|
/**
|
|
67
75
|
* @param {object} tracer
|
|
68
76
|
* @param {import('../../dd-trace/src/config/config-base')} tracerConfig
|
|
69
77
|
*/
|
|
70
78
|
constructor (tracer, tracerConfig) {
|
|
71
79
|
super(tracer, tracerConfig)
|
|
80
|
+
this.addSub(ACQUIRE_START_CHANNEL, ctx => this.#finishPoolWait(ctx.query))
|
|
72
81
|
this.#preparation = new PostgresPreparationPlugin(this.#prepare.bind(this), tracer, tracerConfig)
|
|
73
82
|
}
|
|
74
83
|
|
|
@@ -112,12 +121,26 @@ class PostgresPlugin extends DatabasePlugin {
|
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
this.#spans.set(query, span)
|
|
124
|
+
this.#waitContexts.set(query, ctx)
|
|
115
125
|
if (this.#dbmEnabled) {
|
|
116
126
|
this.#dbmQueries.set(query, { service: service.name })
|
|
117
127
|
}
|
|
118
128
|
return ctx.currentStore
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
/**
|
|
132
|
+
* @param {PostgresQuery} query
|
|
133
|
+
*/
|
|
134
|
+
#finishPoolWait (query) {
|
|
135
|
+
const ctx = this.#waitContexts.get(query)
|
|
136
|
+
if (ctx === undefined) return
|
|
137
|
+
|
|
138
|
+
this.#waitContexts.delete(query)
|
|
139
|
+
ctx.poolWaitFinished = true
|
|
140
|
+
const waitTime = ctx.poolWaitStartMs === undefined ? 0 : performance.now() - ctx.poolWaitStartMs
|
|
141
|
+
ctx.currentStore.span.setTag('db.pool.wait_time_ms', waitTime)
|
|
142
|
+
}
|
|
143
|
+
|
|
121
144
|
/**
|
|
122
145
|
* @param {PostgresPreparationContext} ctx
|
|
123
146
|
*/
|
|
@@ -152,6 +175,7 @@ class PostgresPlugin extends DatabasePlugin {
|
|
|
152
175
|
this.#dbmQueries.delete(query)
|
|
153
176
|
if (span === undefined) return
|
|
154
177
|
|
|
178
|
+
this.#finishPoolWait(query)
|
|
155
179
|
this.#spans.delete(query)
|
|
156
180
|
|
|
157
181
|
const statement = dbmState?.statement ?? result.statement
|
|
@@ -144,12 +144,8 @@ class SchemaExtractor {
|
|
|
144
144
|
span.setTag(SCHEMA_NAME, removeLeadingPeriod(descriptor.fullName))
|
|
145
145
|
span.setTag(SCHEMA_OPERATION, operation)
|
|
146
146
|
|
|
147
|
-
if (!tracer._dataStreamsProcessor.canSampleSchema(operation)) {
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
|
|
151
147
|
// if the span is unsampled, do not sample the schema
|
|
152
|
-
if (!tracer._prioritySampler.isSampled(span)) {
|
|
148
|
+
if (!tracer._dataStreamsProcessor.canSampleSchema(operation) || !tracer._prioritySampler.isSampled(span)) {
|
|
153
149
|
return
|
|
154
150
|
}
|
|
155
151
|
|
|
@@ -52,10 +52,7 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
52
52
|
|
|
53
53
|
function getResourceNameFromMessage (msgObj) {
|
|
54
54
|
let resourceName = 'amq.topic'
|
|
55
|
-
|
|
56
|
-
if (msgObj.receiver && msgObj.receiver.options) {
|
|
57
|
-
options = msgObj.receiver.options
|
|
58
|
-
}
|
|
55
|
+
const options = msgObj.receiver && msgObj.receiver.options ? msgObj.receiver.options : {}
|
|
59
56
|
if (options.source && options.source.address) {
|
|
60
57
|
resourceName = options.source.address
|
|
61
58
|
}
|
|
@@ -354,6 +354,8 @@ class VitestPlugin extends CiPlugin {
|
|
|
354
354
|
browserProjectName,
|
|
355
355
|
testExecutionId,
|
|
356
356
|
testStartLine,
|
|
357
|
+
startTime,
|
|
358
|
+
duration,
|
|
357
359
|
}) => {
|
|
358
360
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
359
361
|
const extraTags = {
|
|
@@ -380,10 +382,11 @@ class VitestPlugin extends CiPlugin {
|
|
|
380
382
|
testSuite,
|
|
381
383
|
testSuiteSpan || this.testSuiteSpan,
|
|
382
384
|
extraTags,
|
|
383
|
-
testExecutionId
|
|
385
|
+
testExecutionId,
|
|
386
|
+
startTime
|
|
384
387
|
)
|
|
385
388
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(testSpan))
|
|
386
|
-
testSpan.finish()
|
|
389
|
+
testSpan.finish(Number.isFinite(duration) && duration >= 0 ? testSpan._startTime + duration : undefined)
|
|
387
390
|
})
|
|
388
391
|
|
|
389
392
|
this.addBind('ci:vitest:test-suite:start', (ctx) => {
|
|
@@ -314,11 +314,11 @@ function assertMethod (target, name, method) {
|
|
|
314
314
|
let message = 'No target object provided'
|
|
315
315
|
|
|
316
316
|
if (target) {
|
|
317
|
-
if (typeof target
|
|
318
|
-
message = 'Invalid target'
|
|
319
|
-
} else {
|
|
317
|
+
if (typeof target === 'object' || typeof target === 'function') {
|
|
320
318
|
name = String(name)
|
|
321
319
|
message = method ? `Original method ${name} is not a function` : `No original method ${name}`
|
|
320
|
+
} else {
|
|
321
|
+
message = 'Invalid target'
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
@@ -335,7 +335,8 @@ function assertMethod (target, name, method) {
|
|
|
335
335
|
function assertNotClass (target) {
|
|
336
336
|
// Frozen functions also have a non-writable `prototype`; inspect their source only then.
|
|
337
337
|
if ('prototype' in target &&
|
|
338
|
-
|
|
338
|
+
// Bound generators inherit `prototype` without owning one, so the descriptor may still be missing.
|
|
339
|
+
Object.getOwnPropertyDescriptor(target, 'prototype')?.writable === false &&
|
|
339
340
|
(!Object.isFrozen(target) || Function.prototype.toString.call(target).startsWith('class'))) {
|
|
340
341
|
throw new TypeError('Target is a native class constructor and cannot be wrapped.')
|
|
341
342
|
}
|
|
@@ -6,11 +6,15 @@ const fs = require('node:fs')
|
|
|
6
6
|
const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
|
|
7
7
|
const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
|
|
8
8
|
const hooks = require('../datadog-instrumentations/src/helpers/hooks')
|
|
9
|
+
const { getRewriteTarget } = require('../datadog-instrumentations/src/helpers/rewriter/targets')
|
|
9
10
|
const { isESMFile } = require('../datadog-esbuild/src/utils')
|
|
11
|
+
const { getValueFromEnvSources } = require('../dd-trace/src/config/helper')
|
|
10
12
|
const log = require('./src/log')
|
|
11
13
|
|
|
12
14
|
const PLUGIN_NAME = 'DatadogWebpackPlugin'
|
|
13
15
|
|
|
16
|
+
const DD_BUNDLER_INJECT_GIT_METADATA = getValueFromEnvSources('DD_BUNDLER_INJECT_GIT_METADATA') !== false
|
|
17
|
+
|
|
14
18
|
for (const hook of Object.values(hooks)) {
|
|
15
19
|
if (hook !== null && typeof hook === 'object') {
|
|
16
20
|
hook.fn()
|
|
@@ -80,50 +84,55 @@ class DatadogWebpackPlugin {
|
|
|
80
84
|
}
|
|
81
85
|
})
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
for (const
|
|
103
|
-
if (!
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
87
|
+
// Bake DD_GIT_* into assets unless DD_BUNDLER_INJECT_GIT_METADATA=false (stable artifact hashes).
|
|
88
|
+
if (DD_BUNDLER_INJECT_GIT_METADATA) {
|
|
89
|
+
const gitMetadata = getGitMetadata()
|
|
90
|
+
if (gitMetadata.repositoryURL || gitMetadata.commitSHA) {
|
|
91
|
+
const banner =
|
|
92
|
+
'if (typeof process === \'object\' && process !== null &&\n' +
|
|
93
|
+
' process.env !== null && typeof process.env === \'object\') {\n' +
|
|
94
|
+
(gitMetadata.repositoryURL
|
|
95
|
+
? ` process.env.DD_GIT_REPOSITORY_URL = ${JSON.stringify(gitMetadata.repositoryURL)};\n`
|
|
96
|
+
: '') +
|
|
97
|
+
(gitMetadata.commitSHA
|
|
98
|
+
? ` process.env.DD_GIT_COMMIT_SHA = ${JSON.stringify(gitMetadata.commitSHA)};\n`
|
|
99
|
+
: '') +
|
|
100
|
+
'}\n'
|
|
101
|
+
|
|
102
|
+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
|
103
|
+
compilation.hooks.processAssets.tap(
|
|
104
|
+
{ name: PLUGIN_NAME, stage: -2000 },
|
|
105
|
+
() => {
|
|
106
|
+
for (const chunk of compilation.chunks) {
|
|
107
|
+
if (!chunk.canBeInitial()) continue
|
|
108
|
+
for (const filename of chunk.files) {
|
|
109
|
+
if (!filename.endsWith('.js') && !filename.endsWith('.mjs')) continue
|
|
110
|
+
compilation.updateAsset(filename, (old) => {
|
|
111
|
+
const content = banner + old.source()
|
|
112
|
+
return {
|
|
113
|
+
source () { return content },
|
|
114
|
+
size () { return Buffer.byteLength(content, 'utf8') },
|
|
115
|
+
map () { return old.map() },
|
|
116
|
+
sourceAndMap () { return { source: content, map: old.map() } },
|
|
117
|
+
updateHash (hash) { hash.update(content) },
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
|
-
|
|
117
|
-
)
|
|
118
|
-
})
|
|
123
|
+
)
|
|
124
|
+
})
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
log.debug(
|
|
127
|
+
'Automatically injected git metadata (DD_GIT_REPOSITORY_URL: %s, DD_GIT_COMMIT_SHA: %s)',
|
|
128
|
+
gitMetadata.repositoryURL || 'not available',
|
|
129
|
+
gitMetadata.commitSHA || 'not available'
|
|
130
|
+
)
|
|
131
|
+
} else {
|
|
132
|
+
log.warn('No git metadata available - skipping injection')
|
|
133
|
+
}
|
|
125
134
|
} else {
|
|
126
|
-
log.
|
|
135
|
+
log.debug('Skipping git metadata injection (DD_BUNDLER_INJECT_GIT_METADATA=false)')
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
compiler.hooks.normalModuleFactory.tap(PLUGIN_NAME, (nmf) => {
|
|
@@ -146,8 +155,9 @@ class DatadogWebpackPlugin {
|
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
const request = resolveData.request
|
|
158
|
+
const isModuleOfInterest = modulesOfInterest.has(request) || modulesOfInterest.has(`${pkg}/${modulePath}`)
|
|
149
159
|
|
|
150
|
-
if (!
|
|
160
|
+
if (!isModuleOfInterest && getRewriteTarget(normalizedResource) === undefined) {
|
|
151
161
|
return
|
|
152
162
|
}
|
|
153
163
|
|
|
@@ -177,10 +187,12 @@ class DatadogWebpackPlugin {
|
|
|
177
187
|
const version = packageJson.version
|
|
178
188
|
const pkgPath = request === pkg ? pkg : `${pkg}/${modulePath}`
|
|
179
189
|
|
|
190
|
+
// Webpack runs loaders right-to-left, so appending makes this loader see the package's original source
|
|
191
|
+
// before any user transpiler lowers the syntax Orchestrion function queries match on.
|
|
180
192
|
createData.loaders ||= []
|
|
181
|
-
createData.loaders.
|
|
193
|
+
createData.loaders.push({
|
|
182
194
|
loader: require.resolve('./src/loader'),
|
|
183
|
-
options: { pkg, version, path: pkgPath },
|
|
195
|
+
options: { pkg, version, path: pkgPath, rewriteOnly: !isModuleOfInterest },
|
|
184
196
|
})
|
|
185
197
|
|
|
186
198
|
log.debug('LOAD: %s@%s, pkg "%s"', pkg, version, pkgPath)
|
|
@@ -1,26 +1,74 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { filename } = require('../../datadog-instrumentations/src/helpers/instrumentation-utils')
|
|
4
|
+
const { createBundlerRewriter } = require('../../datadog-instrumentations/src/helpers/rewriter')
|
|
5
|
+
const { getRewriteTarget } = require('../../datadog-instrumentations/src/helpers/rewriter/targets')
|
|
6
|
+
|
|
3
7
|
const CHANNEL = 'dd-trace:bundler:load'
|
|
8
|
+
// Resolved from dd-trace so the injected require works even when dc-polyfill is not hoisted next to the target.
|
|
9
|
+
const DC_MODULE = require.resolve('dc-polyfill').replaceAll('\\', '/')
|
|
10
|
+
const DC_MODULE_LITERAL = JSON.stringify(DC_MODULE)
|
|
11
|
+
|
|
12
|
+
let rewrite
|
|
4
13
|
|
|
5
14
|
/**
|
|
6
|
-
* Webpack loader
|
|
7
|
-
*
|
|
15
|
+
* Webpack loader called for each module-of-interest identified by DatadogWebpackPlugin.
|
|
16
|
+
* Orchestrion rewrite targets are rewritten and, for pure Orchestrion integrations, get an activation publish
|
|
17
|
+
* appended. Modules with export hooks get a dc-polyfill channel publish of their CJS exports appended.
|
|
8
18
|
*
|
|
9
19
|
* @param {string} source
|
|
20
|
+
* @param {string|object} [inputSourceMap]
|
|
10
21
|
*/
|
|
11
|
-
module.exports = function loader (source) {
|
|
22
|
+
module.exports = function loader (source, inputSourceMap) {
|
|
12
23
|
this.cacheable(false)
|
|
13
|
-
const { pkg, version, path: pkgPath } = this.getOptions()
|
|
24
|
+
const { pkg, version, path: pkgPath, rewriteOnly } = this.getOptions()
|
|
25
|
+
const target = getRewriteTarget(this.resourcePath.replaceAll('\\', '/'))
|
|
26
|
+
|
|
27
|
+
let code = source
|
|
28
|
+
let map = inputSourceMap
|
|
29
|
+
|
|
30
|
+
if (target) {
|
|
31
|
+
rewrite ??= createBundlerRewriter(DC_MODULE)
|
|
32
|
+
const result = rewrite(source, this.resourcePath, 'commonjs', target, inputSourceMap)
|
|
33
|
+
code = result.code
|
|
34
|
+
map = result.map
|
|
35
|
+
|
|
36
|
+
if (code !== source && target.activate) {
|
|
37
|
+
const payload = JSON.stringify({
|
|
38
|
+
activate: true,
|
|
39
|
+
package: target.moduleName,
|
|
40
|
+
version,
|
|
41
|
+
path: filename(target.moduleName, target.filePath),
|
|
42
|
+
})
|
|
43
|
+
this.callback(
|
|
44
|
+
null,
|
|
45
|
+
`${code}\n;{\n` +
|
|
46
|
+
` const __dd_ch = require(${DC_MODULE_LITERAL}).channel('${CHANNEL}');\n` +
|
|
47
|
+
` if (__dd_ch.hasSubscribers) __dd_ch.publish(${payload});\n` +
|
|
48
|
+
'}\n',
|
|
49
|
+
map
|
|
50
|
+
)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Rewrite-only targets have no export hook, so publishing their exports would only report a missing hook.
|
|
56
|
+
if (rewriteOnly) {
|
|
57
|
+
this.callback(null, code, map)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
14
60
|
|
|
15
|
-
|
|
16
|
-
|
|
61
|
+
this.callback(
|
|
62
|
+
null,
|
|
63
|
+
code +
|
|
17
64
|
'\n;{\n' +
|
|
18
|
-
|
|
65
|
+
` const __dd_dc = require(${DC_MODULE_LITERAL});\n` +
|
|
19
66
|
` const __dd_ch = __dd_dc.channel('${CHANNEL}');\n` +
|
|
20
67
|
' const __dd_mod = module.exports;\n' +
|
|
21
68
|
` const __dd_payload = { module: __dd_mod, version: '${version}', package: '${pkg}', path: '${pkgPath}' };\n` +
|
|
22
69
|
' __dd_ch.publish(__dd_payload);\n' +
|
|
23
70
|
' module.exports = __dd_payload.module;\n' +
|
|
24
|
-
'}\n'
|
|
71
|
+
'}\n',
|
|
72
|
+
map
|
|
25
73
|
)
|
|
26
74
|
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
ERROR,
|
|
5
|
+
HTTP_CLIENT_IP,
|
|
6
|
+
HTTP_USERAGENT,
|
|
7
|
+
MANUAL_DROP,
|
|
8
|
+
MANUAL_KEEP,
|
|
9
|
+
NETWORK_CLIENT_IP,
|
|
10
|
+
RESOURCE_NAME,
|
|
11
|
+
SAMPLING_PRIORITY,
|
|
12
|
+
SERVICE_NAME,
|
|
13
|
+
SPAN_KIND,
|
|
14
|
+
SPAN_TYPE,
|
|
15
|
+
} = require('../../../../ext/tags')
|
|
4
16
|
const createRfdc = require('../../../../vendor/dist/rfdc')
|
|
5
17
|
const clone = createRfdc({ proto: false, circles: false })
|
|
6
18
|
const { USER_ID, USER_SESSION_ID } = require('../appsec/addresses')
|
|
7
19
|
const { getActiveRequest } = require('../appsec/store')
|
|
20
|
+
const { ERROR_MESSAGE, ERROR_STACK, ERROR_TYPE, PROCESS_ID, SAMPLING_PRIORITY_KEY } = require('../constants')
|
|
21
|
+
const log = require('../log')
|
|
8
22
|
const { keepTrace } = require('../priority_sampler')
|
|
9
23
|
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
10
24
|
const { AI_GUARD } = require('../standalone/product')
|
|
@@ -20,6 +34,12 @@ const ALLOW = 'ALLOW'
|
|
|
20
34
|
/** @typedef {import('../../../../index').aiguard.RedactionReplacement} RedactionReplacement */
|
|
21
35
|
/** @typedef {import('../opentracing/span')} Span */
|
|
22
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {object} ExtraEntry
|
|
39
|
+
* @property {string} key
|
|
40
|
+
* @property {unknown} value
|
|
41
|
+
*/
|
|
42
|
+
|
|
23
43
|
/**
|
|
24
44
|
* @typedef {object} EvaluationResponse
|
|
25
45
|
* @property {string} action
|
|
@@ -30,6 +50,8 @@ const ALLOW = 'ALLOW'
|
|
|
30
50
|
* @property {boolean} hasTagProbabilities
|
|
31
51
|
* @property {boolean} blockingEnabled
|
|
32
52
|
* @property {unknown} redactionReplacements
|
|
53
|
+
* @property {ExtraEntry[]} extraTags
|
|
54
|
+
* @property {ExtraEntry[]} extraMetastruct
|
|
33
55
|
*/
|
|
34
56
|
|
|
35
57
|
/**
|
|
@@ -40,6 +62,8 @@ const ALLOW = 'ALLOW'
|
|
|
40
62
|
* @property {boolean} shouldBlock
|
|
41
63
|
* @property {boolean} hasTagProbabilities
|
|
42
64
|
* @property {{ enabled: boolean, applied: boolean, failures: number }} redaction
|
|
65
|
+
* @property {ExtraEntry[]} extraTags
|
|
66
|
+
* @property {ExtraEntry[]} extraMetastruct
|
|
43
67
|
*/
|
|
44
68
|
|
|
45
69
|
/**
|
|
@@ -68,6 +92,38 @@ function isRecord (value) {
|
|
|
68
92
|
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
69
93
|
}
|
|
70
94
|
|
|
95
|
+
// Shared by every evaluation without extra entries, so the common case allocates nothing.
|
|
96
|
+
const NO_EXTRA_ENTRIES = /** @type {ExtraEntry[]} */ (/** @type {unknown} */ (Object.freeze([])))
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Normalizes a backend extra field into well-formed key/value entries. The backend sends lists of
|
|
100
|
+
* entries rather than objects because the dotted keys are not valid JSON:API member names. Values
|
|
101
|
+
* are kept as-is: which of them reach the span is decided when they are applied.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} field
|
|
104
|
+
* @param {unknown} entries
|
|
105
|
+
* @returns {ExtraEntry[]}
|
|
106
|
+
*/
|
|
107
|
+
function normalizeExtraEntries (field, entries) {
|
|
108
|
+
if (entries === undefined) return NO_EXTRA_ENTRIES
|
|
109
|
+
if (!Array.isArray(entries)) {
|
|
110
|
+
log.debug('AI Guard: ignoring %s of type %s', field, entries === null ? 'null' : typeof entries)
|
|
111
|
+
return NO_EXTRA_ENTRIES
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const normalized = []
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const key = isRecord(entry) ? entry.key : undefined
|
|
117
|
+
// A __proto__ key is never valid: it cannot become an own property on the span tags or the meta-struct.
|
|
118
|
+
if (typeof key === 'string' && key.length > 0 && key !== '__proto__') {
|
|
119
|
+
normalized.push({ key, value: entry.value })
|
|
120
|
+
} else {
|
|
121
|
+
log.debug('AI Guard: ignoring malformed %s entry', field)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return normalized.length === 0 ? NO_EXTRA_ENTRIES : normalized
|
|
125
|
+
}
|
|
126
|
+
|
|
71
127
|
/**
|
|
72
128
|
* Parses the backend evaluation response into the tracer's internal contract.
|
|
73
129
|
*
|
|
@@ -89,6 +145,9 @@ function parseEvaluationResponse (body) {
|
|
|
89
145
|
hasTagProbabilities: isRecord(attributes.tag_probs),
|
|
90
146
|
blockingEnabled: attributes.is_blocking_enabled === true,
|
|
91
147
|
redactionReplacements: attributes.redaction_replacements,
|
|
148
|
+
// Malformed extra entries are dropped here instead of turning the response into a bad_response.
|
|
149
|
+
extraTags: normalizeExtraEntries('extra_tags', attributes.extra_tags),
|
|
150
|
+
extraMetastruct: normalizeExtraEntries('extra_metastruct', attributes.extra_metastruct),
|
|
92
151
|
}
|
|
93
152
|
}
|
|
94
153
|
|
|
@@ -132,6 +191,8 @@ function createEvaluationOutcome (messages, evaluation, options) {
|
|
|
132
191
|
applied: redaction.redacted,
|
|
133
192
|
failures: redaction.failures,
|
|
134
193
|
},
|
|
194
|
+
extraTags: evaluation.extraTags,
|
|
195
|
+
extraMetastruct: evaluation.extraMetastruct,
|
|
135
196
|
}
|
|
136
197
|
}
|
|
137
198
|
|
|
@@ -147,6 +208,86 @@ const SERVICE_ENTRY_TAG_MAPPINGS = [
|
|
|
147
208
|
[USER_SESSION_ID, TAGS.USR_SESSION_ID_TAG_KEY],
|
|
148
209
|
]
|
|
149
210
|
|
|
211
|
+
// Backend extra tags never override what the tracer computes itself, nor keys that control the
|
|
212
|
+
// span or its trace (sampling, service, resource, error state). Internal _dd keys are covered by
|
|
213
|
+
// isReservedExtraTag instead of being listed here.
|
|
214
|
+
const RESERVED_EXTRA_TAGS = new Set([
|
|
215
|
+
TAGS.ACTION_TAG_KEY,
|
|
216
|
+
TAGS.REASON_TAG_KEY,
|
|
217
|
+
TAGS.TARGET_TAG_KEY,
|
|
218
|
+
TAGS.TOOL_NAME_TAG_KEY,
|
|
219
|
+
TAGS.BLOCKED_TAG_KEY,
|
|
220
|
+
TAGS.REDACTED_TAG_KEY,
|
|
221
|
+
TAGS.EVENT_TAG_KEY,
|
|
222
|
+
...SERVICE_ENTRY_TAG_MAPPINGS.map(([, destinationTag]) => destinationTag),
|
|
223
|
+
MANUAL_KEEP,
|
|
224
|
+
MANUAL_DROP,
|
|
225
|
+
SAMPLING_PRIORITY,
|
|
226
|
+
SERVICE_NAME,
|
|
227
|
+
RESOURCE_NAME,
|
|
228
|
+
SPAN_TYPE,
|
|
229
|
+
SPAN_KIND,
|
|
230
|
+
ERROR,
|
|
231
|
+
ERROR_TYPE,
|
|
232
|
+
ERROR_MESSAGE,
|
|
233
|
+
ERROR_STACK,
|
|
234
|
+
SAMPLING_PRIORITY_KEY,
|
|
235
|
+
PROCESS_ID,
|
|
236
|
+
'analytics.event',
|
|
237
|
+
'language',
|
|
238
|
+
'runtime-id',
|
|
239
|
+
'service.version',
|
|
240
|
+
'version',
|
|
241
|
+
'env',
|
|
242
|
+
])
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Returns whether a backend extra tag key must not be set on the span.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} key
|
|
248
|
+
*/
|
|
249
|
+
function isReservedExtraTag (key) {
|
|
250
|
+
// _dd is the tracer's internal namespace (_dd.*, _dd1.*): the span formatter writes sampling,
|
|
251
|
+
// top-level and propagation keys there and a span tag with the same key would overwrite them.
|
|
252
|
+
// This is not an AI Guard namespace filter; the backend owns ai_guard.* keys.
|
|
253
|
+
return key.startsWith('_dd') || RESERVED_EXTRA_TAGS.has(key)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const RESERVED_EXTRA_META_STRUCT_KEYS = new Set(['messages', 'attack_categories', 'sds', 'tag_probs'])
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Copies backend-provided extra_tags to the span and extra_metastruct to the AI Guard meta-struct,
|
|
260
|
+
* skipping values and keys the span must not receive. Keys are used verbatim and entries are
|
|
261
|
+
* applied in order, so a repeated key keeps its last value.
|
|
262
|
+
*
|
|
263
|
+
* @param {Span} span
|
|
264
|
+
* @param {EvaluationMetaStruct} metaStruct
|
|
265
|
+
* @param {ExtraEntry[]} extraTags
|
|
266
|
+
* @param {ExtraEntry[]} extraMetastruct
|
|
267
|
+
*/
|
|
268
|
+
function applyExtraSpanData (span, metaStruct, extraTags, extraMetastruct) {
|
|
269
|
+
// The client decodes values with JSON.parse, so a string check and a null check cover every JSON type.
|
|
270
|
+
for (const { key, value } of extraTags) {
|
|
271
|
+
if (typeof value !== 'string') {
|
|
272
|
+
log.debug('AI Guard: ignoring extra_tags entry %s with non-string value', key)
|
|
273
|
+
} else if (isReservedExtraTag(key)) {
|
|
274
|
+
log.debug('AI Guard: ignoring reserved extra tag %s', key)
|
|
275
|
+
} else {
|
|
276
|
+
span.setTag(key, value)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
for (const { key, value } of extraMetastruct) {
|
|
281
|
+
if (value === undefined || value === null) {
|
|
282
|
+
log.debug('AI Guard: ignoring extra_metastruct entry %s with null value', key)
|
|
283
|
+
} else if (RESERVED_EXTRA_META_STRUCT_KEYS.has(key)) {
|
|
284
|
+
log.debug('AI Guard: ignoring reserved extra_metastruct key %s', key)
|
|
285
|
+
} else {
|
|
286
|
+
metaStruct[key] = value
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
150
291
|
class EvaluationReporter {
|
|
151
292
|
#config
|
|
152
293
|
#maxMessagesLength
|
|
@@ -230,6 +371,7 @@ class EvaluationReporter {
|
|
|
230
371
|
if (result.tags.length > 0) report.metaStruct.attack_categories = result.tags
|
|
231
372
|
if (result.sds.length > 0) report.metaStruct.sds = result.sds
|
|
232
373
|
if (outcome.hasTagProbabilities) report.metaStruct.tag_probs = result.tagProbabilities
|
|
374
|
+
applyExtraSpanData(report.span, report.metaStruct, outcome.extraTags, outcome.extraMetastruct)
|
|
233
375
|
|
|
234
376
|
if (redaction.enabled) {
|
|
235
377
|
report.span.setTag(TAGS.REDACTED_TAG_KEY, redaction.applied ? 'true' : 'false')
|
|
@@ -73,8 +73,7 @@ function onModelIntercept (ctx) {
|
|
|
73
73
|
null,
|
|
74
74
|
'AIGuard: unable to decode the model result: %s'
|
|
75
75
|
)
|
|
76
|
-
if (outputMessages === null) return result
|
|
77
|
-
if (!outputMessages.length) return result
|
|
76
|
+
if (outputMessages === null || !outputMessages.length) return result
|
|
78
77
|
|
|
79
78
|
return evaluate(ctx, aiguard, [outputMessages], opts).then(() => result)
|
|
80
79
|
}
|
|
@@ -112,8 +111,7 @@ function interceptStreamedResult (ctx, result, inputMessages) {
|
|
|
112
111
|
null,
|
|
113
112
|
'AIGuard: unable to decode the streamed model result: %s'
|
|
114
113
|
)
|
|
115
|
-
if (outputMessages === null) return replayed
|
|
116
|
-
if (!outputMessages.length) return replayed
|
|
114
|
+
if (outputMessages === null || !outputMessages.length) return replayed
|
|
117
115
|
|
|
118
116
|
return evaluate(ctx, aiguard, [outputMessages], opts).then(() => replayed)
|
|
119
117
|
})
|
|
@@ -10,8 +10,7 @@ const IMAGE_FALLBACK = '[image]'
|
|
|
10
10
|
* @returns {string|undefined|null}
|
|
11
11
|
*/
|
|
12
12
|
function stringifyIfNeeded (value) {
|
|
13
|
-
if (value == null) return value
|
|
14
|
-
if (typeof value === 'string') return value
|
|
13
|
+
if (value == null || typeof value === 'string') return value
|
|
15
14
|
|
|
16
15
|
try {
|
|
17
16
|
return JSON.stringify(value)
|
|
@@ -102,8 +102,14 @@ function resolveWritableString (root, path) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
if (
|
|
106
|
-
|
|
105
|
+
if (
|
|
106
|
+
!node ||
|
|
107
|
+
typeof node !== 'object' ||
|
|
108
|
+
!Object.hasOwn(node, terminal.name) ||
|
|
109
|
+
typeof node[terminal.name] !== 'string'
|
|
110
|
+
) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
107
113
|
return { container: node, key: terminal.name }
|
|
108
114
|
}
|
|
109
115
|
|
|
@@ -383,8 +383,9 @@ function presenceBitmask (optionalGroups, present) {
|
|
|
383
383
|
* @returns {string|null}
|
|
384
384
|
*/
|
|
385
385
|
function normalizeRouteExpress (route, params, urlPath, parse, makeMatcher) {
|
|
386
|
-
if (typeof route !== 'string' || !route)
|
|
387
|
-
|
|
386
|
+
if (typeof route !== 'string' || !route || typeof parse !== 'function' || typeof makeMatcher !== 'function') {
|
|
387
|
+
return null
|
|
388
|
+
}
|
|
388
389
|
|
|
389
390
|
let entry = routeCache.get(route)
|
|
390
391
|
if (entry === undefined) {
|