dd-trace 6.9.0 → 6.11.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 +0 -17
- package/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +150 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +14 -10
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +26 -18
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +606 -40
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +14 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +24 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +14 -3
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +16 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +5 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
- package/packages/dd-trace/src/plugins/util/test.js +93 -14
- package/packages/dd-trace/src/plugins/util/web.js +56 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
|
+
const { writeDatadogParentId, writeDatadogTraceId } = require('../../dd-trace/src/carrier')
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
TEST_STATUS,
|
|
@@ -13,7 +14,6 @@ const {
|
|
|
13
14
|
getTestSuiteCommonTags,
|
|
14
15
|
getTestLevelsMetadataTags,
|
|
15
16
|
getTestSessionName,
|
|
16
|
-
getIsFaultyEarlyFlakeDetection,
|
|
17
17
|
TEST_SOURCE_FILE,
|
|
18
18
|
TEST_IS_RETRY,
|
|
19
19
|
TEST_CODE_OWNERS,
|
|
@@ -107,19 +107,6 @@ class VitestPlugin extends CiPlugin {
|
|
|
107
107
|
})
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
this.addSub('ci:vitest:is-early-flake-detection-faulty', ({
|
|
111
|
-
knownTests,
|
|
112
|
-
testFilepaths,
|
|
113
|
-
onDone,
|
|
114
|
-
}) => {
|
|
115
|
-
const isFaulty = getIsFaultyEarlyFlakeDetection(
|
|
116
|
-
testFilepaths.map(testFilepath => getTestSuitePath(testFilepath, this.repositoryRoot)),
|
|
117
|
-
knownTests,
|
|
118
|
-
this.libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
119
|
-
)
|
|
120
|
-
onDone(isFaulty)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
110
|
this.addBind('ci:vitest:test:start', (ctx) => {
|
|
124
111
|
const {
|
|
125
112
|
testName,
|
|
@@ -424,12 +411,13 @@ class VitestPlugin extends CiPlugin {
|
|
|
424
411
|
this._setRepositoryRoot(repositoryRoot, codeOwnersEntries)
|
|
425
412
|
this.command = testCommand
|
|
426
413
|
this.frameworkVersion = frameworkVersion
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
414
|
+
let testSessionSpanContext
|
|
415
|
+
if (testSessionId && testModuleId) {
|
|
416
|
+
const carrier = /** @type {Record<string, unknown>} */ ({})
|
|
417
|
+
writeDatadogTraceId(carrier, testSessionId)
|
|
418
|
+
writeDatadogParentId(carrier, testModuleId)
|
|
419
|
+
testSessionSpanContext = this.tracer.extract('text_map', carrier)
|
|
420
|
+
}
|
|
433
421
|
|
|
434
422
|
const trimmedCommand = DD_MAJOR < 6 ? this.command : 'vitest run'
|
|
435
423
|
// test suites run in a different process, so they also need to init the metadata dictionary
|
|
@@ -535,6 +523,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
535
523
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: coverageLibrary })
|
|
536
524
|
this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeFiles.length)
|
|
537
525
|
}
|
|
526
|
+
this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
|
|
538
527
|
testSuiteSpan.finish()
|
|
539
528
|
finishAllTraceSpans(testSuiteSpan)
|
|
540
529
|
}
|
|
@@ -578,6 +567,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
578
567
|
this.addSub('ci:vitest:session:finish', ({
|
|
579
568
|
status,
|
|
580
569
|
error,
|
|
570
|
+
isTestSessionFinalizationError,
|
|
581
571
|
testCodeCoverageLinesTotal,
|
|
582
572
|
isEarlyFlakeDetectionEnabled,
|
|
583
573
|
isEarlyFlakeDetectionFaulty,
|
|
@@ -600,6 +590,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
600
590
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
601
591
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
602
592
|
if (error) {
|
|
593
|
+
if (isTestSessionFinalizationError) {
|
|
594
|
+
this.tracer._exporter.setDeferredTestSuiteError?.(error)
|
|
595
|
+
}
|
|
603
596
|
this.testModuleSpan.setTag('error', error)
|
|
604
597
|
this.testSessionSpan.setTag('error', error)
|
|
605
598
|
}
|
|
@@ -629,6 +622,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
629
622
|
if (vitestPool) {
|
|
630
623
|
this.testSessionSpan.setTag(VITEST_POOL, vitestPool)
|
|
631
624
|
}
|
|
625
|
+
this.tracer._exporter.exportDeferredTestSuiteSpans?.()
|
|
632
626
|
this.testModuleSpan.finish()
|
|
633
627
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
634
628
|
this.testSessionSpan.finish()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { readDatadogTraceId, readTraceparent } = require('../../dd-trace/src/carrier')
|
|
3
4
|
const DatadogSpanContext = require('../../dd-trace/src/opentracing/span_context')
|
|
4
5
|
|
|
5
6
|
const TRACE_ID_UPPER_TAG = '_dd.p.tid'
|
|
@@ -38,7 +39,7 @@ function createWebSocketSpanContext (spanContext) {
|
|
|
38
39
|
* @returns {boolean}
|
|
39
40
|
*/
|
|
40
41
|
function hasTraceHeaders (headers) {
|
|
41
|
-
return !!(headers && (headers
|
|
42
|
+
return !!(headers && (readDatadogTraceId(headers) || readTraceparent(headers)))
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -6,7 +6,6 @@ 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 { matchesOptionalPeerFile } = require('../datadog-instrumentations/src/helpers/optional-peer-bundler')
|
|
10
9
|
const { isESMFile } = require('../datadog-esbuild/src/utils')
|
|
11
10
|
const log = require('./src/log')
|
|
12
11
|
|
|
@@ -137,16 +136,6 @@ class DatadogWebpackPlugin {
|
|
|
137
136
|
|
|
138
137
|
const normalizedResource = resource.replaceAll('\\', '/')
|
|
139
138
|
|
|
140
|
-
// Rewrite optional-peer loads so installed peers get bundled and survive relocation
|
|
141
|
-
// (#8980); absent peers stay opaque, so a build that does not opt into the feature does
|
|
142
|
-
// not follow their dependency chain (#8635).
|
|
143
|
-
if (matchesOptionalPeerFile(normalizedResource)) {
|
|
144
|
-
createData.loaders ||= []
|
|
145
|
-
createData.loaders.push({ loader: require.resolve('./src/optional-peer-loader') })
|
|
146
|
-
log.debug('INLINE: optional-peer loader applied to %s', normalizedResource)
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
139
|
if (!resource.includes('node_modules')) {
|
|
151
140
|
return
|
|
152
141
|
}
|
|
@@ -17,8 +17,10 @@ module.exports = {
|
|
|
17
17
|
* Fetches agent information from the /info endpoint
|
|
18
18
|
* @param {URL} url - The agent URL
|
|
19
19
|
* @param {Function} callback - Callback function with signature (err, agentInfo)
|
|
20
|
+
* @param {{ deadline?: number, signal?: AbortSignal }} [options] - Request finalization options
|
|
21
|
+
* @param {Function} [makeRequest] - Request implementation
|
|
20
22
|
*/
|
|
21
|
-
function fetchAgentInfo (url, callback) {
|
|
23
|
+
function fetchAgentInfo (url, callback, options = {}, makeRequest = request) {
|
|
22
24
|
const urlKey = url.href
|
|
23
25
|
|
|
24
26
|
if (cachedUrl !== null && cachedUrl !== urlKey) {
|
|
@@ -29,10 +31,9 @@ function fetchAgentInfo (url, callback) {
|
|
|
29
31
|
return process.nextTick(callback, null, cachedData)
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}, (err, res) => {
|
|
34
|
+
options.path = '/info'
|
|
35
|
+
options.url = url
|
|
36
|
+
makeRequest('', options, (err, res) => {
|
|
36
37
|
if (err) {
|
|
37
38
|
return callback(err)
|
|
38
39
|
}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const tracerVersion = require('../../../../package.json').version
|
|
4
|
+
const { AIGuardClientError } = require('./errors')
|
|
5
|
+
const { parseEvaluationResponse } = require('./evaluation')
|
|
6
|
+
const TAGS = require('./tags')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the AI Guard host for a Datadog site.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} site
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function aiGuardHost (site) {
|
|
15
|
+
return site.split('.').length === 2 ? `app.${site}` : site
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Sends a request to the AI Guard service.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} body
|
|
22
|
+
* @param {{ url: string, headers: Record<string, string>, timeout: number }} opts
|
|
23
|
+
* @returns {Promise<{ status: number, body: unknown }>}
|
|
24
|
+
*/
|
|
3
25
|
async function executeRequest (body, opts) {
|
|
4
26
|
const postData = JSON.stringify(body)
|
|
5
27
|
const headers = {
|
|
@@ -22,4 +44,81 @@ async function executeRequest (body, opts) {
|
|
|
22
44
|
}
|
|
23
45
|
}
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
class AIGuardClient {
|
|
48
|
+
#headers
|
|
49
|
+
#evaluateUrl
|
|
50
|
+
#timeout
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {import('../config/config-base')} config
|
|
54
|
+
*/
|
|
55
|
+
constructor (config) {
|
|
56
|
+
this.#headers = {
|
|
57
|
+
'DD-API-KEY': config.DD_API_KEY,
|
|
58
|
+
'DD-APPLICATION-KEY': config.DD_APP_KEY,
|
|
59
|
+
'DD-AI-GUARD-VERSION': tracerVersion,
|
|
60
|
+
'DD-AI-GUARD-SOURCE': 'SDK',
|
|
61
|
+
'DD-AI-GUARD-LANGUAGE': 'nodejs',
|
|
62
|
+
}
|
|
63
|
+
const endpoint = config.experimental.aiguard.endpoint || `https://${aiGuardHost(config.site)}/api/v2/ai-guard`
|
|
64
|
+
this.#evaluateUrl = `${endpoint}/evaluate`
|
|
65
|
+
this.#timeout = config.experimental.aiguard.timeout
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Evaluates messages and converts the service response to the internal evaluation contract.
|
|
70
|
+
*
|
|
71
|
+
* @param {import('../../../../index').aiguard.Message[]} messages
|
|
72
|
+
* @param {{ service: string, env: string }} meta
|
|
73
|
+
* @returns {Promise<NonNullable<ReturnType<typeof parseEvaluationResponse>>>}
|
|
74
|
+
*/
|
|
75
|
+
evaluate (messages, meta) {
|
|
76
|
+
const payload = {
|
|
77
|
+
data: {
|
|
78
|
+
attributes: {
|
|
79
|
+
messages,
|
|
80
|
+
meta,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
return executeRequest(
|
|
85
|
+
payload,
|
|
86
|
+
{ url: this.#evaluateUrl, headers: this.#headers, timeout: this.#timeout }
|
|
87
|
+
)
|
|
88
|
+
.then(response => this.#parseResponse(response))
|
|
89
|
+
.catch(cause => {
|
|
90
|
+
if (cause instanceof AIGuardClientError) throw cause
|
|
91
|
+
|
|
92
|
+
throw new AIGuardClientError(`Unexpected error calling AI Guard service: ${cause.message}`, {
|
|
93
|
+
cause,
|
|
94
|
+
telemetryType: TAGS.ERROR_TYPE_CLIENT,
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validates an AI Guard HTTP response.
|
|
101
|
+
*
|
|
102
|
+
* @param {{ status: number, body: unknown }} response
|
|
103
|
+
* @returns {NonNullable<ReturnType<typeof parseEvaluationResponse>>}
|
|
104
|
+
*/
|
|
105
|
+
#parseResponse (response) {
|
|
106
|
+
if (response.status !== 200) {
|
|
107
|
+
throw new AIGuardClientError(`AI Guard service call failed, status ${response.status}`, {
|
|
108
|
+
errors: response.body?.errors,
|
|
109
|
+
telemetryType: TAGS.ERROR_TYPE_STATUS,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const evaluation = parseEvaluationResponse(response.body)
|
|
114
|
+
if (!evaluation) {
|
|
115
|
+
throw new AIGuardClientError(`AI Guard service returned unexpected response : ${response.body}`, {
|
|
116
|
+
telemetryType: TAGS.ERROR_TYPE_RESPONSE,
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return evaluation
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = AIGuardClient
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
class AIGuardAbortError extends Error {
|
|
4
|
+
/**
|
|
5
|
+
* @param {string|undefined} reason
|
|
6
|
+
* @param {Array<unknown>} tags
|
|
7
|
+
* @param {Record<string, unknown>} tagProbabilities
|
|
8
|
+
* @param {Array<unknown>} sds
|
|
9
|
+
*/
|
|
10
|
+
constructor (reason, tags, tagProbabilities, sds) {
|
|
11
|
+
super(reason)
|
|
12
|
+
this.name = 'AIGuardAbortError'
|
|
13
|
+
this.reason = reason
|
|
14
|
+
this.tags = tags
|
|
15
|
+
this.tagProbabilities = tagProbabilities
|
|
16
|
+
this.sds = sds || []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class AIGuardClientError extends Error {
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} message
|
|
23
|
+
* @param {{ telemetryType: string, errors?: Array<unknown>, cause?: Error }} opts
|
|
24
|
+
*/
|
|
25
|
+
constructor (message, opts) {
|
|
26
|
+
super(message)
|
|
27
|
+
this.name = 'AIGuardClientError'
|
|
28
|
+
this.telemetryType = opts.telemetryType
|
|
29
|
+
if (opts.errors) {
|
|
30
|
+
this.errors = opts.errors
|
|
31
|
+
}
|
|
32
|
+
if (opts.cause) {
|
|
33
|
+
this.cause = opts.cause
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
AIGuardAbortError,
|
|
40
|
+
AIGuardClientError,
|
|
41
|
+
}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { HTTP_CLIENT_IP, HTTP_USERAGENT, NETWORK_CLIENT_IP } = require('../../../../ext/tags')
|
|
4
|
+
const clone = require('../../../../vendor/dist/rfdc')({ proto: false, circles: false })
|
|
5
|
+
const { USER_ID, USER_SESSION_ID } = require('../appsec/addresses')
|
|
6
|
+
const { getActiveRequest } = require('../appsec/store')
|
|
7
|
+
const { keepTrace } = require('../priority_sampler')
|
|
8
|
+
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
9
|
+
const { AI_GUARD } = require('../standalone/product')
|
|
10
|
+
const telemetryMetrics = require('../telemetry/metrics')
|
|
11
|
+
const TAGS = require('./tags')
|
|
12
|
+
|
|
13
|
+
const ALLOW = 'ALLOW'
|
|
14
|
+
|
|
15
|
+
/** @typedef {import('../../../../index').aiguard.Message} Message */
|
|
16
|
+
/** @typedef {import('../opentracing/span')} Span */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} EvaluationResponse
|
|
20
|
+
* @property {string} action
|
|
21
|
+
* @property {string|undefined} reason
|
|
22
|
+
* @property {Array<unknown>} tags
|
|
23
|
+
* @property {Array<unknown>} sdsFindings
|
|
24
|
+
* @property {Record<string, unknown>} tagProbabilities
|
|
25
|
+
* @property {boolean} hasTagProbabilities
|
|
26
|
+
* @property {boolean} blockingEnabled
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} EvaluationOutcome
|
|
31
|
+
* @property {{ action: string, reason: string|undefined, tags: Array<unknown>,
|
|
32
|
+
* tagProbabilities: Record<string, unknown>, sds: Array<unknown> }} result
|
|
33
|
+
* @property {boolean} shouldBlock
|
|
34
|
+
* @property {boolean} hasTagProbabilities
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {object} EvaluationMetaStruct
|
|
39
|
+
* @property {Message[]} messages
|
|
40
|
+
* @property {Array<unknown>} [attack_categories]
|
|
41
|
+
* @property {Array<unknown>} [sds]
|
|
42
|
+
* @property {Record<string, unknown>} [tag_probs]
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {object} EvaluationReport
|
|
47
|
+
* @property {Span} span
|
|
48
|
+
* @property {EvaluationMetaStruct} metaStruct
|
|
49
|
+
* @property {{ source: string, integration: string }} telemetryTags
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns whether a value is a non-array object.
|
|
54
|
+
*
|
|
55
|
+
* @param {unknown} value
|
|
56
|
+
* @returns {value is Record<string, unknown>}
|
|
57
|
+
*/
|
|
58
|
+
function isRecord (value) {
|
|
59
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parses the backend evaluation response into the tracer's internal contract.
|
|
64
|
+
*
|
|
65
|
+
* @param {unknown} body
|
|
66
|
+
* @returns {EvaluationResponse|undefined}
|
|
67
|
+
*/
|
|
68
|
+
function parseEvaluationResponse (body) {
|
|
69
|
+
if (!isRecord(body) || !isRecord(body.data) || !isRecord(body.data.attributes)) return
|
|
70
|
+
|
|
71
|
+
const attributes = body.data.attributes
|
|
72
|
+
if (typeof attributes.action !== 'string' || attributes.action.length === 0) return
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
action: attributes.action,
|
|
76
|
+
reason: typeof attributes.reason === 'string' ? attributes.reason : undefined,
|
|
77
|
+
tags: Array.isArray(attributes.tags) ? attributes.tags : [],
|
|
78
|
+
sdsFindings: Array.isArray(attributes.sds_findings) ? attributes.sds_findings : [],
|
|
79
|
+
tagProbabilities: isRecord(attributes.tag_probs) ? attributes.tag_probs : {},
|
|
80
|
+
hasTagProbabilities: isRecord(attributes.tag_probs),
|
|
81
|
+
blockingEnabled: attributes.is_blocking_enabled === true,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Determines whether the current evaluation must abort the guarded operation.
|
|
87
|
+
*
|
|
88
|
+
* @param {boolean} block
|
|
89
|
+
* @param {{ action: string, blockingEnabled: boolean }} evaluation
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
function shouldBlockEvaluation (block, evaluation) {
|
|
93
|
+
return block && evaluation.blockingEnabled && evaluation.action !== ALLOW
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Applies local evaluation policy and creates the result returned to the caller.
|
|
98
|
+
*
|
|
99
|
+
* @param {EvaluationResponse} evaluation
|
|
100
|
+
* @param {boolean} block
|
|
101
|
+
* @returns {EvaluationOutcome}
|
|
102
|
+
*/
|
|
103
|
+
function createEvaluationOutcome (evaluation, block) {
|
|
104
|
+
return {
|
|
105
|
+
result: {
|
|
106
|
+
action: evaluation.action,
|
|
107
|
+
reason: evaluation.reason,
|
|
108
|
+
tags: evaluation.tags,
|
|
109
|
+
tagProbabilities: evaluation.tagProbabilities,
|
|
110
|
+
sds: evaluation.sdsFindings,
|
|
111
|
+
},
|
|
112
|
+
shouldBlock: shouldBlockEvaluation(block, evaluation),
|
|
113
|
+
hasTagProbabilities: evaluation.hasTagProbabilities,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const aiguardMetrics = telemetryMetrics.manager.namespace('ai_guard')
|
|
118
|
+
|
|
119
|
+
// Tags from the service entry span that must be mirrored onto every AI Guard span
|
|
120
|
+
// so anomaly detection pipelines can process each span independently.
|
|
121
|
+
const SERVICE_ENTRY_TAG_MAPPINGS = [
|
|
122
|
+
[HTTP_USERAGENT, TAGS.HTTP_USERAGENT_TAG_KEY],
|
|
123
|
+
[HTTP_CLIENT_IP, TAGS.HTTP_CLIENT_IP_TAG_KEY],
|
|
124
|
+
[NETWORK_CLIENT_IP, TAGS.NETWORK_CLIENT_IP_TAG_KEY],
|
|
125
|
+
[USER_ID, TAGS.USR_ID_TAG_KEY],
|
|
126
|
+
[USER_SESSION_ID, TAGS.USR_SESSION_ID_TAG_KEY],
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
class EvaluationReporter {
|
|
130
|
+
#config
|
|
131
|
+
#maxMessagesLength
|
|
132
|
+
#maxContentSize
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {import('../config/config-base')} config
|
|
136
|
+
*/
|
|
137
|
+
constructor (config) {
|
|
138
|
+
this.#config = config
|
|
139
|
+
this.#maxMessagesLength = config.experimental.aiguard.maxMessagesLength
|
|
140
|
+
this.#maxContentSize = config.experimental.aiguard.maxContentSize
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Initializes span and trace reporting before the AI Guard request starts.
|
|
145
|
+
*
|
|
146
|
+
* @param {Span} span
|
|
147
|
+
* @param {Message[]} messages
|
|
148
|
+
* @param {{ source: string, integration: string }} options
|
|
149
|
+
* @returns {EvaluationReport}
|
|
150
|
+
*/
|
|
151
|
+
start (span, messages, options) {
|
|
152
|
+
const telemetryTags = { source: options.source, integration: options.integration }
|
|
153
|
+
const last = messages.at(-1)
|
|
154
|
+
const target = this.#isToolCall(last) ? 'tool' : 'prompt'
|
|
155
|
+
span.setTag(TAGS.TARGET_TAG_KEY, target)
|
|
156
|
+
if (target === 'tool') {
|
|
157
|
+
const name = this.#getToolName(last, messages)
|
|
158
|
+
if (name) {
|
|
159
|
+
span.setTag(TAGS.TOOL_NAME_TAG_KEY, name)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const metaStruct = {
|
|
164
|
+
messages: this.#buildMessagesForMetaStruct(messages, telemetryTags),
|
|
165
|
+
}
|
|
166
|
+
span.meta_struct = {
|
|
167
|
+
[TAGS.META_STRUCT_KEY]: metaStruct,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const rootSpan = span.context()?._trace?.started?.[0]
|
|
171
|
+
if (rootSpan) {
|
|
172
|
+
this.#setRootSpanClientIpTags(rootSpan)
|
|
173
|
+
this.#copyServiceEntryTagsToGuardSpan(span, rootSpan)
|
|
174
|
+
// This must happen before the request so its sampling decision reaches outgoing HTTP spans.
|
|
175
|
+
keepTrace(rootSpan, AI_GUARD)
|
|
176
|
+
rootSpan.setTag(TAGS.EVENT_TAG_KEY, 'true')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
span,
|
|
181
|
+
metaStruct,
|
|
182
|
+
telemetryTags,
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Reports a failed evaluation request.
|
|
188
|
+
*
|
|
189
|
+
* @param {EvaluationReport} report
|
|
190
|
+
* @param {string} errorType
|
|
191
|
+
* @returns {void}
|
|
192
|
+
*/
|
|
193
|
+
fail (report, errorType) {
|
|
194
|
+
aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, { error: true, ...report.telemetryTags }).inc(1)
|
|
195
|
+
aiguardMetrics.count(TAGS.TELEMETRY_ERROR, { type: errorType, ...report.telemetryTags }).inc(1)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Reports a successful evaluation and its local policy outcome.
|
|
200
|
+
*
|
|
201
|
+
* @param {EvaluationReport} report
|
|
202
|
+
* @param {EvaluationOutcome} outcome
|
|
203
|
+
* @returns {void}
|
|
204
|
+
*/
|
|
205
|
+
finish (report, outcome) {
|
|
206
|
+
const { result, shouldBlock } = outcome
|
|
207
|
+
|
|
208
|
+
if (result.tags.length > 0) report.metaStruct.attack_categories = result.tags
|
|
209
|
+
if (result.sds.length > 0) report.metaStruct.sds = result.sds
|
|
210
|
+
if (outcome.hasTagProbabilities) report.metaStruct.tag_probs = result.tagProbabilities
|
|
211
|
+
|
|
212
|
+
const requestTelemetryTags = {
|
|
213
|
+
action: result.action,
|
|
214
|
+
error: false,
|
|
215
|
+
block: shouldBlock,
|
|
216
|
+
...report.telemetryTags,
|
|
217
|
+
}
|
|
218
|
+
aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, requestTelemetryTags).inc(1)
|
|
219
|
+
|
|
220
|
+
report.span.setTag(TAGS.ACTION_TAG_KEY, result.action)
|
|
221
|
+
if (result.reason) {
|
|
222
|
+
report.span.setTag(TAGS.REASON_TAG_KEY, result.reason)
|
|
223
|
+
}
|
|
224
|
+
if (shouldBlock) {
|
|
225
|
+
report.span.setTag(TAGS.BLOCKED_TAG_KEY, 'true')
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Returns a safe, size-limited message copy for the span meta-struct.
|
|
231
|
+
*
|
|
232
|
+
* @param {Message[]} messages
|
|
233
|
+
* @param {{ source: string, integration: string }} telemetryTags
|
|
234
|
+
* @returns {Message[]}
|
|
235
|
+
*/
|
|
236
|
+
#buildMessagesForMetaStruct (messages, telemetryTags) {
|
|
237
|
+
const size = Math.min(messages.length, this.#maxMessagesLength)
|
|
238
|
+
if (messages.length > size) {
|
|
239
|
+
aiguardMetrics.count(TAGS.TELEMETRY_TRUNCATED, { type: 'messages', ...telemetryTags }).inc(1)
|
|
240
|
+
}
|
|
241
|
+
const result = []
|
|
242
|
+
let contentTruncated = false
|
|
243
|
+
for (let i = messages.length - size; i < messages.length; i++) {
|
|
244
|
+
const message = clone(messages[i])
|
|
245
|
+
if (message.content?.length > this.#maxContentSize) {
|
|
246
|
+
contentTruncated = true
|
|
247
|
+
message.content = message.content.slice(0, this.#maxContentSize)
|
|
248
|
+
}
|
|
249
|
+
result.push(message)
|
|
250
|
+
}
|
|
251
|
+
if (contentTruncated) {
|
|
252
|
+
aiguardMetrics.count(TAGS.TELEMETRY_TRUNCATED, { type: 'content', ...telemetryTags }).inc(1)
|
|
253
|
+
}
|
|
254
|
+
return result
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Returns whether a message represents a tool call or tool output.
|
|
259
|
+
*
|
|
260
|
+
* @param {Message} message
|
|
261
|
+
* @returns {boolean}
|
|
262
|
+
*/
|
|
263
|
+
#isToolCall (message) {
|
|
264
|
+
return Boolean(message.tool_calls || message.tool_call_id)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Resolves the tool name associated with a tool call or output message.
|
|
269
|
+
*
|
|
270
|
+
* @param {Message} message
|
|
271
|
+
* @param {Message[]} history
|
|
272
|
+
* @returns {string|null}
|
|
273
|
+
*/
|
|
274
|
+
#getToolName (message, history) {
|
|
275
|
+
if (message.tool_calls) {
|
|
276
|
+
const names = message.tool_calls.map(tool => tool.function.name)
|
|
277
|
+
return names.length === 0 ? null : names.join(',')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const id = message.tool_call_id
|
|
281
|
+
for (let i = history.length - 2; i >= 0; i--) {
|
|
282
|
+
const item = history[i]
|
|
283
|
+
if (item.tool_calls) {
|
|
284
|
+
for (const toolCall of item.tool_calls) {
|
|
285
|
+
if (toolCall.id === id) {
|
|
286
|
+
return toolCall.function.name
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return null
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Adds missing client IP tags to the service entry span.
|
|
296
|
+
*
|
|
297
|
+
* @param {Span} rootSpan
|
|
298
|
+
* @returns {void}
|
|
299
|
+
*/
|
|
300
|
+
#setRootSpanClientIpTags (rootSpan) {
|
|
301
|
+
const currentTags = rootSpan.context().getTags()
|
|
302
|
+
const needsHttpClientIp = !Object.hasOwn(currentTags, HTTP_CLIENT_IP)
|
|
303
|
+
const needsNetworkClientIp = !Object.hasOwn(currentTags, NETWORK_CLIENT_IP)
|
|
304
|
+
|
|
305
|
+
if (!needsHttpClientIp && !needsNetworkClientIp) return
|
|
306
|
+
|
|
307
|
+
const request = getActiveRequest()
|
|
308
|
+
if (!request) return
|
|
309
|
+
|
|
310
|
+
const newTags = {}
|
|
311
|
+
let hasNewTags = false
|
|
312
|
+
|
|
313
|
+
if (needsHttpClientIp) {
|
|
314
|
+
const clientIp = extractIp(this.#config, request)
|
|
315
|
+
if (clientIp) {
|
|
316
|
+
newTags[HTTP_CLIENT_IP] = clientIp
|
|
317
|
+
hasNewTags = true
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (needsNetworkClientIp) {
|
|
322
|
+
const networkClientIp = request.socket?.remoteAddress
|
|
323
|
+
if (networkClientIp) {
|
|
324
|
+
newTags[NETWORK_CLIENT_IP] = networkClientIp
|
|
325
|
+
hasNewTags = true
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (hasNewTags) {
|
|
330
|
+
rootSpan.addTags(newTags)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Copies service entry tags required by anomaly detection to the AI Guard span.
|
|
336
|
+
*
|
|
337
|
+
* @param {Span} guardSpan
|
|
338
|
+
* @param {Span} rootSpan
|
|
339
|
+
* @returns {void}
|
|
340
|
+
*/
|
|
341
|
+
#copyServiceEntryTagsToGuardSpan (guardSpan, rootSpan) {
|
|
342
|
+
const rootTags = rootSpan.context().getTags()
|
|
343
|
+
for (const [sourceTag, destinationTag] of SERVICE_ENTRY_TAG_MAPPINGS) {
|
|
344
|
+
const value = rootTags[sourceTag]
|
|
345
|
+
if (value !== undefined && value !== null) {
|
|
346
|
+
guardSpan.setTag(destinationTag, value)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
module.exports = {
|
|
353
|
+
createEvaluationOutcome,
|
|
354
|
+
EvaluationReporter,
|
|
355
|
+
parseEvaluationResponse,
|
|
356
|
+
shouldBlockEvaluation,
|
|
357
|
+
}
|