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:
@@ -122,6 +122,8 @@ function FolderRow({
|
||||
<button
|
||||
type="button"
|
||||
aria-label={open ? 'Collapse' : 'Expand'}
|
||||
aria-hidden={!hasChildren}
|
||||
tabIndex={hasChildren ? 0 : -1}
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className={cn(
|
||||
'flex h-5 w-5 items-center justify-center text-muted-foreground hover:text-foreground',
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user