61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const ACTION_GATES: Array<[string, string]> = [
|
|
["admin-bans.ts", "PERMS.USERS_BAN"],
|
|
["admin-wordfilter.ts", "PERMS.WORDFILTER_EDIT"],
|
|
["admin-articles.ts", "PERMS.NEWS_EDIT"],
|
|
["admin-radio-autodj.ts", "PERMS.RADIO_EDIT"],
|
|
["catalog.ts", "PERMS.CATALOG_EDIT"],
|
|
["rooms.ts", "PERMS.ROOMS_EDIT"],
|
|
["admin-vpn.ts", "PERMS.SETTINGS_EDIT"],
|
|
["admin-ads.ts", "PERMS.PAGES_EDIT"],
|
|
["admin-alerts.ts", "PERMS.NOTIFICATIONS_EDIT"],
|
|
["tickets.ts", "PERMS.TICKETS_EDIT"],
|
|
["admin-help-tickets.ts", "PERMS.TICKETS_EDIT"],
|
|
["permissions.ts", "PERMS.PERMISSIONS_MANAGE"],
|
|
["permissions.ts", "repairAdminNavAclGrants"],
|
|
["admin-guilds.ts", "PERMS.USERS_EDIT"],
|
|
["moderation.ts", "PERMS.MODERATION_EDIT"],
|
|
];
|
|
|
|
describe("administration backend contract", () => {
|
|
it.each([
|
|
["analytics/export", "PERMS.ANALYTICS_EXPORT"],
|
|
["devops/health", "PERMS.DEVOPS_VIEW"],
|
|
["users/actions", "PERMS.USERS_EDIT"],
|
|
])("provides and guards /api/admin/%s", (route, permission) => {
|
|
const path = `src/app/api/admin/${route}/route.ts`;
|
|
expect(existsSync(path), path).toBe(true);
|
|
expect(readFileSync(path, "utf8"), path).toContain(permission);
|
|
});
|
|
|
|
it.each(ACTION_GATES)("guards %s with %s", (file, permission) => {
|
|
const source = readFileSync(`src/actions/${file}`, "utf8");
|
|
expect(source).toContain(permission);
|
|
expect(source).not.toMatch(/await requireStaff\(\)/);
|
|
expect(source).not.toMatch(/await requireStaffRateLimited\(\)/);
|
|
});
|
|
|
|
it("requires explicit permissions for every administration action", () => {
|
|
const dir = "src/actions";
|
|
const offenders: string[] = [];
|
|
for (const name of readdirSync(dir)) {
|
|
if (!name.endsWith(".ts") || name.endsWith(".test.ts")) continue;
|
|
const source = readFileSync(join(dir, name), "utf8");
|
|
if (/await requireStaff(RateLimited)?\(\)/.test(source)) {
|
|
offenders.push(name);
|
|
}
|
|
}
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
|
|
it("keeps the shared ops health probe behind the API", () => {
|
|
expect(existsSync("src/lib/admin/ops-health.ts")).toBe(true);
|
|
expect(
|
|
readFileSync("src/app/api/admin/devops/health/route.ts", "utf8"),
|
|
).toContain("fetchOpsHealth");
|
|
});
|
|
});
|