Release-cycle review: POST /pages/template/lookup had only JwtAuthGuard and the
embed depth cap was client-only, so a scripted client could drive heavy
full-content fan-out (access control holds per-id, but a cost/DoS gap). And
page_template_references rows were written for any sourcePageId with no
workspace check at sync time (no leak today since lookup re-checks access, but
the graph could accumulate cross-space rows).
- Apply the standard per-user throttler (PAGE_TEMPLATE_THROTTLER, 30/min) to
/pages/template/lookup and /pages/toggle-template (mirrors ai-chat); auth +
the toggle's validateCanEdit CASL are unchanged.
- syncPageTemplateReferences / insertTemplateReferencesForPages now restrict
inserts to in-workspace source ids (filterInWorkspaceSourceIds, workspace +
not-deleted scoped, trx-aware) and still delete stale out-of-workspace rows
(self-heal). SECURITY comment: the ref table is NOT access-filtered; every
consumer must permission-filter at read time (as lookupTemplate does).
- Tests: lookup access exercises the REAL filterViewerAccessiblePageIds
(no_access / cross-workspace excluded / accessible+comment-stripped / <=50);
toggle controller CASL (cannot-edit -> Forbidden, flag not flipped); ref-sync
excludes cross-workspace and keeps in-workspace.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>