security: attempt something else
This commit is contained in:
parent
34960c1868
commit
120ed4b94f
|
|
@ -37,10 +37,39 @@ function clientIp(req: Request): string {
|
||||||
|| "unknown";
|
|| "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 404s must NOT be cached by browsers or Cloudflare. The same URL frequently
|
||||||
|
// flips between "200 with data" and "404 not-found" — for example when an
|
||||||
|
// operator adds a password to a previously-public page, or when a slug
|
||||||
|
// changes, or when a monitor is removed. If Cloudflare cached a 404 (default
|
||||||
|
// behaviour for unspecified Cache-Control on 404 responses) the operator
|
||||||
|
// would see stale 404s for the entire edge TTL. Likewise, an old 200 cached
|
||||||
|
// at the edge could keep serving image/svg+xml headers over a now-changed
|
||||||
|
// body, which is exactly the symptom that prompted this fix.
|
||||||
|
//
|
||||||
|
// no-store is the bluntest form: never store, always revalidate. Pair with
|
||||||
|
// 404 status so any layer in front knows not to hold on to it.
|
||||||
|
const NO_STORE_HEADERS = {
|
||||||
|
"cache-control": "no-store, must-revalidate",
|
||||||
|
"pragma": "no-cache",
|
||||||
|
} as const;
|
||||||
|
|
||||||
function notFound(): Response {
|
function notFound(): Response {
|
||||||
return new Response(eta.render("not-found", {}), {
|
return new Response(eta.render("not-found", {}), {
|
||||||
status: 404,
|
status: 404,
|
||||||
headers: { "content-type": "text/html; charset=utf-8" },
|
headers: {
|
||||||
|
"content-type": "text/html; charset=utf-8",
|
||||||
|
...NO_STORE_HEADERS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonNotFound(): Response {
|
||||||
|
return new Response(JSON.stringify({ error: "not found" }), {
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
...NO_STORE_HEADERS,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,12 +121,10 @@ async function renderJson(slug: string, request: Request, win?: Window): Promise
|
||||||
// as an oracle for which slugs are private. Forces any OSINT enumeration to
|
// as an oracle for which slugs are private. Forces any OSINT enumeration to
|
||||||
// fall back to HTML scraping (which gets the password form, also a 401-ish
|
// fall back to HTML scraping (which gets the password form, also a 401-ish
|
||||||
// signal but more expensive to parse and less stable).
|
// signal but more expensive to parse and less stable).
|
||||||
if (!page || !isAuthorised(page, request)) {
|
if (!page || !isAuthorised(page, request)) return jsonNotFound();
|
||||||
return new Response(JSON.stringify({ error: "not found" }), { status: 404, headers: { "content-type": "application/json" } });
|
|
||||||
}
|
|
||||||
const cacheKey = `payload:${slug}:${win ?? page.default_window}`;
|
const cacheKey = `payload:${slug}:${win ?? page.default_window}`;
|
||||||
const payload = await cached(cacheKey, 15, () => loadPagePayload(slug, win));
|
const payload = await cached(cacheKey, 15, () => loadPagePayload(slug, win));
|
||||||
if (!payload) return new Response(JSON.stringify({ error: "not found" }), { status: 404, headers: { "content-type": "application/json" } });
|
if (!payload) return jsonNotFound();
|
||||||
return new Response(JSON.stringify(payload), {
|
return new Response(JSON.stringify(payload), {
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
|
@ -190,29 +217,13 @@ const app = new Elysia()
|
||||||
// monitor id (8 hex bytes). Match the page existence 404 so unauthenticated
|
// monitor id (8 hex bytes). Match the page existence 404 so unauthenticated
|
||||||
// requests can't even tell whether a slug is gated.
|
// requests can't even tell whether a slug is gated.
|
||||||
const page = await loadStatusPage(params.slug);
|
const page = await loadStatusPage(params.slug);
|
||||||
if (!page) {
|
if (!page || !isAuthorised(page, request)) return jsonNotFound();
|
||||||
return new Response(JSON.stringify({ error: "not found" }), {
|
|
||||||
status: 404,
|
|
||||||
headers: { "content-type": "application/json" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!isAuthorised(page, request)) {
|
|
||||||
return new Response(JSON.stringify({ error: "not found" }), {
|
|
||||||
status: 404,
|
|
||||||
headers: { "content-type": "application/json" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const idWithExt = params.idWithExt;
|
const idWithExt = params.idWithExt;
|
||||||
const monitorId = idWithExt.endsWith(".json") ? idWithExt.slice(0, -5) : idWithExt;
|
const monitorId = idWithExt.endsWith(".json") ? idWithExt.slice(0, -5) : idWithExt;
|
||||||
const win = (query as any)?.window as Window | undefined;
|
const win = (query as any)?.window as Window | undefined;
|
||||||
const cacheKey = `monitor:${params.slug}:${monitorId}:${win ?? ''}`;
|
const cacheKey = `monitor:${params.slug}:${monitorId}:${win ?? ''}`;
|
||||||
const payload = await cached(cacheKey, 15, () => loadMonitorDetail(params.slug, monitorId, win));
|
const payload = await cached(cacheKey, 15, () => loadMonitorDetail(params.slug, monitorId, win));
|
||||||
if (!payload) {
|
if (!payload) return jsonNotFound();
|
||||||
return new Response(JSON.stringify({ error: "not found" }), {
|
|
||||||
status: 404,
|
|
||||||
headers: { "content-type": "application/json" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return new Response(JSON.stringify(payload), {
|
return new Response(JSON.stringify(payload), {
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue