Files
Epicnabbo-Catalogus-Updated…/src/lib/sentry-redact.ts
T
SimoandCursor 09f1bc2bd6 Add production observability: Sentry, pino, and sharp badge encoding.
Sentry is opt-in via DSN env vars; logger uses structured pino JSON in prod; badge uploads are normalized to GIF with sharp.

Co-authored-by: Cursor <[email protected]>
2026-07-17 23:09:57 +02:00

52 lines
1.6 KiB
TypeScript

import type { ErrorEvent, EventHint } from "@sentry/nextjs";
const SENSITIVE_KEY_RE =
/password|secret|token|otp|recovery|authTicket|two_factor|api_key/i;
const REDACTED = "[Redacted]";
function redactObject(input: unknown, depth = 0): unknown {
if (depth > 4 || input == null) return input;
if (Array.isArray(input)) return input.map((v) => redactObject(v, depth + 1));
if (typeof input !== "object") return input;
const out: Record<string, unknown> = {};
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
if (SENSITIVE_KEY_RE.test(key)) {
out[key] = REDACTED;
} else {
out[key] = redactObject(value, depth + 1);
}
}
return out;
}
/** Scrub cookies/auth headers and sensitive keys before sending to Sentry. */
export function redactSentryEvent(
event: ErrorEvent,
_hint: EventHint,
): ErrorEvent | null {
if (event.request) {
if (event.request.cookies) {
event.request.cookies =
REDACTED as unknown as typeof event.request.cookies;
}
if (event.request.headers) {
const headers = event.request.headers as Record<string, string>;
if (headers.cookie) headers.cookie = REDACTED;
if (headers.Cookie) headers.Cookie = REDACTED;
if (headers.authorization) headers.authorization = REDACTED;
if (headers.Authorization) headers.Authorization = REDACTED;
}
if (event.request.data) {
event.request.data = redactObject(
event.request.data,
) as typeof event.request.data;
}
}
if (event.extra) event.extra = redactObject(event.extra) as typeof event.extra;
if (event.contexts) {
event.contexts = redactObject(event.contexts) as typeof event.contexts;
}
return event;
}