CMMC 2.0 AC.L1-3.1.2 (NIST 800-171r2 3.1.2) requires that users are limited to the specific transactions and functions they are authorized to execute — not merely authenticated to the system. Without ownership or role checks on write operations, any logged-in user can modify or delete records belonging to others, a vulnerability OWASP A01 and CWE-285 classify as Broken Access Control. In FCI-handling systems this creates an audit trail problem: if user A can overwrite user B's contract documents, data integrity and individual accountability both collapse — both mandatory CMMC concerns.
High because an authenticated-but-unauthorized write can corrupt or delete another user's FCI without any privilege escalation step.
Add an ownership or role check immediately after the session check in every mutation handler. Authentication and authorization are two separate gates — passing the first does not satisfy the second:
// app/api/documents/[id]/route.ts
export async function PUT(req: Request, { params }: { params: { id: string } }) {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const document = await db.document.findUnique({ where: { id: params.id } })
if (!document) return Response.json({ error: 'Not found' }, { status: 404 })
// Ownership check — not just "is there a session?"
if (document.userId !== session.user.id && session.user.role !== 'admin') {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
const body = await req.json()
const updated = await db.document.update({ where: { id: params.id }, data: body })
return Response.json(updated)
}
Report the ratio of mutation endpoints covered — partial coverage is still a CMMC failure under AC.L1-3.1.2.
ID: gov-cmmc-level-1.access-control.transaction-control
Severity: high
CMMC Practice: AC.L1-3.1.2
What to look for: Examine authorization checks on write and mutate operations. Look at every POST, PUT, PATCH, and DELETE handler — after verifying that a session exists (authentication), also verify that the authenticated user is authorized to perform that specific action (authorization). Look for role checks, ownership verification (e.g., if (record.userId !== session.user.id)), or permission gates. Check for any mutation endpoint that only confirms a valid session without also checking whether the user has permission to modify the targeted resource.
Pass criteria: Count all mutation endpoints (POST, PUT, PATCH, DELETE) and check each for authorization logic. All mutation endpoints verify user authorization beyond just authentication. At least 90% of sensitive write operations have server-side permission or ownership checks. Report the ratio: "X of Y mutation endpoints have authorization checks."
Fail criteria: Write operations only confirm the user is logged in but do not check whether they are allowed to perform the specific action. Must not pass when ownership checks are present on some endpoints but absent on others — partial coverage is still a fail.
Skip (N/A) when: The project has no write operations or data mutations (fully read-only).
Detail on fail: Name the specific endpoints lacking authorization checks. Example: "PUT /api/documents/[id] only checks session exists but not whether session.user.id matches document.userId. Any authenticated user can edit any document." Keep under 500 characters.
Remediation: Add ownership or role verification to every mutation handler:
// app/api/documents/[id]/route.ts
export async function PUT(req: Request, { params }: { params: { id: string } }) {
const session = await getServerSession()
if (!session) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
// Authorization: verify ownership, not just authentication
const document = await db.document.findUnique({ where: { id: params.id } })
if (!document) {
return Response.json({ error: 'Not found' }, { status: 404 })
}
if (document.userId !== session.user.id && session.user.role !== 'admin') {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
// Proceed with update
const body = await req.json()
const updated = await db.document.update({ where: { id: params.id }, data: body })
return Response.json(updated)
}