-
Notifications
You must be signed in to change notification settings - Fork 266
Description
Vulnerability: Cross-Organization Project Creation IDOR
Classification: CWE-639 (Authorization Bypass Through User-Controlled Key)
Severity: High
Affected file: apps/server/src/app/projects/projects.resolver.ts (createProject mutation)
Description
The createProject mutation is missing both the @CurrentUser() decorator and the isOrgMemberOrThrow() authorization check. The organizationId comes entirely from user input (the GraphQL data argument), allowing any authenticated user to create projects in arbitrary organizations they do not belong to.
Comparison with Secure Siblings
All sibling operations correctly enforce organization membership:
| Operation | @CurrentUser() |
Org membership check |
|---|---|---|
updateProjectSettings |
Yes | isOrgMemberOrThrow() |
deleteProject |
Yes | isOrgAdminOrThrow() |
project (query) |
Yes | isOrgMemberOrThrow() |
projects (query) |
Yes | isOrgMemberOrThrow() |
createProject |
Missing | Missing |
This is a classic 1-of-N inconsistency where one mutation was missed when authorization was added to the resolver.
Impact
Any authenticated user can create projects in organizations they do not belong to. This is a cross-tenant authorization bypass.
Suggested Fix
Add the @CurrentUser() decorator and call isOrgMemberOrThrow() before creating the project:
@Mutation(() => Project)
async createProject(
@CurrentUser() user: RequestUser,
@Args('data') data: CreateProjectInput
) {
isOrgMemberOrThrow(user, data.organizationId);
return this.projectsService.createProject(...);
}This aligns createProject with the authorization pattern used by all other mutations and queries in the same resolver.
Found during systematic auth/authz audit. Reported responsibly — no exploit details included.