fix(documents): defense-in-depth port_id scope + invisible chevron a11y

- renameFolder/moveFolder UPDATE and deleteFolderSoftRescue DELETE now
  carry an explicit port_id predicate so the write is bounded to the
  same tenancy the pre-fetch verified, defending against future
  refactors that drop or reorder the ownership check.
- FolderRow's collapsed-children chevron is `invisible` for layout
  purposes, but it was still in the tab order with a misleading
  Expand/Collapse aria-label. Add aria-hidden + tabIndex=-1 when no
  children so keyboard users skip it.

Surfaced by post-implementation review (subagent code-review pass).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 16:50:02 +02:00
parent ae68e384ca
commit cf8bbf3018
2 changed files with 7 additions and 3 deletions

View File

@@ -136,7 +136,7 @@ export async function renameFolder(
const [updated] = await db
.update(documentFolders)
.set({ name: trimmed, updatedAt: new Date() })
.where(eq(documentFolders.id, folderId))
.where(and(eq(documentFolders.id, folderId), eq(documentFolders.portId, portId)))
.returning();
if (!updated) throw new NotFoundError('Folder');
@@ -209,7 +209,7 @@ export async function moveFolder(
const [updated] = await db
.update(documentFolders)
.set({ parentId: newParentId, updatedAt: new Date() })
.where(eq(documentFolders.id, folderId))
.where(and(eq(documentFolders.id, folderId), eq(documentFolders.portId, portId)))
.returning();
if (!updated) throw new NotFoundError('Folder');
@@ -262,7 +262,9 @@ export async function deleteFolderSoftRescue(
.set({ folderId: newParent })
.where(and(eq(documents.folderId, folderId), eq(documents.portId, portId)));
await tx.delete(documentFolders).where(eq(documentFolders.id, folderId));
await tx
.delete(documentFolders)
.where(and(eq(documentFolders.id, folderId), eq(documentFolders.portId, portId)));
});
void createAuditLog({