fix(security): close CSP report-only gaps, wire Sentry sourcemap upload into Docker build

- connect-src: allow the GlitchTip domain itself so the SDK can report events
- media-src: allow Mux's edge CDN (*.edgemv.mux.com) for HLS manifests
- img-src: allow blob: for client-side upload previews
- pass SENTRY_ORG/SENTRY_PROJECT/SENTRY_AUTH_TOKEN as Docker build args so
  withSentryConfig can upload readable source maps during `npm run build`
This commit is contained in:
2026-08-13 20:13:00 +02:00
parent fd6f1f0564
commit b469389654
3 changed files with 35 additions and 5 deletions
+12 -2
View File
@@ -17,15 +17,25 @@ WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Only NEXT_PUBLIC_* vars are baked into the client bundle at build time.
# All server-side secrets are injected at runtime via docker-compose env_file.
# NEXT_PUBLIC_* vars are baked into the client bundle at build time.
# All other server-side secrets are injected at runtime via docker-compose
# env_file — except the three below, which withSentryConfig (next.config.ts)
# needs during `npm run build` itself to upload source maps. They never reach
# the final runner stage (discarded with this builder stage), so they don't
# leak into the shipped image's runtime environment.
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SENTRY_DSN
ARG SENTRY_ORG
ARG SENTRY_PROJECT
ARG SENTRY_AUTH_TOKEN
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
ENV SENTRY_ORG=$SENTRY_ORG
ENV SENTRY_PROJECT=$SENTRY_PROJECT
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
ENV NEXT_TELEMETRY_DISABLED=1
RUN npx prisma generate
+8 -1
View File
@@ -12,11 +12,18 @@ services:
context: ..
dockerfile: docker/Dockerfile
args:
# Only public vars needed at build time (baked into client bundle).
# Public vars needed at build time (baked into client bundle).
# Docker Compose reads these from docker/.env automatically.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL}
NEXT_PUBLIC_SENTRY_DSN: ${NEXT_PUBLIC_SENTRY_DSN}
# Sentry/GlitchTip source-map upload also runs during `npm run build`
# (withSentryConfig in next.config.ts), so it needs these as build
# args too — env_file below only reaches the running container, not
# the build stage. Leave unset to keep sourcemap upload disabled.
SENTRY_ORG: ${SENTRY_ORG}
SENTRY_PROJECT: ${SENTRY_PROJECT}
SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN}
image: pawfeed:latest
container_name: pawfeed
restart: unless-stopped
+15 -2
View File
@@ -41,6 +41,18 @@ function getSupabaseHostname(): string {
return "*.supabase.co";
}
function getGlitchtipHostname(): string {
const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN;
if (dsn) {
try {
return new URL(dsn).hostname;
} catch {
/* fall through */
}
}
return "glitchtip.pawfeed.org";
}
export default clerkMiddleware(
async (auth, request) => {
const { pathname } = request.nextUrl;
@@ -82,15 +94,16 @@ export default clerkMiddleware(
reportOnly: true,
reportTo: "/api/csp-report",
directives: {
"img-src": [`https://${getSupabaseHostname()}`, "https://image.mux.com"],
"img-src": [`https://${getSupabaseHostname()}`, "https://image.mux.com", "blob:"],
"connect-src": [
`https://${getSupabaseHostname()}`,
`https://${getGlitchtipHostname()}`,
"https://stream.mux.com",
"https://image.mux.com",
"https://litix.io",
"https://storage.googleapis.com",
],
"media-src": ["self", "blob:", "https://stream.mux.com"],
"media-src": ["self", "blob:", "https://stream.mux.com", "https://*.edgemv.mux.com"],
"font-src": ["self", "data:"],
"frame-ancestors": ["none"],
"object-src": ["none"],