mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-06 06:52:49 +08:00
fix(guest): guard reserved __guest__ principal in user mgmt
The by-id user-management handlers use findById, which has no role filter, so supplying the synthetic GUEST_USER_ID let an admin delete, re-role, reset-password, and read/write permissions on the shared guest principal (privilege-escalation / DoS / credential-login holes). - web/api/users.ts: 404-guard every :id handler against GUEST_USER_ID (DELETE, reset-password, role, GET/PUT permissions). - data/users.ts: defense-in-depth — setRoleIfNotLastAdmin and deleteUserIfNotLastAdmin return "not_found" for any role=guest row. - web/api/session.ts: wrap POST /guest createSession in try/catch so a missing guest row yields 503 instead of an unhandled 500. - Tests: data-layer guest-protection + users-router 404 by-id guards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
45414b3baa
commit
365352cdd3
5 files changed
+79
-3
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { Router } from "express";
|
||||
import type { Logger } from "../../logger.js";
|
||||
import type { UserStore } from "../../data/users.js";
|
||||
import { UsernameTakenError } from "../../data/users.js";
|
||||
import { UsernameTakenError, GUEST_USER_ID } from "../../data/users.js";
|
||||
import type { SessionStore } from "../../data/sessions.js";
|
||||
import type { AuditStore } from "../../data/audit.js";
|
||||
import { isCapability, BASIC_TIER_CAPABILITIES, type PermissionStore } from "../../data/permissions.js";
|
||||
@@ -63,6 +63,7 @@ export function createUsersRouter(
|
||||
|
||||
router.delete("/:id", (req, res) => {
|
||||
const targetId = req.params.id;
|
||||
if (targetId === GUEST_USER_ID) { res.status(404).json({ error: "not found" }); return; }
|
||||
// Snapshot target's username BEFORE deletion for audit
|
||||
const target = users.findById(targetId);
|
||||
if (!target) {
|
||||
@@ -104,6 +105,7 @@ export function createUsersRouter(
|
||||
return;
|
||||
}
|
||||
const targetId = req.params.id;
|
||||
if (targetId === GUEST_USER_ID) { res.status(404).json({ error: "not found" }); return; }
|
||||
const target = users.findById(targetId);
|
||||
if (!target) {
|
||||
res.status(404).json({ error: "not found" });
|
||||
@@ -130,6 +132,7 @@ export function createUsersRouter(
|
||||
|
||||
router.patch("/:id/role", (req, res) => {
|
||||
const targetId = req.params.id;
|
||||
if (targetId === GUEST_USER_ID) { res.status(404).json({ error: "not found" }); return; }
|
||||
const { role: newRole } = req.body ?? {};
|
||||
if (newRole !== "admin" && newRole !== "member") {
|
||||
res.status(400).json({ error: "invalid role" });
|
||||
@@ -168,6 +171,7 @@ export function createUsersRouter(
|
||||
});
|
||||
|
||||
router.get("/:id/permissions", (req, res) => {
|
||||
if (req.params.id === GUEST_USER_ID) { res.status(404).json({ error: "not found" }); return; }
|
||||
const user = users.findById(req.params.id);
|
||||
if (!user) {
|
||||
res.status(404).json({ error: "not_found" });
|
||||
@@ -180,6 +184,7 @@ export function createUsersRouter(
|
||||
});
|
||||
|
||||
router.put("/:id/permissions", (req, res) => {
|
||||
if (req.params.id === GUEST_USER_ID) { res.status(404).json({ error: "not found" }); return; }
|
||||
const user = users.findById(req.params.id);
|
||||
if (!user) {
|
||||
res.status(404).json({ error: "not_found" });
|
||||
|
||||
Reference in new issue
Block a user