-
-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathsentry.edge.config.ts
More file actions
107 lines (96 loc) · 3.79 KB
/
Copy pathsentry.edge.config.ts
File metadata and controls
107 lines (96 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
// The config you add here will be used whenever one of the edge features is loaded.
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from '@sentry/nextjs';
// NOTE: the scrubber code is intentionally duplicated inline (not imported from a
// shared module) — the edge runtime forbids Node-only deps and we don't want a
// shared-helper import to accidentally pull one in.
const REDACTED_HEADERS = new Set(['authorization', 'apikey', 'api-key', 'cookie', 'x-api-key']);
const JWT_RE = /eyJ[A-Za-z0-9_\-.]{40,}/g;
const SB_RE = /sb_[a-z0-9_]{30,}/g;
function scrubSecrets(value: string): string {
return value.replace(JWT_RE, '[REDACTED_JWT]').replace(SB_RE, '[REDACTED_SB]');
}
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
beforeSend(event) {
try {
if (event.request?.headers) {
for (const key of Object.keys(event.request.headers)) {
if (REDACTED_HEADERS.has(key.toLowerCase())) {
event.request.headers[key] = '[REDACTED]';
}
}
}
if (event.extra) {
for (const key of Object.keys(event.extra)) {
const v = event.extra[key];
if (typeof v === 'string') {
event.extra[key] = scrubSecrets(v);
}
}
}
if (event.breadcrumbs) {
for (const crumb of event.breadcrumbs) {
if (typeof crumb.message === 'string') {
crumb.message = scrubSecrets(crumb.message);
}
if (crumb.data) {
for (const key of Object.keys(crumb.data)) {
const v = crumb.data[key];
if (typeof v === 'string') {
crumb.data[key] = scrubSecrets(v);
}
}
}
}
}
// Scrub local variable captures from each stack frame — axios errors
// serialize `config.headers.Authorization` into `frame.vars` and that
// slips past the header/extra/breadcrumb passes above.
if (event.exception?.values) {
for (const ev of event.exception.values) {
const frames = ev.stacktrace?.frames;
if (!frames) continue;
for (const frame of frames) {
const vars = frame.vars as Record<string, unknown> | undefined;
if (!vars || typeof vars !== 'object') continue;
for (const key of Object.keys(vars)) {
const val = vars[key];
if (typeof val === 'string') {
vars[key] = scrubSecrets(val);
} else if (val && typeof val === 'object') {
try {
vars[key] = JSON.parse(scrubSecrets(JSON.stringify(val)));
} catch {
/* leave as-is if non-serializable */
}
}
}
}
}
}
// Scrub `contexts` — Sentry often stores request/response bodies here.
if (event.contexts) {
for (const ctxName of Object.keys(event.contexts)) {
const ctx = event.contexts[ctxName] as Record<string, unknown> | undefined;
if (!ctx || typeof ctx !== 'object') continue;
for (const key of Object.keys(ctx)) {
const val = ctx[key];
if (typeof val === 'string') {
ctx[key] = scrubSecrets(val);
}
}
}
}
} catch {
/* never block the SDK */
}
return event;
},
});