feat(security): capture line/column/sample in CSP violation reports

Add 'report-sample' to script-src and capture lineNumber/columnNumber/
sample in /api/csp-report for both report shapes (Reporting API and
legacy report-uri). Previously only sourceFile was captured, which
wasn't enough to pin down the 2026-08-15 eval violations — live repro
via the avatar-crop flow (react-easy-crop) came back clean, so the
next real occurrence needs better diagnostics to actually find it.
This commit is contained in:
admin committed 2026-08-22 14:14:27 +02:00
1 parent ddbeee759b
commit ab1b58d644
2 files changed
+14 -1

No files matched your search

+13
View File
@@ -23,12 +23,19 @@ interface NormalizedViolation {
blockedUri?: string;
disposition?: string;
sourceFile?: string;
lineNumber?: string;
columnNumber?: string;
sample?: string;
}
function asString(value: unknown): string | undefined {
return typeof value === "string" ? value : undefined;
}
function asStringified(value: unknown): string | undefined {
return typeof value === "number" ? String(value) : asString(value);
}
function normalizeReports(payload: unknown): NormalizedViolation[] {
if (Array.isArray(payload)) {
return payload
@@ -41,6 +48,9 @@ function normalizeReports(payload: unknown): NormalizedViolation[] {
blockedUri: asString(body.blockedURL),
disposition: asString(body.disposition),
sourceFile: asString(body.sourceFile),
lineNumber: asStringified(body.lineNumber),
columnNumber: asStringified(body.columnNumber),
sample: asString(body.sample),
};
});
}
@@ -54,6 +64,9 @@ function normalizeReports(payload: unknown): NormalizedViolation[] {
blockedUri: asString(report["blocked-uri"]),
disposition: asString(report.disposition),
sourceFile: asString(report["source-file"]),
lineNumber: asStringified(report["line-number"]),
columnNumber: asStringified(report["column-number"]),
sample: asString(report["script-sample"]),
},
];
}
+1 -1
View File
@@ -139,7 +139,7 @@ export default clerkMiddleware(
reportOnly: false,
reportTo: "/api/csp-report",
directives: {
"script-src": ["unsafe-eval"],
"script-src": ["unsafe-eval", "'report-sample'"],
"img-src": [`https://${getSupabaseHostname()}`, "https://image.mux.com", "blob:"],
"connect-src": [
`https://${getSupabaseHostname()}`,