fix: sanitize adminGroups on config load + final-review cleanups

This commit is contained in:
saopig1 committed 2026-06-26 21:12:56 +08:00
1 parent b387d6581e
commit e104093614
4 files changed
+39 -3

No files matched your search

+1 -1
View File
@@ -360,7 +360,7 @@ sudo systemctl start tsmusicbot
- 网页端:设置 → 命令权限,填写允许的服务器组 ID(逗号分隔),保存即时生效。 - 网页端:设置 → 命令权限,填写允许的服务器组 ID(逗号分隔),保存即时生效。
- 或编辑 `config.json` 的 `adminGroups`(数字数组),例如 `"adminGroups": [6, 8]`。 - 或编辑 `config.json` 的 `adminGroups`(数字数组),例如 `"adminGroups": [6, 8]`。
填入任意服务器组 ID 后,限制立即开启:只有属于这些组之一的用户才能运行受限命令,其他人会收到「⛔ 需要管理员权限」的提示。 填入任意服务器组 ID 后,限制立即开启:只有属于这些组之一的用户才能运行受限命令,其他人会收到「⛔ 需要管理员权限(该命令仅限管理员服务器组)」的提示。
> 提示(fail-closed):当受限命令来自一个机器人当前看不到其服务器组的发送者(例如不在机器人所在频道的私聊),机器人会尝试查询其分组;若仍无法确定,则拒绝执行。 > 提示(fail-closed):当受限命令来自一个机器人当前看不到其服务器组的发送者(例如不在机器人所在频道的私聊),机器人会尝试查询其分组;若仍无法确定,则拒绝执行。
+1 -2
View File
@@ -1,6 +1,5 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { parseCommand } from "./commands.js"; import { parseCommand, canRunCommand, isAdminCommand } from "./commands.js";
import { canRunCommand, isAdminCommand } from "./commands.js";
describe("Command Parser", () => { describe("Command Parser", () => {
it("parses simple command", () => { it("parses simple command", () => {
+26
View File
@@ -177,3 +177,29 @@ describe("guestMode config", () => {
}); });
}); });
}); });
describe("adminGroups normalization", () => {
function loadAdminGroups(raw: unknown) {
const dir = mkdtempSync(join(tmpdir(), "tsmb-cfg-"));
const p = join(dir, "config.json");
writeFileSync(p, JSON.stringify(raw));
try {
return loadConfig(p).adminGroups;
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
it("defaults to [] when absent", () => {
expect(loadAdminGroups({})).toEqual([]);
});
it("keeps valid non-negative integers", () => {
expect(loadAdminGroups({ adminGroups: [6, 8] })).toEqual([6, 8]);
});
it("filters out negatives, non-integers and non-numbers", () => {
expect(loadAdminGroups({ adminGroups: [6, -1, 2.5, "8", null] })).toEqual([6]);
});
it("a non-array value falls back to the default [] (no crash)", () => {
expect(loadAdminGroups({ adminGroups: "6" })).toEqual([]);
});
});
+11
View File
@@ -104,9 +104,20 @@ export function loadConfig(path: string): BotConfig {
gm.permissions[f] = gm.permissions[f] === true; gm.permissions[f] = gm.permissions[f] === true;
} }
// Sanitize adminGroups on load too: the WebUI write path filters it, but a
// hand-edited / legacy / corrupt config.json reaches the command gate
// directly. Keep only non-negative integers; a non-array falls back to the
// default []. Mirrors the guestMode sanitization above.
const adminGroups = Array.isArray(partial.adminGroups)
? partial.adminGroups.filter(
(g): g is number => typeof g === "number" && Number.isInteger(g) && g >= 0,
)
: defaults.adminGroups;
return { return {
...defaults, ...defaults,
...partial, ...partial,
adminGroups,
guestMode: gm, guestMode: gm,
}; };
} catch { } catch {