fix: require genuine live hosting evidence

This commit is contained in:
donghyeon-ka
2026-07-25 22:25:04 +09:00
parent 4667cafa43
commit c089e749d0
4 changed files with 160 additions and 12 deletions
+61 -11
View File
@@ -1,5 +1,7 @@
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { classifyLiveHostingBaseUrl } from "./lib/hosting-probe.mjs";
const cachePolicy = JSON.parse(
await readFile("config/hosting/cache-policy.json", "utf8"),
);
@@ -7,6 +9,7 @@ const securityPolicy = JSON.parse(
await readFile("config/hosting/security-headers.json", "utf8"),
);
const baseUrl = process.env.HOSTING_BASE_URL;
const liveTarget = baseUrl ? classifyLiveHostingBaseUrl(baseUrl) : null;
const distFiles = (await readdir("dist", { recursive: true })).map(String);
const publicSourceMaps = distFiles.filter((file) => file.endsWith(".map"));
const publicServiceWorkers = distFiles.filter((file) =>
@@ -14,10 +17,19 @@ const publicServiceWorkers = distFiles.filter((file) =>
);
/** @type {Record<string, Record<string, string>>} */
let responses;
let responses = {};
let mode;
/** @type {Array<{
* surface: string;
* header: string;
* expected: unknown;
* observed: unknown;
* reason?: string;
* passed: boolean;
* }>} */
const probeResults = [];
if (baseUrl) {
if (liveTarget?.passed) {
mode = "live";
const assets = await readdir("dist/assets");
const hashedJavaScript = assets.find((file) => file.endsWith(".js"));
@@ -30,14 +42,52 @@ if (baseUrl) {
};
responses = {};
for (const [surface, pathname] of Object.entries(paths)) {
const response = await fetch(new URL(pathname, baseUrl));
responses[surface] = Object.fromEntries(
[...response.headers.entries()].map(([name, value]) => [
name.toLowerCase(),
value,
]),
);
const requestedUrl = new URL(pathname, liveTarget.url);
try {
const response = await fetch(requestedUrl, { redirect: "follow" });
const finalUrl = new URL(response.url);
probeResults.push(
{
surface,
header: "http-status",
expected: 200,
observed: response.status,
passed: response.status === 200,
},
{
surface,
header: "final-origin",
expected: liveTarget.url.origin,
observed: finalUrl.origin,
passed: finalUrl.origin === liveTarget.url.origin,
},
);
responses[surface] = Object.fromEntries(
[...response.headers.entries()].map(([name, value]) => [
name.toLowerCase(),
value,
]),
);
} catch (error) {
probeResults.push({
surface,
header: "transport",
expected: "reachable",
observed: error instanceof Error ? error.name : "UnknownError",
passed: false,
});
}
}
} else if (liveTarget) {
mode = "invalid-live";
probeResults.push({
surface: "deployment",
header: "base-url",
expected: "canonical non-loopback HTTPS root URL",
observed: liveTarget.observedOrigin,
reason: liveTarget.reason,
passed: false,
});
} else {
mode = "fixture";
responses = JSON.parse(
@@ -45,7 +95,7 @@ if (baseUrl) {
).responses;
}
const results = [];
const results = [...probeResults];
for (const [surface, policy] of Object.entries(cachePolicy.surfaces)) {
if (!("cacheControl" in policy)) continue;
const observed = responses[surface]?.["cache-control"];
@@ -110,7 +160,7 @@ await writeFile(
schemaVersion: 1,
generatedAt: new Date().toISOString(),
mode,
baseUrl: baseUrl ?? null,
baseUrl: liveTarget?.observedOrigin ?? null,
providerVerificationRequired: mode !== "live",
results,
passed,