Spaces:
Running
Running
File size: 5,612 Bytes
002f18c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | #!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const EXPECTED_RATE_LIMITS = new Map([
["API_PREAUTH_RATE_LIMITER", { limit: 20, period: 60 }],
["API_RATE_LIMITER", { limit: 60, period: 60 }],
]);
export class DeploymentPreflightError extends Error {
constructor(message) {
super(message);
this.name = "DeploymentPreflightError";
}
}
export function validateDeploymentEnvironment(env = {}) {
const accessSecret = env.AUREON_WORKER_ACCESS_SECRET;
if (
typeof accessSecret !== "string"
|| !accessSecret
|| accessSecret !== accessSecret.trim()
|| Buffer.byteLength(accessSecret, "utf8") < 32
) {
throw new DeploymentPreflightError(
"AUREON_WORKER_ACCESS_SECRET must be trimmed and at least 32 UTF-8 bytes.",
);
}
const rawOrigins = env.AUREON_ALLOWED_ORIGINS;
if (typeof rawOrigins !== "string" || !rawOrigins) {
throw new DeploymentPreflightError(
"AUREON_ALLOWED_ORIGINS must contain at least one exact HTTPS origin.",
);
}
const origins = rawOrigins.split(",");
const uniqueOrigins = new Set();
for (let index = 0; index < origins.length; index += 1) {
const candidate = origins[index];
if (!candidate || candidate !== candidate.trim() || candidate === "*") {
throw new DeploymentPreflightError(
`AUREON_ALLOWED_ORIGINS entry ${index + 1} is not an exact HTTPS origin.`,
);
}
try {
const parsed = new URL(candidate);
if (parsed.protocol !== "https:" || parsed.origin !== candidate) {
throw new Error("not canonical");
}
} catch {
throw new DeploymentPreflightError(
`AUREON_ALLOWED_ORIGINS entry ${index + 1} is not an exact HTTPS origin.`,
);
}
if (uniqueOrigins.has(candidate)) {
throw new DeploymentPreflightError(
`AUREON_ALLOWED_ORIGINS entry ${index + 1} is duplicated.`,
);
}
uniqueOrigins.add(candidate);
}
const paidProviders = env.AUREON_ALLOW_PAID_PROVIDERS;
if (paidProviders !== undefined && paidProviders !== "true") {
throw new DeploymentPreflightError(
"AUREON_ALLOW_PAID_PROVIDERS must be omitted or exactly true.",
);
}
return {
originCount: uniqueOrigins.size,
paidProvidersEnabled: paidProviders === "true",
};
}
export function validateWranglerRateLimits(config) {
if (!config || typeof config !== "object" || Array.isArray(config)) {
throw new DeploymentPreflightError("Wrangler configuration must be an object.");
}
if (
config.vars
&& Object.prototype.hasOwnProperty.call(config.vars, "AUREON_WORKER_ACCESS_SECRET")
) {
throw new DeploymentPreflightError(
"AUREON_WORKER_ACCESS_SECRET must be a provider secret, not a Wrangler vars value.",
);
}
if (!Array.isArray(config.ratelimits)) {
throw new DeploymentPreflightError("Wrangler rate-limit bindings are missing.");
}
for (const [name, expected] of EXPECTED_RATE_LIMITS) {
const matches = config.ratelimits.filter((entry) => entry?.name === name);
if (matches.length !== 1) {
throw new DeploymentPreflightError(
`Wrangler must define exactly one ${name} rate-limit binding.`,
);
}
const binding = matches[0];
if (typeof binding.namespace_id !== "string" || !binding.namespace_id.trim()) {
throw new DeploymentPreflightError(`${name} must have a namespace_id.`);
}
if (
binding.simple?.limit !== expected.limit
|| binding.simple?.period !== expected.period
) {
throw new DeploymentPreflightError(
`${name} must use limit ${expected.limit} and period ${expected.period}.`,
);
}
}
return { bindingCount: EXPECTED_RATE_LIMITS.size };
}
export function loadWranglerConfig(configPath) {
let source;
try {
source = fs.readFileSync(configPath, "utf8").replace(/^\uFEFF/, "");
} catch {
throw new DeploymentPreflightError("Wrangler configuration could not be read.");
}
try {
return JSON.parse(source);
} catch {
throw new DeploymentPreflightError(
"Wrangler configuration must remain JSON-compatible JSONC for offline validation.",
);
}
}
export function runPreflight({ env = process.env, configPath } = {}) {
const environment = validateDeploymentEnvironment(env);
const rateLimits = validateWranglerRateLimits(loadWranglerConfig(configPath));
return { ...environment, ...rateLimits };
}
const modulePath = fileURLToPath(import.meta.url);
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : null;
if (invokedPath && pathToFileURL(invokedPath).href === import.meta.url) {
const defaultConfigPath = path.resolve(path.dirname(modulePath), "..", "wrangler.jsonc");
try {
const result = runPreflight({
env: process.env,
configPath: process.argv[2] ? path.resolve(process.argv[2]) : defaultConfigPath,
});
console.log(
`Cloudflare deployment security preflight passed: ${result.originCount} origin(s), `
+ `${result.bindingCount} rate-limit binding(s), paid providers `
+ `${result.paidProvidersEnabled ? "enabled" : "disabled"}.`,
);
} catch (error) {
const message = error instanceof DeploymentPreflightError
? error.message
: "unexpected validation error";
console.error(`Cloudflare deployment security preflight failed: ${message}`);
process.exitCode = 1;
}
}
|