diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 5956b6c..f864bfe 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -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")