Skip to content

Commit 0b8173b

Browse files
authored
fix(platform): derive Sentry CSP origin from SENTRY_DSN (#1548)
1 parent 4231f0f commit 0b8173b

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

services/platform/server.test.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,24 @@ describe('security headers', () => {
6262
expect(csp).toContain('https://mcp.figma.com');
6363
});
6464

65-
test('CSP includes Sentry origin when SENTRY_DSN is set', async () => {
65+
test('CSP includes Sentry origin parsed from SENTRY_DSN', async () => {
6666
const app = createApp({
6767
...baseEnv,
68-
SENTRY_DSN: 'https://abc@sentry.io/123',
68+
SENTRY_DSN: 'https://abc@o1.ingest.us.sentry.io/123',
6969
});
7070
const res = await app.fetch(new Request('http://localhost/api/health'));
7171
const csp = res.headers.get('content-security-policy') ?? '';
72-
expect(csp).toContain('https://*.ingest.sentry.io');
72+
expect(csp).toContain('https://o1.ingest.us.sentry.io');
73+
});
74+
75+
test('CSP supports self-hosted Sentry on a custom domain', async () => {
76+
const app = createApp({
77+
...baseEnv,
78+
SENTRY_DSN: 'https://abc@sentry.elintrio.com/123',
79+
});
80+
const res = await app.fetch(new Request('http://localhost/api/health'));
81+
const csp = res.headers.get('content-security-policy') ?? '';
82+
expect(csp).toContain('https://sentry.elintrio.com');
7383
});
7484
});
7585

services/platform/server.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ function getEnvConfig(): EnvConfig {
107107
// dropped for this reason.
108108
//
109109
// Current exceptions are gated by explicit operator opt-in:
110-
// - Sentry (`*.ingest.sentry.io`): only when SENTRY_DSN is set.
110+
// - Sentry: origin is parsed from SENTRY_DSN (supports SaaS ingest and
111+
// self-hosted Sentry on custom domains). Only emitted when DSN is set.
111112
// - Figma MCP (`mcp.figma.com`): only when SITE_URL is a loopback host
112113
// (dev-only; production policy never includes it).
113114
//
@@ -118,8 +119,20 @@ function getEnvConfig(): EnvConfig {
118119
// whether HSTS is emitted (only when the deployment is HTTPS).
119120
// ---------------------------------------------------------------------------
120121

122+
function sentryOriginFromDsn(dsn: string | undefined): string | null {
123+
if (!dsn) return null;
124+
try {
125+
const url = new URL(dsn);
126+
return `${url.protocol}//${url.host}`;
127+
} catch (err) {
128+
console.warn('Invalid SENTRY_DSN, skipping CSP allow-list entry:', err);
129+
return null;
130+
}
131+
}
132+
121133
function buildContentSecurityPolicy(env: EnvConfig) {
122-
const sentry = env.SENTRY_DSN ? ['https://*.ingest.sentry.io'] : [];
134+
const sentryOrigin = sentryOriginFromDsn(env.SENTRY_DSN);
135+
const sentry = sentryOrigin ? [sentryOrigin] : [];
123136
const figmaMcp = isLoopbackSite(env) ? ['https://mcp.figma.com'] : [];
124137
return {
125138
defaultSrc: ["'self'"],

0 commit comments

Comments
 (0)