The share card stops trusting request headers

Host and x-forwarded-proto are attacker-writable and were interpolated
raw into the share page's meta attributes. The proto must now be
literally http or https, and the host is HTML-escaped like everything
else that reaches the head.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-23 17:42:20 -04:00
co-authored by Claude Fable 5
parent b120f9b6bd
commit a8a63cc27b
+5 -1
View File
@@ -143,7 +143,11 @@ const escapeHtml = (t: string) =>
/** index.html with this share's OpenGraph card folded into its head —
* crawlers never run the app, so the unfurl must arrive pre-baked. */
function shareHtml(id: string, data: ShareData, host: string, proto: string): string {
function shareHtml(id: string, data: ShareData, rawHost: string, rawProto: string): string {
// Host and proto arrive from request headers — attacker-writable text
// that must never reach an HTML attribute raw.
const proto = /^https?$/.test(rawProto) ? rawProto : "https";
const host = escapeHtml(rawHost);
// The stock page carries its own generic card; strip it, or crawlers
// (which take the FIRST tag they meet) never see this turn's.
const html = readFileSync(join(staticRoot!, "index.html"), "utf8")