fix(api/v1): scope category lookup to event_owned or global

PR review pointed out the original lookup
  db('photo_categories').where({ id: parsedCategoryId }).first()
accepted any category id — including one that belongs to a different
event. photo_categories carries both event_id (per-event) and is_global
(see backend/migrations/legacy/004_add_categories_and_cms.js); the v1
upload route should require either match.

Not a privilege issue (apiTokenAuth.js inherits the admin's powers, no
per-event scoping), but it lets a misconfigured uploader silently file
photos under a category the target event doesn't own — and the 201 echo
includes a category_id that makes no semantic sense.

Tighten to:
  .where({ id: parsedCategoryId })
  .andWhere(function () {
    this.where({ event_id: event.id }).orWhere('is_global', true);
  })
…and update the 400 message to "Unknown or out-of-scope category_id N".

OpenAPI description already documents the intended scope.

Tests deferred to a follow-up; v1 has no jest harness today, see PR
discussion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marian
2026-05-16 18:20:41 +00:00
parent 6901e2661e
commit 92bb9e1a12
+15 -2
View File
@@ -385,9 +385,22 @@ router.post(
let categoryId = null;
let photoType = 'individual';
if (!Number.isNaN(parsedCategoryId)) {
const category = await db('photo_categories').where({ id: parsedCategoryId }).first();
// Scope to categories owned by this event (event_id = event.id) or
// marked global (is_global = true) — see migration
// backend/migrations/legacy/004_add_categories_and_cms.js. An API
// token inherits its owning admin's powers (no per-event scoping
// in apiTokenAuth), so accepting any category_id would silently
// mis-file uploads under a category belonging to a different event.
const category = await db('photo_categories')
.where({ id: parsedCategoryId })
.andWhere(function () {
this.where({ event_id: event.id }).orWhere('is_global', true);
})
.first();
if (!category) {
return res.status(400).json({ error: `Unknown category_id ${parsedCategoryId}` });
return res.status(400).json({
error: `Unknown or out-of-scope category_id ${parsedCategoryId}`,
});
}
categoryId = category.id;
if (category.slug === 'collage' || category.slug === 'collages') {