// packages/core-shared/src/instrumentation/sentry/scrub.test.ts import { describe, it, expect } from "vitest"; import { beforeSend, beforeSendTransaction } from "@/instrumentation/sentry/scrub"; // Use structural types that match what scrub functions accept type ScrubEvent = Parameters[0]; type ScrubHint = Parameters[1]; type TxEvent = Parameters[0]; type TxHint = Parameters[1]; const hint = {} as ScrubHint; const txHint = {} as TxHint; describe("beforeSend", () => { it("redacts top-level keys whose names contain PII substrings", () => { const event = { extra: { email: "a@b.c", username: "alice" }, contexts: { custom: { password: "p", note: "ok" } }, } as unknown as ScrubEvent; const result = beforeSend(event, hint) as Record; const extra = result["extra"] as Record; const contexts = result["contexts"] as { custom: Record }; expect(extra["email"]).toBe("[redacted]"); expect(extra["username"]).toBe("alice"); expect(contexts.custom["password"]).toBe("[redacted]"); expect(contexts.custom["note"]).toBe("ok"); }); it("redacts derived key names (substring match): userEmail, accessToken, apiKey", () => { const event = { extra: { userEmail: "a@b.c", accessToken: "t", apiKey: "k", id: "u1" }, } as unknown as ScrubEvent; const result = beforeSend(event, hint) as Record; const extra = result["extra"] as Record; expect(extra["userEmail"]).toBe("[redacted]"); expect(extra["accessToken"]).toBe("[redacted]"); expect(extra["apiKey"]).toBe("[redacted]"); expect(extra["id"]).toBe("u1"); }); it("redacts headers map keys case-insensitively", () => { const event = { request: { headers: { Authorization: "Bearer x", "Set-Cookie": "session=abc", "User-Agent": "ua" }, }, } as unknown as ScrubEvent; const result = beforeSend(event, hint) as Record; const request = result["request"] as { headers: Record }; expect(request.headers["Authorization"]).toBe("[redacted]"); expect(request.headers["Set-Cookie"]).toBe("[redacted]"); expect(request.headers["User-Agent"]).toBe("ua"); }); it("redacts IPv4 addresses found in string values", () => { const event = { extra: { note: "Connection from 192.168.1.10 failed" }, } as unknown as ScrubEvent; const result = beforeSend(event, hint) as Record; const extra = result["extra"] as Record; expect(extra["note"]).toBe("Connection from [redacted-ip] failed"); }); it("redacts IPv6 addresses found in string values", () => { const event = { extra: { note: "Tunnel to fe80::1ff:fe23:4567:890a established" }, } as unknown as ScrubEvent; const result = beforeSend(event, hint) as Record; const extra = result["extra"] as Record; expect(extra["note"] as string).toContain("[redacted-ip]"); }); it("does not crash on null/undefined branches", () => { expect(beforeSend({ extra: null } as unknown as ScrubEvent, hint)).toBeTruthy(); expect(beforeSend({} as ScrubEvent, hint)).toBeTruthy(); }); it("returns the event (not null) — keeps Sentry transport flowing", () => { expect(beforeSend({ extra: { ok: true } } as unknown as ScrubEvent, hint)).toBeTruthy(); }); }); describe("beforeSendTransaction", () => { it("strips PII query params from request.url", () => { const event = { request: { url: "https://app/api/foo?token=secret&user=alice&email=a@b.c" }, } as unknown as TxEvent; const result = beforeSendTransaction(event, txHint) as Record; const request = result["request"] as { url: string }; expect(request.url).toContain("token=%5Bredacted%5D"); expect(request.url).toContain("email=%5Bredacted%5D"); expect(request.url).toContain("user=alice"); }); it("strips PII query params from event.transaction", () => { const event = { transaction: "/foo?token=x&id=y" } as unknown as TxEvent; const result = beforeSendTransaction(event, txHint) as Record; expect(result["transaction"] as string).toContain("token=%5Bredacted%5D"); expect(result["transaction"] as string).toContain("id=y"); }); it("matches derived param names (accessToken, ApiSecret)", () => { const event = { request: { url: "https://x/y?accessToken=t&ApiSecret=z&safe=1" }, } as unknown as TxEvent; const result = beforeSendTransaction(event, txHint) as Record; const request = result["request"] as { url: string }; expect(request.url).toContain("accessToken=%5Bredacted%5D"); expect(request.url).toContain("ApiSecret=%5Bredacted%5D"); expect(request.url).toContain("safe=1"); }); it("returns the event when no URL present", () => { expect(beforeSendTransaction({} as TxEvent, txHint)).toBeTruthy(); }); });