Files
EpicNext-Cms/src/lib/admin/rank-guard.test.ts
T
openhands 7b249a7222
CI / check (push) Canceled after 0s
CI / tests-unit (push) Canceled after 0s
CI / tests-integration (push) Canceled after 0s
CI / tests-ui (push) Canceled after 0s
CI / preflight (push) Canceled after 0s
CI / deploy (push) Canceled after 0s
Gitea Runner Clean Test / test-job (push) Canceled after 0s
Gitea Actions Runner Test / test-job (push) Canceled after 0s
P1-P3 admin cleanup: rank guard, SSRF guard, count caching, dead code removal, pagination rewrite
- P1: new src/lib/admin/rank-guard.ts with tryGuardRank/guardRank + RankGuardTarget/Result; extracted from users.ts; applied to admin-bans (create/lift + rank redirect) and admin-badges (caps + rank guard + error surfacing); import-url-safety.ts SSRF guard wired into clone save+icon fetch; logAudit await in help-tickets; antiddos mutations SETTINGS_EDIT; markAllAlertsRead NOTIFICATIONS_EDIT; rankError/saveError in all 25 message files; tests rew/new for bans/badges/rank-guard/import-url-safety

- P2: ticket-inbox merged pagination reworked to single UNION ALL SQL with LIMIT/OFFSET (avoids O(offset) materialization); getCachedAdminCount with cacheFiltered option applied to devops/errors/photos/items/guilds/help-tickets/moderation pages; admin-theme catches→?error=1+banner+25 locales; logger in silent catches (rare-values/writeable-boxes); catalog-items-loader CAST→plain equality; count caching helper extended with opt-in cacheFiltered

- P3: removed dead modules is-staff/notice/recent-furni-resync/log-filters plus load-user-sanctions/user-sanction-timeline; removed orphaned CATALOG_PAGE_COLUMNS re-export; removed getFurniIconUrl from studio-furni-icons

Build: lint 0, typecheck 0, 3480 tests pass, i18n:check exit 0, colors exit 0. Circular chunk warning (9452/10535/66463) is webpack code-splitting artifact; build succeeds.
2026-10-12 17:42:19 +02:00

83 lines
2.5 KiB
TypeScript

// @ts-nocheck
import { beforeEach, describe, expect, it, vi } from "vitest";
const state = vi.hoisted(() => ({
users: [] as Array<{ username: string; rank: number; mail: string | null }>,
highestRank: 8,
}));
vi.mock("@/lib/db", async () => {
const schema = await import("@/db/schema");
const { createFakeDb } = await import("@/test/fake-db");
const fake = createFakeDb((_table, projection) =>
"username" in projection ? state.users : [],
);
return { ...schema, db: fake };
});
vi.mock("@/lib/permissions", () => ({
getHighestRank: vi.fn(async () => state.highestRank),
}));
vi.mock("@/lib/safe-action-shared", () => ({
ActionError: class ActionError extends Error {},
}));
import { getHighestRank } from "@/lib/permissions";
import { guardRank, tryGuardRank } from "./rank-guard";
beforeEach(() => {
vi.clearAllMocks();
state.users = [{ username: "victim", rank: 5, mail: "[email protected]" }];
state.highestRank = 8;
});
describe("tryGuardRank", () => {
it("allows an actor at a strictly higher rank", async () => {
const result = await tryGuardRank(1, 7);
expect(result).toEqual({ ok: true, target: state.users[0] });
});
it("blocks an actor at the same rank", async () => {
state.users = [{ username: "victim", rank: 7, mail: null }];
const result = await tryGuardRank(1, 7);
expect(result).toEqual({ ok: false, reason: "rank" });
});
it("blocks an actor at a lower rank", async () => {
const result = await tryGuardRank(1, 3);
expect(result).toEqual({ ok: false, reason: "rank" });
});
it("lets the dynamic super admin act on anyone", async () => {
state.users = [{ username: "victim", rank: 8, mail: "[email protected]" }];
state.highestRank = 8;
const result = await tryGuardRank(1, 8);
expect(result).toEqual({
ok: true,
target: { username: "victim", rank: 8, mail: "[email protected]" },
});
expect(await getHighestRank()).toBe(8);
});
it("reports a missing target as not_found", async () => {
state.users = [];
const result = await tryGuardRank(999, 7);
expect(result).toEqual({ ok: false, reason: "not_found" });
});
});
describe("guardRank", () => {
it("throws a readable ActionError for a target at equal or higher rank", async () => {
state.users = [{ username: "victim", rank: 7, mail: null }];
await expect(guardRank(1, 7)).rejects.toThrow(
"Cannot modify user with equal or higher rank",
);
});
it("throws a readable ActionError for a missing target", async () => {
state.users = [];
await expect(guardRank(999, 7)).rejects.toThrow("User not found");
});
});