Summary
Task creation/update rules trust client-provided task fields too broadly. A non-admin authenticated user can create tasks with arbitrary groupId/workerUid, and the isRejectionTask == true shortcut can be used as an authorization bypass.
Evidence
firestore.rules:65-67 allows task creation when createdBy == request.auth.uid or isRejectionTask == true.
firestore.rules:70-72 allows updates by task creator or for any task where resource.data.isRejectionTask == true.
src/app/(main)/admin/tasks/create/page.tsx:131-176 constructs task documents client-side.
src/lib/hooks/useTask.ts:183-199 creates the task and rows from client code.
src/lib/firebase/firestore.ts:168-176 writes task documents directly to Firestore.
Impact
A signed-in user can directly write a task document with themselves as createdBy, an arbitrary groupId, arbitrary workerUid, arbitrary columns, and potentially isRejectionTask: true. Once a task is created or marked as a rejection task, broad update rights can alter assignment, columns, group association, row counts, export semantics, or ownership-sensitive fields.
Minimal Fix
- Require
request.resource.data.createdBy == request.auth.uid and isGroupAdmin(request.resource.data.groupId) for task creation.
- Remove the unconditional
isRejectionTask == true create/update bypass.
- If rejection tasks are needed, require reviewer/admin membership in the relevant group and restrict the allowed fields.
- Make immutable fields immutable after creation:
groupId, createdBy, workerUid, columns, csvFileName, and totalRows unless an admin-only edit flow explicitly supports them.
- Validate task document keys and allowed enum values in rules.
Acceptance Criteria
- Non-admin group members cannot create tasks in a group.
- A user cannot create a task for a group they do not administer.
- Setting
isRejectionTask: true does not bypass admin/reviewer authorization.
- Direct Firestore writes cannot mutate immutable task fields after creation.
Summary
Task creation/update rules trust client-provided task fields too broadly. A non-admin authenticated user can create tasks with arbitrary
groupId/workerUid, and theisRejectionTask == trueshortcut can be used as an authorization bypass.Evidence
firestore.rules:65-67allows task creation whencreatedBy == request.auth.uidorisRejectionTask == true.firestore.rules:70-72allows updates by task creator or for any task whereresource.data.isRejectionTask == true.src/app/(main)/admin/tasks/create/page.tsx:131-176constructs task documents client-side.src/lib/hooks/useTask.ts:183-199creates the task and rows from client code.src/lib/firebase/firestore.ts:168-176writes task documents directly to Firestore.Impact
A signed-in user can directly write a task document with themselves as
createdBy, an arbitrarygroupId, arbitraryworkerUid, arbitrary columns, and potentiallyisRejectionTask: true. Once a task is created or marked as a rejection task, broad update rights can alter assignment, columns, group association, row counts, export semantics, or ownership-sensitive fields.Minimal Fix
request.resource.data.createdBy == request.auth.uidandisGroupAdmin(request.resource.data.groupId)for task creation.isRejectionTask == truecreate/update bypass.groupId,createdBy,workerUid,columns,csvFileName, andtotalRowsunless an admin-only edit flow explicitly supports them.Acceptance Criteria
isRejectionTask: truedoes not bypass admin/reviewer authorization.