Skip to content

[BUG] Room creation API accepts unbounded input lengths and has no per-user room limit #2213

@nyxsky404

Description

@nyxsky404

Describe the bug

The `POST /api/rooms` handler only verifies that required fields are non-empty. It applies no maximum-length constraints on any field and imposes no cap on how many rooms a single user can create.

```ts
// src/app/api/rooms/route.ts
if (!body.name?.trim() || !body.repo_owner?.trim() || !body.repo_name?.trim())
return NextResponse.json({ error: '...' }, { status: 400 });
// No length checks. No room count limit.
```

Impact

  1. Storage exhaustion — A user can send `name: "a".repeat(100_000)`, either triggering a silent 500 from the database or successfully writing large payloads. The `description` field is unbounded as well.

  2. GitHub constraint mismatch — GitHub enforces that usernames are ≤ 39 characters and repository names are ≤ 100 characters, matching the regex `[a-zA-Z0-9._-]+`. The API accepts any string for `repo_owner` and `repo_name`, allowing values that will never correspond to a real GitHub repository.

  3. Unlimited room creation — There is no upper bound on how many rooms a single user can own. This is inconsistent with the goals API, which explicitly enforces `MAX_GOALS_PER_USER = 5`.

Files affected

  • `src/app/api/rooms/route.ts` (lines 22–26) — POST handler with no length or count validation
  • `src/lib/supabase-rooms.ts` (line 20) — `createRoom` with no pre-insert checks
  • `src/components/rooms/CreateRoomModal.tsx` — form inputs have no `maxLength` attributes

Expected behaviour

The API should enforce:

  • `name`: 1–100 characters
  • `repo_owner`: 1–39 characters, matching GitHub's username format
  • `repo_name`: 1–100 characters, matching GitHub's repository name format
  • `description`: 0–500 characters
  • Maximum rooms owned per user (suggested: 20)

Suggested fix

```ts
// src/app/api/rooms/route.ts

const MAX_ROOMS_PER_USER = 20;
const GITHUB_USERNAME_RE = /^a-zA-Z0-9?$|^[a-zA-Z0-9]$/;
const GITHUB_REPO_RE = /^[a-zA-Z0-9._-]{1,100}$/;

// In POST handler, before createRoom():
if (body.name.trim().length > 100)
return NextResponse.json({ error: 'name must be 100 characters or fewer' }, { status: 400 });
if (!GITHUB_USERNAME_RE.test(body.repo_owner.trim()))
return NextResponse.json({ error: 'Invalid GitHub username format for repo_owner' }, { status: 400 });
if (!GITHUB_REPO_RE.test(body.repo_name.trim()))
return NextResponse.json({ error: 'Invalid GitHub repository name format' }, { status: 400 });
if (body.description && body.description.length > 500)
return NextResponse.json({ error: 'description must be 500 characters or fewer' }, { status: 400 });

const { count } = await supabaseAdmin
.from('room_members')
.select('*', { count: 'exact', head: true })
.eq('github_username', session.githubLogin)
.eq('role', 'owner');

if ((count ?? 0) >= MAX_ROOMS_PER_USER)
return NextResponse.json({ error: `You can own at most ${MAX_ROOMS_PER_USER} rooms` }, { status: 429 });
```

Also add corresponding `maxLength` attributes to the form inputs in `CreateRoomModal.tsx` for immediate client-side feedback.

Additional context

The goals API (`src/app/api/goals/route.ts`) is a good reference — it defines `MAX_GOALS_PER_USER`, `MAX_TITLE_LEN`, and `MAX_UNIT_LEN` constants and validates every field before touching the database.

Metadata

Metadata

Assignees

Labels

gssoc:assignedGSSoC: Issue assigned to a contributor

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions