dd-trace 6.12.0 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/index.d.ts +127 -26
- package/package.json +22 -19
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +165 -38
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +13 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +137 -27
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +176 -28
- package/packages/datadog-plugin-undici/src/index.js +29 -56
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/index.js +75 -14
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +47 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +532 -71
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +24 -3
- package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +31 -7
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +105 -0
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +111 -48
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +157 -41
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +6 -74
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +11 -5
- package/packages/dd-trace/src/profiling/profiler.js +30 -13
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +108 -10
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
- package/packages/dd-trace/src/serverless/vercel.js +155 -0
- package/packages/dd-trace/src/serverless.js +45 -21
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +64 -31
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +26 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- 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/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
- package/packages/dd-trace/src/encode/agentless-json.js +0 -209
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../../../../index').aiguard.RedactionReplacement} RedactionReplacement */
|
|
4
|
+
|
|
5
|
+
const SEGMENT_PATTERN = /^([A-Za-z0-9_]+)(?:\[([0-9]+)\])?$/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts the raw backend replacements into the public, typed contract.
|
|
9
|
+
*
|
|
10
|
+
* @param {unknown} replacements
|
|
11
|
+
* @returns {RedactionReplacement[]}
|
|
12
|
+
*/
|
|
13
|
+
function normalizeRedactionReplacements (replacements) {
|
|
14
|
+
if (!Array.isArray(replacements)) return []
|
|
15
|
+
|
|
16
|
+
const result = []
|
|
17
|
+
for (const entry of replacements) {
|
|
18
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue
|
|
19
|
+
|
|
20
|
+
const { path, replacement } = entry
|
|
21
|
+
if (typeof path !== 'string' || path.length === 0 || typeof replacement !== 'string') continue
|
|
22
|
+
|
|
23
|
+
result.push({ path, replacement })
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parses an AI Guard location path into named segments and optional indexes.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} path
|
|
32
|
+
* @returns {Array<{ name: string, index: number|undefined }>|undefined}
|
|
33
|
+
*/
|
|
34
|
+
function parsePath (path) {
|
|
35
|
+
const segments = []
|
|
36
|
+
for (const rawSegment of path.split('.')) {
|
|
37
|
+
const match = SEGMENT_PATTERN.exec(rawSegment)
|
|
38
|
+
if (!match) return
|
|
39
|
+
|
|
40
|
+
segments.push({
|
|
41
|
+
name: match[1],
|
|
42
|
+
index: match[2] === undefined ? undefined : Number(match[2]),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return segments
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reports whether a parsed path targets an allowed message string.
|
|
50
|
+
*
|
|
51
|
+
* @param {Array<{ name: string, index: number|undefined }>} segments
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function isRedactablePath (segments) {
|
|
55
|
+
const first = segments[0]
|
|
56
|
+
if (first?.name !== 'messages' || first.index === undefined) return false
|
|
57
|
+
|
|
58
|
+
if (segments.length === 2) {
|
|
59
|
+
const content = segments[1]
|
|
60
|
+
return content.name === 'content' && content.index === undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (segments.length === 3) {
|
|
64
|
+
const content = segments[1]
|
|
65
|
+
const text = segments[2]
|
|
66
|
+
return content.name === 'content' && content.index !== undefined &&
|
|
67
|
+
text.name === 'text' && text.index === undefined
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (segments.length === 4) {
|
|
71
|
+
const toolCalls = segments[1]
|
|
72
|
+
const functionSegment = segments[2]
|
|
73
|
+
const argumentsSegment = segments[3]
|
|
74
|
+
return toolCalls.name === 'tool_calls' && toolCalls.index !== undefined &&
|
|
75
|
+
functionSegment.name === 'function' && functionSegment.index === undefined &&
|
|
76
|
+
argumentsSegment.name === 'arguments' && argumentsSegment.index === undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolves a location path to a writable string container and key.
|
|
84
|
+
*
|
|
85
|
+
* @param {{ messages: Array<object> }} root
|
|
86
|
+
* @param {string} path
|
|
87
|
+
* @returns {{ container: object|Array<unknown>, key: string|number }|undefined}
|
|
88
|
+
*/
|
|
89
|
+
function resolveWritableString (root, path) {
|
|
90
|
+
const segments = parsePath(path)
|
|
91
|
+
if (!segments || !isRedactablePath(segments)) return
|
|
92
|
+
|
|
93
|
+
const terminal = segments.at(-1)
|
|
94
|
+
let node = root
|
|
95
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
96
|
+
const { name, index } = segments[i]
|
|
97
|
+
if (!node || typeof node !== 'object' || !Object.hasOwn(node, name)) return
|
|
98
|
+
|
|
99
|
+
node = node[name]
|
|
100
|
+
if (index !== undefined) {
|
|
101
|
+
if (!Array.isArray(node) || index >= node.length) return
|
|
102
|
+
node = node[index]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!node || typeof node !== 'object') return
|
|
107
|
+
if (!Object.hasOwn(node, terminal.name) || typeof node[terminal.name] !== 'string') return
|
|
108
|
+
return { container: node, key: terminal.name }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Applies redaction replacements to an AI Guard message list.
|
|
113
|
+
* The caller transfers ownership of a private snapshot. Successful replacements mutate that snapshot in place.
|
|
114
|
+
*
|
|
115
|
+
* @param {Array<object>} messages
|
|
116
|
+
* @param {unknown} replacements
|
|
117
|
+
* @returns {{ messages: Array<object>, redacted: boolean, failures: number }}
|
|
118
|
+
*/
|
|
119
|
+
function redactMessages (messages, replacements) {
|
|
120
|
+
if (!Array.isArray(messages) || replacements === undefined || replacements === null) {
|
|
121
|
+
return { messages, redacted: false, failures: 0 }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
if (!Array.isArray(replacements)) {
|
|
126
|
+
return { messages, redacted: false, failures: 1 }
|
|
127
|
+
}
|
|
128
|
+
if (replacements.length === 0) return { messages, redacted: false, failures: 0 }
|
|
129
|
+
|
|
130
|
+
const replacementsByPath = new Map()
|
|
131
|
+
const conflictingPaths = new Set()
|
|
132
|
+
let failures = 0
|
|
133
|
+
for (const entry of replacements) {
|
|
134
|
+
if (!entry || typeof entry !== 'object') {
|
|
135
|
+
failures++
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { path, replacement } = entry
|
|
140
|
+
if (typeof path !== 'string' || path.length === 0 || typeof replacement !== 'string') {
|
|
141
|
+
failures++
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (conflictingPaths.has(path)) continue
|
|
146
|
+
if (replacementsByPath.has(path) && replacementsByPath.get(path) !== replacement) {
|
|
147
|
+
replacementsByPath.delete(path)
|
|
148
|
+
conflictingPaths.add(path)
|
|
149
|
+
failures++
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
replacementsByPath.set(path, replacement)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (replacementsByPath.size === 0) return { messages, redacted: false, failures }
|
|
156
|
+
|
|
157
|
+
const root = { messages }
|
|
158
|
+
const targets = []
|
|
159
|
+
|
|
160
|
+
for (const [path, replacement] of replacementsByPath) {
|
|
161
|
+
const resolved = resolveWritableString(root, path)
|
|
162
|
+
if (!resolved) {
|
|
163
|
+
failures++
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
targets.push({ ...resolved, replacement })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const { container, key, replacement } of targets) {
|
|
171
|
+
container[key] = replacement
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { messages, redacted: targets.length > 0, failures }
|
|
175
|
+
} catch {
|
|
176
|
+
return { messages, redacted: false, failures: 1 }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = {
|
|
181
|
+
normalizeRedactionReplacements,
|
|
182
|
+
redactMessages,
|
|
183
|
+
}
|
|
@@ -12,6 +12,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
12
12
|
#tracer
|
|
13
13
|
#client
|
|
14
14
|
#reporter
|
|
15
|
+
#redactionEnabled
|
|
15
16
|
#meta
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -29,6 +30,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
29
30
|
this.#tracer = tracer
|
|
30
31
|
this.#client = new AIGuardClient(config)
|
|
31
32
|
this.#reporter = new EvaluationReporter(config)
|
|
33
|
+
this.#redactionEnabled = config.experimental.aiguard.redactionEnabled
|
|
32
34
|
this.#meta = { service: config.service, env: config.env }
|
|
33
35
|
this.#initialized = true
|
|
34
36
|
}
|
|
@@ -45,13 +47,16 @@ class AIGuard extends NoopAIGuard {
|
|
|
45
47
|
const report = this.#reporter.start(span, messages, { source, integration })
|
|
46
48
|
let evaluation
|
|
47
49
|
try {
|
|
48
|
-
evaluation = await this.#client.evaluate(messages, this.#meta)
|
|
50
|
+
evaluation = await this.#client.evaluate(report.messages, this.#meta)
|
|
49
51
|
} catch (error) {
|
|
50
52
|
this.#reporter.fail(report, error.telemetryType ?? TAGS.ERROR_TYPE_CLIENT)
|
|
51
53
|
throw error
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
const outcome = createEvaluationOutcome(evaluation,
|
|
56
|
+
const outcome = createEvaluationOutcome(report.messages, evaluation, {
|
|
57
|
+
block,
|
|
58
|
+
redactionEnabled: this.#redactionEnabled,
|
|
59
|
+
})
|
|
55
60
|
this.#reporter.finish(report, outcome)
|
|
56
61
|
|
|
57
62
|
if (outcome.shouldBlock) {
|
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
ACTION_TAG_KEY: 'ai_guard.action',
|
|
8
8
|
REASON_TAG_KEY: 'ai_guard.reason',
|
|
9
9
|
BLOCKED_TAG_KEY: 'ai_guard.blocked',
|
|
10
|
+
REDACTED_TAG_KEY: 'ai_guard.redacted',
|
|
10
11
|
EVENT_TAG_KEY: 'ai_guard.event',
|
|
11
12
|
META_STRUCT_KEY: 'ai_guard',
|
|
12
13
|
|
|
@@ -27,4 +28,5 @@ module.exports = {
|
|
|
27
28
|
ERROR_TYPE_CLIENT: 'client_error',
|
|
28
29
|
ERROR_TYPE_STATUS: 'bad_status',
|
|
29
30
|
ERROR_TYPE_RESPONSE: 'bad_response',
|
|
31
|
+
ERROR_TYPE_REDACTION: 'redaction_error',
|
|
30
32
|
}
|
|
@@ -5,6 +5,8 @@ const { isSchemaAttribute } = require('../reporter')
|
|
|
5
5
|
const appsecTelemetry = require('../telemetry')
|
|
6
6
|
const sampler = require('./sampler')
|
|
7
7
|
|
|
8
|
+
/** @typedef {import('../../opentracing/span')} DatadogSpan */
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Map a sampling decision into the corresponding API Security telemetry metric.
|
|
10
12
|
*
|
|
@@ -13,14 +15,14 @@ const sampler = require('./sampler')
|
|
|
13
15
|
* - MISSING_ROUTE: missing_route
|
|
14
16
|
* - SKIP: no metric emitted
|
|
15
17
|
*
|
|
16
|
-
* @param {
|
|
18
|
+
* @param {DatadogSpan} rootSpan Span the sampling decision was attached to
|
|
17
19
|
* @param {'sample' | 'missing_route' | 'skip'} samplingDecision Sampler decision
|
|
18
20
|
* @param {{ attributes?: Record<string, unknown> } | undefined} wafResult WAF run result
|
|
19
21
|
*/
|
|
20
|
-
function
|
|
22
|
+
function reportRootSpanRequest (rootSpan, samplingDecision, wafResult) {
|
|
21
23
|
switch (samplingDecision) {
|
|
22
24
|
case sampler.SamplingDecision.SAMPLE: {
|
|
23
|
-
const framework = getFramework(
|
|
25
|
+
const framework = getFramework(rootSpan)
|
|
24
26
|
if (hasSchemaAttributes(wafResult?.attributes)) {
|
|
25
27
|
appsecTelemetry.incrementApiSecRequestSchemaMetric(framework)
|
|
26
28
|
} else {
|
|
@@ -29,13 +31,26 @@ function reportRequest (req, samplingDecision, wafResult) {
|
|
|
29
31
|
break
|
|
30
32
|
}
|
|
31
33
|
case sampler.SamplingDecision.MISSING_ROUTE:
|
|
32
|
-
appsecTelemetry.incrementApiSecMissingRouteMetric(getFramework(
|
|
34
|
+
appsecTelemetry.incrementApiSecMissingRouteMetric(getFramework(rootSpan))
|
|
33
35
|
break
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Node HTTP adapter over {@link reportRootSpanRequest}.
|
|
41
|
+
*
|
|
42
|
+
* @param {import('http').IncomingMessage} req
|
|
43
|
+
* @param {'sample' | 'missing_route' | 'skip'} samplingDecision Sampler decision
|
|
44
|
+
* @param {{ attributes?: Record<string, unknown> } | undefined} wafResult WAF run result
|
|
45
|
+
*/
|
|
46
|
+
function reportRequest (req, samplingDecision, wafResult) {
|
|
47
|
+
if (samplingDecision === sampler.SamplingDecision.SKIP) return
|
|
48
|
+
|
|
49
|
+
reportRootSpanRequest(web.root(req), samplingDecision, wafResult)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getFramework (rootSpan) {
|
|
53
|
+
return rootSpan?.context?.()?.getTag?.('component')
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
function hasSchemaAttributes (attributes) {
|
|
@@ -50,6 +65,8 @@ module.exports = {
|
|
|
50
65
|
configure: sampler.configure,
|
|
51
66
|
disable: sampler.disable,
|
|
52
67
|
sampleRequest: sampler.sampleRequest,
|
|
68
|
+
sampleRootSpanRequest: sampler.sampleRootSpanRequest,
|
|
53
69
|
reportRequest,
|
|
70
|
+
reportRootSpanRequest,
|
|
54
71
|
SamplingDecision: sampler.SamplingDecision,
|
|
55
72
|
}
|
|
@@ -8,6 +8,8 @@ const { keepTrace } = require('../../priority_sampler')
|
|
|
8
8
|
const { ASM } = require('../../standalone/product')
|
|
9
9
|
const { isBlocked } = require('../blocking')
|
|
10
10
|
|
|
11
|
+
/** @typedef {import('../../opentracing/span')} DatadogSpan */
|
|
12
|
+
|
|
11
13
|
const MAX_SIZE = 4096
|
|
12
14
|
|
|
13
15
|
const SamplingDecision = Object.freeze({
|
|
@@ -44,77 +46,113 @@ function disable () {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {
|
|
49
|
+
* @param {DatadogSpan} rootSpan Span the sampling decision is attached to
|
|
50
|
+
* @param {object} request
|
|
51
|
+
* @param {string} request.method
|
|
52
|
+
* @param {number|string} request.statusCode
|
|
53
|
+
* @param {string|undefined} request.route Tri-state: a route string, an empty string (still a
|
|
54
|
+
* valid route — dd-trace-js represents the express root path '/' as an empty path segment), or
|
|
55
|
+
* `undefined` meaning no route information at all.
|
|
56
|
+
* @param {boolean} [request.blocked] Whether the response was blocked by ASM
|
|
49
57
|
* @param {boolean} record When true and the decision is SAMPLE, records the endpoint in the TTL cache
|
|
50
58
|
* @returns {'sample' | 'missing_route' | 'skip'}
|
|
51
59
|
*/
|
|
52
|
-
function
|
|
60
|
+
function sampleRootSpanRequest (rootSpan, request, record = false) {
|
|
53
61
|
if (!enabled) return SamplingDecision.SKIP
|
|
54
62
|
|
|
55
|
-
const rootSpan = web.root(req)
|
|
56
63
|
if (!rootSpan) return SamplingDecision.SKIP
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
let priority = getSpanPriority(rootSpan)
|
|
60
|
-
if (!priority) {
|
|
61
|
-
rootSpan._prioritySampler?.sample(rootSpan)
|
|
62
|
-
priority = getSpanPriority(rootSpan)
|
|
63
|
-
}
|
|
65
|
+
const { method, statusCode, route, blocked = false } = request ?? {}
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
if (!method || !statusCode) {
|
|
68
|
+
log.warn('[ASM] Unsupported groupkey for API security')
|
|
69
|
+
return SamplingDecision.SKIP
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
if (!resolved) return SamplingDecision.SKIP
|
|
72
|
+
if (isRejected(rootSpan)) return SamplingDecision.SKIP
|
|
72
73
|
|
|
73
|
-
if (
|
|
74
|
-
if (
|
|
74
|
+
if (isRoutelessRecord(route, record)) {
|
|
75
|
+
if (isNotFound(statusCode) || blocked) return SamplingDecision.SKIP
|
|
75
76
|
return SamplingDecision.MISSING_ROUTE
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
const key = buildSamplingKey(method, route, statusCode)
|
|
80
|
+
if (sampledRequests.has(key)) return SamplingDecision.SKIP
|
|
79
81
|
|
|
80
82
|
if (asmStandaloneEnabled) {
|
|
81
83
|
keepTrace(rootSpan, ASM)
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (record) {
|
|
85
|
-
sampledRequests.set(
|
|
87
|
+
sampledRequests.set(key, undefined)
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
return SamplingDecision.SAMPLE
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/**
|
|
94
|
+
* Node HTTP adapter over {@link sampleRootSpanRequest}.
|
|
95
|
+
*
|
|
92
96
|
* @param {import('http').IncomingMessage} req
|
|
93
97
|
* @param {import('http').ServerResponse} res
|
|
94
|
-
* @
|
|
98
|
+
* @param {boolean} record When true and the decision is SAMPLE, records the endpoint in the TTL cache
|
|
99
|
+
* @returns {'sample' | 'missing_route' | 'skip'}
|
|
95
100
|
*/
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
return resolved !== null && sampledRequests.has(resolved.key)
|
|
99
|
-
}
|
|
101
|
+
function sampleRequest (req, res, record = false) {
|
|
102
|
+
if (!enabled) return SamplingDecision.SKIP
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const status = res.statusCode
|
|
104
|
+
const rootSpan = web.root(req)
|
|
105
|
+
if (!rootSpan) return SamplingDecision.SKIP
|
|
104
106
|
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
if (isRejected(rootSpan)) return SamplingDecision.SKIP
|
|
108
|
+
|
|
109
|
+
const statusCode = res.statusCode
|
|
110
|
+
const route = getRouteOrEndpoint(web.getContext(req), statusCode)
|
|
111
|
+
|
|
112
|
+
return sampleRootSpanRequest(rootSpan, {
|
|
113
|
+
method: req.method,
|
|
114
|
+
statusCode,
|
|
115
|
+
route,
|
|
116
|
+
blocked: isRoutelessRecord(route, record) && !isNotFound(statusCode) ? isBlocked(res) : false,
|
|
117
|
+
}, record)
|
|
118
|
+
}
|
|
109
119
|
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Whether this is a request with no route information
|
|
122
|
+
*
|
|
123
|
+
* @param {string|undefined} route
|
|
124
|
+
* @param {boolean} record
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
function isRoutelessRecord (route, record) {
|
|
128
|
+
return record && route === undefined
|
|
129
|
+
}
|
|
112
130
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
131
|
+
/**
|
|
132
|
+
* @param {number|string} statusCode
|
|
133
|
+
* @returns {boolean}
|
|
134
|
+
*/
|
|
135
|
+
function isNotFound (statusCode) {
|
|
136
|
+
return Number(statusCode) === 404
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {string} method
|
|
141
|
+
* @param {string|undefined} route A route string, an empty string (still a valid route), or
|
|
142
|
+
* `undefined`
|
|
143
|
+
* @param {number|string} statusCode
|
|
144
|
+
* @returns {string}
|
|
145
|
+
*/
|
|
146
|
+
function buildSamplingKey (method, route, statusCode) {
|
|
147
|
+
return method + (route ?? '') + statusCode
|
|
116
148
|
}
|
|
117
149
|
|
|
150
|
+
/**
|
|
151
|
+
* @param {{ paths?: string[], span?: object }} [context] Web context of the request
|
|
152
|
+
* @param {number|string} statusCode
|
|
153
|
+
* @returns {string|undefined} A route string, an empty string (still a valid route), or
|
|
154
|
+
* `undefined` when no route information is available.
|
|
155
|
+
*/
|
|
118
156
|
function getRouteOrEndpoint (context, statusCode) {
|
|
119
157
|
// The router plugin populates `context.paths` whenever the framework matched something.
|
|
120
158
|
// For express's root '/' route the matched path is normalized to '' (see datadog-plugin-router),
|
|
@@ -125,23 +163,44 @@ function getRouteOrEndpoint (context, statusCode) {
|
|
|
125
163
|
return paths.join('')
|
|
126
164
|
}
|
|
127
165
|
|
|
128
|
-
if (statusCode
|
|
166
|
+
if (isNotFound(statusCode)) return
|
|
129
167
|
|
|
130
168
|
const endpoint = context?.span?.context()?.getTag?.('http.endpoint')
|
|
131
169
|
if (endpoint) return endpoint
|
|
170
|
+
}
|
|
132
171
|
|
|
133
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Whether the span's trace is dropped, forcing the sampling decision if none was made yet.
|
|
174
|
+
*
|
|
175
|
+
* Always false under ASM standalone: there the trace is kept via `keepTrace(rootSpan, ASM)`
|
|
176
|
+
* regardless of the APM sampling decision.
|
|
177
|
+
*
|
|
178
|
+
* @param {DatadogSpan} rootSpan
|
|
179
|
+
* @returns {boolean}
|
|
180
|
+
*/
|
|
181
|
+
function isRejected (rootSpan) {
|
|
182
|
+
if (asmStandaloneEnabled) return false
|
|
183
|
+
|
|
184
|
+
let priority = getSpanPriority(rootSpan)
|
|
185
|
+
if (priority == null) {
|
|
186
|
+
rootSpan._prioritySampler?.sample(rootSpan)
|
|
187
|
+
priority = getSpanPriority(rootSpan)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return priority === AUTO_REJECT || priority === USER_REJECT
|
|
134
191
|
}
|
|
135
192
|
|
|
136
193
|
function getSpanPriority (span) {
|
|
137
194
|
const spanContext = span.context?.()
|
|
138
|
-
|
|
195
|
+
const priority = spanContext?._sampling?.priority
|
|
196
|
+
|
|
197
|
+
return priority == null ? priority : Number(priority)
|
|
139
198
|
}
|
|
140
199
|
|
|
141
200
|
module.exports = {
|
|
142
201
|
configure,
|
|
143
202
|
disable,
|
|
144
203
|
sampleRequest,
|
|
145
|
-
|
|
204
|
+
sampleRootSpanRequest,
|
|
146
205
|
SamplingDecision,
|
|
147
206
|
}
|
|
@@ -32,11 +32,15 @@ function disable () {
|
|
|
32
32
|
disableGraphql()
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {{ createResolverInfo?: (data: object) => object | null }} data
|
|
37
|
+
*/
|
|
38
|
+
function onGraphqlStartResolver (data) {
|
|
36
39
|
const req = getActiveRequest()
|
|
37
40
|
|
|
38
41
|
if (!req) return
|
|
39
42
|
|
|
43
|
+
const resolverInfo = data.createResolverInfo?.(data)
|
|
40
44
|
if (!resolverInfo || typeof resolverInfo !== 'object') return
|
|
41
45
|
|
|
42
46
|
const result = waf.run({ ephemeral: { [addresses.HTTP_INCOMING_GRAPHQL_RESOLVER]: resolverInfo } }, req)
|
|
@@ -46,7 +50,7 @@ function onGraphqlStartResolver ({ abortController, resolverInfo }) {
|
|
|
46
50
|
if (requestData?.isInGraphqlRequest) {
|
|
47
51
|
requestData.blocked = true
|
|
48
52
|
requestData.wafAction = blockingAction
|
|
49
|
-
abortController?.abort()
|
|
53
|
+
data.abortController?.abort()
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
}
|
|
@@ -178,12 +178,12 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
178
178
|
addGraphQLSubscriptions () {
|
|
179
179
|
this.addSub(
|
|
180
180
|
{ channelName: 'apm:graphql:resolve:start', tag: HTTP_REQUEST_BODY },
|
|
181
|
-
(
|
|
181
|
+
({ rootCtx, args }) => {
|
|
182
182
|
const iastContext = getIastContext(storage('legacy').getStore())
|
|
183
|
-
const source =
|
|
183
|
+
const source = rootCtx?.source
|
|
184
184
|
const ranges = source && getRanges(iastContext, source)
|
|
185
185
|
if (ranges?.length) {
|
|
186
|
-
this._taintTrackingHandler(ranges[0].iinfo.type,
|
|
186
|
+
this._taintTrackingHandler(ranges[0].iinfo.type, args, null, iastContext)
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
)
|
|
@@ -242,7 +242,8 @@ function lodashTaintTrackingHandler (message) {
|
|
|
242
242
|
const context = getContextDefault()
|
|
243
243
|
const transactionId = getTransactionId(context)
|
|
244
244
|
if (transactionId) {
|
|
245
|
-
|
|
245
|
+
const taintResult = getLodashTaintedUtilFn(message.operation)
|
|
246
|
+
message.result = taintResult(transactionId, message.result, ...message.arguments)
|
|
246
247
|
}
|
|
247
248
|
} catch (e) {
|
|
248
249
|
log.error('[ASM] Error invoking CSI lodash %s', message.operation, e)
|
|
@@ -46,8 +46,14 @@ function filterTags (tags) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function processTagValue (tags) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
let processedTags = ''
|
|
50
|
+
let isFirstTag = true
|
|
51
|
+
for (const tag of tags) {
|
|
52
|
+
if (!isFirstTag) processedTags += '_'
|
|
53
|
+
processedTags += tag.includes(':') ? getSegment(tag, ':', 1) : tag
|
|
54
|
+
isFirstTag = false
|
|
55
|
+
}
|
|
56
|
+
return processedTags.replaceAll('.', '_')
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
module.exports = {
|
|
@@ -8,58 +8,33 @@ const MYSQL_STRING_LITERAL = String.raw`"(?:\\"|[^"])*"|'(?:\\'|[^'])*'`
|
|
|
8
8
|
const LINE_COMMENT = '--.*$'
|
|
9
9
|
const BLOCK_COMMENT = String.raw`/\*[\s\S]*\*/`
|
|
10
10
|
const EXPONENT = String.raw`(?:E[-+]?\d+[fd]?)?`
|
|
11
|
-
const INTEGER_NUMBER = String.raw
|
|
11
|
+
const INTEGER_NUMBER = String.raw`\b\d+`
|
|
12
12
|
const DECIMAL_NUMBER = String.raw`\d*\.\d+`
|
|
13
13
|
const HEX_NUMBER = 'x\'[0-9a-f]+\'|0x[0-9a-f]+'
|
|
14
14
|
const BIN_NUMBER = 'b\'[0-9a-f]+\'|0b[0-9a-f]+'
|
|
15
15
|
const NUMERIC_LITERAL =
|
|
16
|
-
`[-+]?(?:${
|
|
17
|
-
[
|
|
18
|
-
HEX_NUMBER,
|
|
19
|
-
BIN_NUMBER,
|
|
20
|
-
DECIMAL_NUMBER + EXPONENT,
|
|
21
|
-
INTEGER_NUMBER + EXPONENT,
|
|
22
|
-
].join('|')
|
|
23
|
-
})`
|
|
16
|
+
`[-+]?(?:${HEX_NUMBER}|${BIN_NUMBER}|${DECIMAL_NUMBER + EXPONENT}|${INTEGER_NUMBER + EXPONENT})`
|
|
24
17
|
const ORACLE_ESCAPED_LITERAL = String.raw`q'<.*?>'|q'\(.*?\)'|q'\{.*?\}'|q'\[.*?\]'|q'(?<ESCAPE>.).*?\k<ESCAPE>'`
|
|
25
18
|
|
|
26
19
|
const patterns = {
|
|
27
20
|
ANSI: new RegExp( // Default
|
|
28
|
-
|
|
29
|
-
NUMERIC_LITERAL,
|
|
30
|
-
STRING_LITERAL,
|
|
31
|
-
LINE_COMMENT,
|
|
32
|
-
BLOCK_COMMENT,
|
|
33
|
-
].join('|'),
|
|
21
|
+
`${NUMERIC_LITERAL}|${STRING_LITERAL}|${LINE_COMMENT}|${BLOCK_COMMENT}`,
|
|
34
22
|
'gmi'
|
|
35
23
|
),
|
|
36
24
|
MYSQL: new RegExp(
|
|
37
|
-
|
|
38
|
-
NUMERIC_LITERAL,
|
|
39
|
-
MYSQL_STRING_LITERAL,
|
|
40
|
-
LINE_COMMENT,
|
|
41
|
-
BLOCK_COMMENT,
|
|
42
|
-
].join('|'),
|
|
25
|
+
`${NUMERIC_LITERAL}|${MYSQL_STRING_LITERAL}|${LINE_COMMENT}|${BLOCK_COMMENT}`,
|
|
43
26
|
'gmi'
|
|
44
27
|
),
|
|
45
28
|
POSTGRES: new RegExp(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
29
|
+
`${NUMERIC_LITERAL}|${POSTGRESQL_ESCAPED_LITERAL}|${STRING_LITERAL}|${LINE_COMMENT}|${BLOCK_COMMENT}`,
|
|
30
|
+
'gmi'
|
|
31
|
+
),
|
|
32
|
+
ORACLE: new RegExp(
|
|
33
|
+
// The capture owns the quote delimiter while the lazy quantifier owns the body.
|
|
34
|
+
// eslint-disable-next-line regexp/optimal-quantifier-concatenation
|
|
35
|
+
`${NUMERIC_LITERAL}|${ORACLE_ESCAPED_LITERAL}|${STRING_LITERAL}|${LINE_COMMENT}|${BLOCK_COMMENT}`,
|
|
53
36
|
'gmi'
|
|
54
37
|
),
|
|
55
|
-
ORACLE: new RegExp([
|
|
56
|
-
NUMERIC_LITERAL,
|
|
57
|
-
ORACLE_ESCAPED_LITERAL,
|
|
58
|
-
STRING_LITERAL,
|
|
59
|
-
LINE_COMMENT,
|
|
60
|
-
BLOCK_COMMENT,
|
|
61
|
-
].join('|'),
|
|
62
|
-
'gmi'),
|
|
63
38
|
}
|
|
64
39
|
patterns.SQLITE = patterns.MYSQL
|
|
65
40
|
patterns.MARIADB = patterns.MYSQL
|
|
@@ -8,7 +8,7 @@ const AUTHORITY = '^(?:[^:]+:)?//([^@]+)@'
|
|
|
8
8
|
// boundaries), so excluding them preserves match semantics for valid URLs while keeping
|
|
9
9
|
// the regex linear on arbitrary input.
|
|
10
10
|
const QUERY_FRAGMENT = '[?#&]([^=&;?#]+)=([^?#&]+)'
|
|
11
|
-
const pattern = new RegExp(
|
|
11
|
+
const pattern = new RegExp(`${AUTHORITY}|${QUERY_FRAGMENT}`, 'gm')
|
|
12
12
|
|
|
13
13
|
module.exports = function extractSensitiveRanges (evidence) {
|
|
14
14
|
try {
|