-
-
Notifications
You must be signed in to change notification settings - Fork 466
Description
Summary
All GraphQL mutation resolvers operate on resources (tasks, checklists, teams, projects) by ID without verifying the authenticated user has access to the team or project that owns the resource.
Details
The authentication middleware (internal/route/route.go:118) only verifies the user is logged in, not that they belong to the relevant team/project. The GetProjectRoles() function exists in internal/graph/graph.go:215 but is rarely called by resolvers.
Example 1 - Add anyone to any team (internal/graph/team.resolvers.go:18):
func (r *mutationResolver) CreateTeamMember(ctx context.Context, input CreateTeamMember) {
// Takes input.TeamID directly - no membership validation
r.Repository.CreateTeamMember(ctx, db.CreateTeamMemberParams{
TeamID: input.TeamID, UserID: input.UserID, ...})
}Example 2 - Modify any task (internal/graph/task.resolvers.go:23):
func (r *mutationResolver) CreateTaskChecklist(ctx context.Context, input CreateTaskChecklist) {
r.Repository.CreateTaskChecklist(ctx, db.CreateTaskChecklistParams{
TaskID: input.TaskID, // No project/team check
})
}This pattern extends across all resolvers: task CRUD, checklist operations, team member management, label operations, etc.
Impact
Any authenticated user can add themselves to any team, escalate roles, create/modify/delete tasks across teams, and access all project data.
Suggested Fix
Add project/team membership validation to each resolver using the existing GetProjectRoles():
role, err := GetProjectRoles(ctx, r.Repository, projectID)
if err != nil {
return nil, errors.New("unauthorized")
}CWE-639: Authorization Bypass Through User-Controlled Key