import { SCRATCH_HEADERS, SECURITY_HEADERS } from "./static";
export const SCRATCH_BANNER = `<div role="note" data-gitfor-scratch style="all:initial;position:fixed;z-index:2147483647;left:0;right:0;bottom:0;padding:6px 12px;background:#7c2d12;color:#fff;font:13px/1.4 system-ui,sans-serif;text-align:center">Unclaimed scratch site: anonymous, temporary content. Do not enter passwords or payment details here.</div>`;
const HTTP_SWITCHING = 101;
/**
* WebSocket upgrades can't be rebuilt: `new Response` rejects status 101 and would drop `webSocket`. They carry no
* document, so they pass through untouched.
*/
export function isUpgrade(response: Response): boolean {
return response.status === HTTP_SWITCHING || Boolean((response as { webSocket?: unknown }).webSocket);
}
function rebuilt(response: Response, headers: Headers, body: BodyInit | null = response.body): Response {
return new Response(body, { status: response.status, statusText: response.statusText, headers });
}
/**
* Every response of an unclaimed scratch site, its `_worker.js` included, carries the scratch security headers
* (overriding whatever user code set) and HTML pages get a banner a phishing page can't remove server-side.
*/
export function hardenScratchResponse(response: Response): Response {
if (isUpgrade(response)) return response;
const headers = new Headers(response.headers);
for (const [name, value] of Object.entries(SCRATCH_HEADERS)) headers.set(name, value);
const isHtml = (headers.get("content-type") ?? "").toLowerCase().includes("text/html");
if (!isHtml || !response.body) return rebuilt(response, headers);
headers.delete("content-length");
let injected = false;
const rewritten = new HTMLRewriter()
.on("body", {
element(element) {
injected = true;
element.append(SCRATCH_BANNER, { html: true });
},
})
.onDocument({
end(end) {
if (!injected) end.append(SCRATCH_BANNER, { html: true });