统一 Node 22 与后端安全运行基线(07-15-production-trust-closeout-cleanup)
这个提交包含在:
@@ -0,0 +1,81 @@
|
||||
require("dotenv/config");
|
||||
|
||||
const { Client } = require("pg");
|
||||
|
||||
const LOCAL_DATABASE_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
||||
|
||||
class DevSeedSafetyError extends Error {
|
||||
constructor(code, message) {
|
||||
super(message);
|
||||
this.name = "DevSeedSafetyError";
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function refuse(code, message) {
|
||||
throw new DevSeedSafetyError(code, message);
|
||||
}
|
||||
|
||||
function validateDevSeedSafety(env = process.env) {
|
||||
if (String(env.NODE_ENV || "").trim().toLowerCase() === "production") {
|
||||
refuse("production_forbidden", "seed:dev is permanently disabled in production");
|
||||
}
|
||||
if (String(env.SEED_DEV_ALLOW || "").trim() !== "1") {
|
||||
refuse("explicit_allow_required", "seed:dev requires the explicit local allow switch");
|
||||
}
|
||||
|
||||
let databaseUrl;
|
||||
try {
|
||||
databaseUrl = new URL(String(env.DATABASE_URL || ""));
|
||||
} catch {
|
||||
refuse("database_url_invalid", "seed:dev requires a valid local PostgreSQL target");
|
||||
}
|
||||
if (!["postgres:", "postgresql:"].includes(databaseUrl.protocol)) {
|
||||
refuse("database_protocol_invalid", "seed:dev requires a local PostgreSQL target");
|
||||
}
|
||||
|
||||
let effectiveTarget;
|
||||
try {
|
||||
const client = new Client({ connectionString: databaseUrl.toString() });
|
||||
effectiveTarget = {
|
||||
host: String(client.host || "").toLowerCase(),
|
||||
databaseName: String(client.database || "").trim(),
|
||||
};
|
||||
} catch {
|
||||
refuse("database_url_invalid", "seed:dev requires a valid local PostgreSQL target");
|
||||
}
|
||||
if (!LOCAL_DATABASE_HOSTS.has(effectiveTarget.host)) {
|
||||
refuse("database_host_not_local", "seed:dev only accepts a loopback database target");
|
||||
}
|
||||
|
||||
const databaseName = decodeURIComponent(databaseUrl.pathname.replace(/^\/+/, "")).trim();
|
||||
if (!databaseName || databaseName.includes("/") || effectiveTarget.databaseName !== databaseName) {
|
||||
refuse("database_name_invalid", "seed:dev requires one explicit database name");
|
||||
}
|
||||
const confirmation = String(env.SEED_DEV_DATABASE_CONFIRM || "").trim();
|
||||
if (!confirmation || confirmation !== databaseName) {
|
||||
refuse("database_confirmation_mismatch", "seed:dev database confirmation does not match the target");
|
||||
}
|
||||
|
||||
return {
|
||||
databaseName,
|
||||
resetPasswords: String(env.SEED_DEV_RESET_PASSWORDS || "").trim() === "1",
|
||||
};
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
try {
|
||||
const result = validateDevSeedSafety();
|
||||
const passwordMode = result.resetPasswords ? "one_time_reset" : "preserve_existing";
|
||||
console.log(`[seed-dev] safety check passed; passwordMode=${passwordMode}`);
|
||||
} catch (error) {
|
||||
const code = error instanceof DevSeedSafetyError ? error.code : "unexpected_error";
|
||||
console.error(`[seed-dev][REFUSED] ${code}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DevSeedSafetyError,
|
||||
validateDevSeedSafety,
|
||||
};
|
||||
在新工单中引用
屏蔽一个用户