fix(guest): normalize guestMode config on load + strict-boolean authorize gate

loadConfig now sanitizes guestMode the same way the write path does: bots is
coerced to "all" | string[] (numbers/objects/missing fall back to the default
"all"), and permissions are rebuilt from defaults with each known flag
strict-coerced to a boolean so a hand-edited/legacy/corrupt config.json can no
longer crash the gate or leak garbage index keys. The authorize guest gate now
uses === true instead of a truthy check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.8 committed 2026-06-25 15:59:42 +08:00
1 parent 952f1fbad3
commit 66230e6b43
4 files changed
+84 -9

No files matched your search

+33 -8
View File
@@ -1,6 +1,7 @@
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync, rmSync } from "node:fs";
import { dirname } from "node:path";
import type { BotAccess, GuestPermissions } from "./permissions.js";
import { GUEST_PERMISSION_FLAGS } from "./permissions.js";
export interface GuestModeConfig {
enabled: boolean;
@@ -72,17 +73,41 @@ export function loadConfig(path: string): BotConfig {
try {
const raw = readFileSync(path, "utf-8");
const partial = JSON.parse(raw) as Partial<BotConfig>;
// Normalize/sanitize guestMode on load. The WRITE path (POST /api/bot/settings)
// sanitizes too, but a hand-edited/legacy/corrupt config.json reaches the gate
// directly — so coerce it here as well, mirroring that write-path logic.
const partialGm = (partial.guestMode ?? {}) as Partial<GuestModeConfig>;
const gm: GuestModeConfig = {
...defaults.guestMode,
...partialGm,
// bots → "all" | string[]; anything else falls back to the default ("all").
bots:
partialGm.bots === "all"
? "all"
: Array.isArray(partialGm.bots)
? partialGm.bots.filter((id): id is string => typeof id === "string")
: defaults.guestMode.bots,
// permissions → defaults, then spread ONLY a plain object, then strict-coerce
// each known flag to a boolean (drops index keys + non-boolean values).
permissions: { ...defaults.guestMode.permissions },
};
const partialPerms = partialGm.permissions;
if (
partialPerms !== null &&
typeof partialPerms === "object" &&
!Array.isArray(partialPerms)
) {
Object.assign(gm.permissions, partialPerms);
}
for (const f of GUEST_PERMISSION_FLAGS) {
gm.permissions[f] = gm.permissions[f] === true;
}
return {
...defaults,
...partial,
guestMode: {
...defaults.guestMode,
...(partial.guestMode ?? {}),
permissions: {
...defaults.guestMode.permissions,
...(partial.guestMode?.permissions ?? {}),
},
},
guestMode: gm,
};
} catch {
return defaults;