Files
TX/artifacts/api-server/tests
riyadhafraa 553aec1256 Task #524: Fix critical/high object-storage authorization findings
Scope: MR-H1, MR-H2, MR-M7 from .local/security/manual-review.md.

Changes:
- New lib/objectAuthz.ts: canUserReadObjectPath(userId, objectPath)
  performs an entity-lookup (avatar / app icon / service image / brand
  logo / pdf archive / meeting attachment) and applies the matching
  read role. Admin override; orphan paths denied.
- routes/storage.ts: GET /api/storage/objects/* now calls
  canUserReadObjectPath BEFORE getObjectEntityFile and returns 404
  on deny so existence is not leaked (MR-H1 fix).
- routes/executive-meetings.ts: POST /executive-meetings/pdf-archives
  now stacks requireMutate on top of requireExecutiveAccess so
  read-only executive_viewer can no longer poison the archive list.
  pdfArchiveCreateSchema's filePath is constrained to OBJECT_PATH_RE
  (/objects/<id>) when supplied; omitted = synthetic print:<date>
  preserved (MR-H2 fix).
- lib/objectAcl.ts: removed the empty enum + throwing factory that
  formed the MR-M7 trap. Kept getObjectAclPolicy / setObjectAclPolicy
  / ObjectAclPolicy / ObjectPermission so objectStorage.ts compiles.
  canAccessObject is now a deny-all shim with @deprecated pointer.

Tests added (artifacts/api-server/tests/storage-object-authz.test.mjs):
1. Unauthenticated GET /api/storage/objects/* -> 401
2. Authed user GET orphan path -> 404
3. POST /pdf-archives by executive_viewer -> 403
4. POST /pdf-archives with free-form filePath -> 400
5. POST /pdf-archives by mutator with no filePath -> 201 (regression)
6. GET /pdf-archives by executive_viewer -> 200 (regression)

Test results: all 6 new tests pass; 318/320 of the full api-server
suite pass. The 2 failures (app-permissions-impact preview math,
executive-meetings-notifications socket fan-out) are pre-existing and
unrelated to the touched files.

Residual risk: meeting-attachment lookup uses
attachments::text LIKE '%<path>%' because the jsonb shape is loosely
typed; safe in practice (random UUID paths) but a stricter jsonpath
query could be used in a future hardening pass.

Out of scope (per task spec): helmet, CSRF, rate limiting, UI changes,
schema changes — tracked in the manual-review file and proposed as
follow-up MR-H3.
2026-05-13 09:54:35 +00:00
..