fix(audit): M24 — reserve 'branding'/'avatar' file categories from the upload/update API
The public file-stream gate keys off files.category==='branding'; the API upload/update schemas now reject the reserved categories so a user can't self-set branding to publicly expose their own file. System writers (admin image, avatar) set them via the service directly and are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -173,6 +173,23 @@ export async function getPreviewUrl(id: string, portId: string) {
|
||||
|
||||
// ─── Update ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Categories that gate system surfaces and must never be settable through the
|
||||
* general (validated) update path. `branding` is the public-stream gate on
|
||||
* `/api/public/files/[id]`; `avatar` is system-managed. The upload/update Zod
|
||||
* schemas (`userFileCategorySchema`) already reject these, so this is
|
||||
* belt-and-suspenders for any future non-HTTP caller (M24).
|
||||
*
|
||||
* NB: `uploadFile` is intentionally NOT guarded — the admin branding writer
|
||||
* (`admin/settings/image` route) and the avatar writer (`me/avatar` route)
|
||||
* legitimately call the service with `category: 'branding' | 'avatar'`,
|
||||
* constructing the payload inline and bypassing the Zod schema. Guarding the
|
||||
* upload service would break those legitimate system writers. `updateFile`,
|
||||
* by contrast, has a single schema-validated caller and no legitimate
|
||||
* reserved-category use, so it is safe to harden here.
|
||||
*/
|
||||
const RESERVED_FILE_CATEGORIES = new Set(['branding', 'avatar']);
|
||||
|
||||
export async function updateFile(
|
||||
id: string,
|
||||
portId: string,
|
||||
@@ -181,6 +198,10 @@ export async function updateFile(
|
||||
) {
|
||||
const existing = await getFileById(id, portId);
|
||||
|
||||
if (data.category !== undefined && RESERVED_FILE_CATEGORIES.has(data.category)) {
|
||||
throw new ValidationError(`Category '${data.category}' is reserved and cannot be set`);
|
||||
}
|
||||
|
||||
const updates: { filename?: string; category?: string } = {};
|
||||
if (data.filename !== undefined) updates.filename = sanitizeFilename(data.filename);
|
||||
if (data.category !== undefined) updates.category = data.category;
|
||||
|
||||
Reference in New Issue
Block a user