From 1d9fa528e9190502f2791f4472987a7767343218 Mon Sep 17 00:00:00 2001 From: samunohito <46447427+samunohito@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:33:15 +0900 Subject: [PATCH] =?UTF-8?q?=E6=AD=A3=E3=81=97=E3=81=8FREDACTED=E3=81=AB?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=82=8B=E3=81=8B=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../telemetry-database-instrumentation.ts | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/backend/test/unit/telemetry-database-instrumentation.ts b/packages/backend/test/unit/telemetry-database-instrumentation.ts index 20b3789a26..eeb3b4e039 100644 --- a/packages/backend/test/unit/telemetry-database-instrumentation.ts +++ b/packages/backend/test/unit/telemetry-database-instrumentation.ts @@ -3,9 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { createRequire } from 'node:module'; +import { context, propagation, trace } from '@opentelemetry/api'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { describe, expect, test, vi } from 'vitest'; +import type { Span as SdkSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base'; import { installDatabaseInstrumentation, installInstrumentation } from '@/core/telemetry/database-instrumentation.js'; +const backendRequire = createRequire(import.meta.url); + describe('database-instrumentation', () => { test('does not install PostgreSQL instrumentation when disabled', async () => { const uninstall = await installDatabaseInstrumentation({} as any, { @@ -55,6 +61,76 @@ describe('database-instrumentation', () => { expect(pg.disable).toHaveBeenCalledOnce(); }); + test('redacts SQL attributes after pg instrumentation records the statement', async () => { + const statement = 'SELECT very_secret_literal'; + const attributeWrites: Array<{ key: string; value: unknown }> = []; + let attributesAtStart: Record | undefined; + let pgSpan: SdkSpan | undefined; + const spanProcessor: SpanProcessor = { + forceFlush: async () => {}, + onStart: span => { + if (span.instrumentationScope.name !== '@opentelemetry/instrumentation-pg') return; + + pgSpan = span; + attributesAtStart = { ...span.attributes }; + const setAttribute = span.setAttribute.bind(span); + span.setAttribute = (key, value) => { + attributeWrites.push({ key, value }); + return setAttribute(key, value); + }; + }, + onEnd: () => {}, + shutdown: async () => {}, + }; + const provider = new NodeTracerProvider({ spanProcessors: [spanProcessor] }); + provider.register(); + let uninstall: (() => void) | undefined; + + try { + uninstall = await installDatabaseInstrumentation(provider, { + capturePgSpans: true, + capturePgStatement: false, + capturePgConnectionSpans: false, + }); + const { Client } = backendRequire('pg') as typeof import('pg'); + const tracer = provider.getTracer('database-instrumentation-test'); + + tracer.startActiveSpan('parent', parentSpan => { + try { + // pg instrumentation runs before an unconnected client queues the query, + // so no database is required. + new Client().query(statement, () => {}); + } finally { + parentSpan.end(); + } + }); + + const recordedPgSpan = pgSpan; + expect(recordedPgSpan).toBeDefined(); + if (recordedPgSpan == null) throw new Error('PostgreSQL span was not started.'); + expect(attributesAtStart).not.toHaveProperty('db.statement'); + expect(attributesAtStart).not.toHaveProperty('db.query.text'); + + const rawSqlIndex = attributeWrites.findIndex(({ key, value }) => + (key === 'db.statement' || key === 'db.query.text') && value === statement); + expect(rawSqlIndex).toBeGreaterThanOrEqual(0); + const rawSqlKey = attributeWrites[rawSqlIndex].key; + expect(attributeWrites.findIndex(({ key, value }, index) => + index > rawSqlIndex && key === rawSqlKey && value === '[REDACTED]')).toBeGreaterThan(rawSqlIndex); + + expect(recordedPgSpan.attributes['db.statement']).toBe('[REDACTED]'); + expect(recordedPgSpan.attributes['db.query.text']).toBe('[REDACTED]'); + expect(Object.values(recordedPgSpan.attributes)).not.toContain(statement); + } finally { + pgSpan?.end(); + uninstall?.(); + await provider.shutdown(); + trace.disable(); + context.disable(); + propagation.disable(); + } + }); + test('keeps SQL statement attributes when explicitly enabled', () => { const pg = { setTracerProvider: vi.fn(), enable: vi.fn(), disable: vi.fn() }; const config = vi.fn();