From 82d80014bf8a719f0811e4835f9afc16d2625291 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 11 May 2026 12:42:38 +0200 Subject: [PATCH] fix(otel): address lint and test regressions from C1/C2 fixes - Prefix unused opts param with _opts in bindOtelInstrumentation (ESLint) - Extend IPv6 regex to cover prefix::suffix form (e.g. 2001:0db8::1) so the IPv6 scrub test passes correctly - Delete orphaned sentry-pii-scrubber.test.ts files from apps/cms and apps/web-tanstack that imported the deleted sentry/scrub module (broke typecheck; the OTel-layer replacement tests are in pii-scrub-processor.test.ts) Co-Authored-By: Claude Sonnet 4.6 --- .../src/__tests__/sentry-pii-scrubber.test.ts | 56 ------------------- .../src/__tests__/sentry-pii-scrubber.test.ts | 56 ------------------- .../di/bind-otel-instrumentation.ts | 4 +- .../src/instrumentation/otel/pii-fields.ts | 11 +++- 4 files changed, 12 insertions(+), 115 deletions(-) delete mode 100644 apps/cms/src/__tests__/sentry-pii-scrubber.test.ts delete mode 100644 apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts diff --git a/apps/cms/src/__tests__/sentry-pii-scrubber.test.ts b/apps/cms/src/__tests__/sentry-pii-scrubber.test.ts deleted file mode 100644 index e8e45ef..0000000 --- a/apps/cms/src/__tests__/sentry-pii-scrubber.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - beforeSend, - beforeSendTransaction, -} from "@repo/core-shared/instrumentation/sentry/scrub"; - -describe("R38 — apps/cms PII scrubber", () => { - it("strips email/password/cookie/auth/IP from event payload", () => { - const event = { - extra: { - userEmail: "alice@example.com", - password: "p4$$w0rd", - ipAddress: "192.168.1.10", - note: "request from 10.0.0.1", - }, - request: { - headers: { - Authorization: "Bearer secret", - "Set-Cookie": "session=abc", - "User-Agent": "Mozilla", - }, - }, - } as Parameters[0]; - const result = beforeSend(event, {}) as { - extra: Record; - request: { headers: Record }; - }; - expect(result.extra["userEmail"]).toBe("[redacted]"); - expect(result.extra["password"]).toBe("[redacted]"); - expect(result.extra["ipAddress"]).toBe("[redacted]"); - expect(result.extra["note"]).toContain("[redacted-ip]"); - expect(result.request.headers["Authorization"]).toBe("[redacted]"); - expect(result.request.headers["Set-Cookie"]).toBe("[redacted]"); - expect(result.request.headers["User-Agent"]).toBe("Mozilla"); - }); - - it("strips ?token / ?email / ?password / ?secret / ?signature from URLs", () => { - const event = { - request: { - url: "https://app/api/x?token=abc&email=a@b.c&password=p&secret=z&signature=s&safe=1", - }, - transaction: "/foo?accessToken=t", - } as Parameters[0]; - const result = beforeSendTransaction(event, {}) as { - request: { url: string }; - transaction: string; - }; - const url = decodeURIComponent(result.request.url); - const txn = decodeURIComponent(result.transaction); - for (const key of ["token", "email", "password", "secret", "signature"]) { - expect(url).toContain(`${key}=[redacted]`); - } - expect(url).toContain("safe=1"); - expect(txn).toContain("accessToken=[redacted]"); - }); -}); diff --git a/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts b/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts deleted file mode 100644 index 11eddd5..0000000 --- a/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - beforeSend, - beforeSendTransaction, -} from "@repo/core-shared/instrumentation/sentry/scrub"; - -describe("R38 — apps/web-tanstack PII scrubber", () => { - it("strips email/password/cookie/auth/IP from event payload", () => { - const event = { - extra: { - userEmail: "alice@example.com", - password: "p4$$w0rd", - ipAddress: "192.168.1.10", - note: "request from 10.0.0.1", - }, - request: { - headers: { - Authorization: "Bearer secret", - "Set-Cookie": "session=abc", - "User-Agent": "Mozilla", - }, - }, - } as Parameters[0]; - const result = beforeSend(event, {}) as { - extra: Record; - request: { headers: Record }; - }; - expect(result.extra["userEmail"]).toBe("[redacted]"); - expect(result.extra["password"]).toBe("[redacted]"); - expect(result.extra["ipAddress"]).toBe("[redacted]"); - expect(result.extra["note"]).toContain("[redacted-ip]"); - expect(result.request.headers["Authorization"]).toBe("[redacted]"); - expect(result.request.headers["Set-Cookie"]).toBe("[redacted]"); - expect(result.request.headers["User-Agent"]).toBe("Mozilla"); - }); - - it("strips ?token / ?email / ?password / ?secret / ?signature from URLs", () => { - const event = { - request: { - url: "https://app/api/x?token=abc&email=a@b.c&password=p&secret=z&signature=s&safe=1", - }, - transaction: "/foo?accessToken=t", - } as Parameters[0]; - const result = beforeSendTransaction(event, {}) as { - request: { url: string }; - transaction: string; - }; - const url = decodeURIComponent(result.request.url); - const txn = decodeURIComponent(result.transaction); - for (const key of ["token", "email", "password", "secret", "signature"]) { - expect(url).toContain(`${key}=[redacted]`); - } - expect(url).toContain("safe=1"); - expect(txn).toContain("accessToken=[redacted]"); - }); -}); diff --git a/packages/core-shared/src/instrumentation/di/bind-otel-instrumentation.ts b/packages/core-shared/src/instrumentation/di/bind-otel-instrumentation.ts index e5e7bd7..0d9bd15 100644 --- a/packages/core-shared/src/instrumentation/di/bind-otel-instrumentation.ts +++ b/packages/core-shared/src/instrumentation/di/bind-otel-instrumentation.ts @@ -24,7 +24,9 @@ export type BindOtelOpts = { */ export function bindOtelInstrumentation( container: Container, - opts: BindOtelOpts, + // opts is accepted for API compatibility with call sites that still pass dsn + app. + // The SDK is initialized by instrumentation.ts register() — no fields are used here. + _opts: BindOtelOpts, ): { tracer: ITracer; logger: ILogger; metrics: IMetrics } { const tracer = new OtelTracer(); const logger = new OtelLogger(); diff --git a/packages/core-shared/src/instrumentation/otel/pii-fields.ts b/packages/core-shared/src/instrumentation/otel/pii-fields.ts index 8e5a703..b0386fc 100644 --- a/packages/core-shared/src/instrumentation/otel/pii-fields.ts +++ b/packages/core-shared/src/instrumentation/otel/pii-fields.ts @@ -44,10 +44,17 @@ export const PII_QUERY_PARAM_SUBSTRINGS = [ export const REDACTED_VALUE = "[redacted]" as const; export const REDACTED_IP = "[redacted-ip]" as const; -// IPv4: simple dotted-quad; IPv6: any colon-separated hex with at least one :: +// IPv4: simple dotted-quad. export const IPV4_REGEX = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g; + +// IPv6: covers the three common forms: +// 1. Full / no-:: form: 2001:0db8:0000:0000:0000:0000:0000:0001 +// 2. Compressed prefix::rest: 2001:0db8::1 (one or more groups before ::) +// 3. Leading ::suffix: ::1 or ::ffff:192.0.2.1 +// The alternation order puts the longer prefix:: pattern first so it captures +// the full address rather than leaving the prefix unmatched. export const IPV6_REGEX = - /\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\b|::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}/g; + /\b(?:[0-9a-fA-F]{1,4}:){1,7}:[0-9a-fA-F]{0,4}\b|\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\b|::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{0,4}/g; export function keyContainsPii(key: string): boolean { const lower = key.toLowerCase();