Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commitf4abb8eunknown_key

fix(M13): guard inArray empty array + validate reviewer repo membership

fix(M13): guard inArray empty array + validate reviewer repo membership

- reviewer-suggest.ts: add `if (uniqueEmails.length === 0) return []`
  guard before inArray query (matches pattern used in notify.ts/email-digest.ts)
- pulls.tsx: verify reviewer is repo owner or accepted collaborator before
  calling requestReview() — prevents arbitrary user ID injection via form tamper
- pulls.tsx: import isNotNull, repoCollaborators for the membership check

https://claude.ai/code/session_01ACsT2Pc8GRoZwZRF8SK68Y
Claude committed on May 29, 2026Parent: ace34ef
2 files changed+251f4abb8e8d638f7701a4b555f8d18b50c9be89510
2 changed files+25−1
Modifiedsrc/lib/reviewer-suggest.ts+1−0View fileUnifiedSplit
8888 }
8989
9090 const uniqueEmails = Array.from(emailCounts.keys());
91 if (uniqueEmails.length === 0) return [];
9192
9293 // Step 3 — look up users by email
9394 const emailUsers = await db
Modifiedsrc/routes/pulls.tsx+24−1View fileUnifiedSplit
1414 */
1515
1616import { Hono } from "hono";
17import { eq, and, desc, asc, sql, inArray, ilike, ne } from "drizzle-orm";
17import { eq, and, desc, asc, sql, inArray, ilike, ne, isNotNull } from "drizzle-orm";
1818import { db } from "../db";
1919import {
2020 pullRequests,
2424 users,
2525 issues,
2626 issueComments,
27 repoCollaborators,
2728} from "../db/schema";
2829import { Layout } from "../views/layout";
2930import { RepoHeader } from "../views/components";
52225223 );
52235224 }
52245225
5226 // Verify the reviewer is the repo owner or an accepted collaborator — prevents
5227 // requesting reviews from arbitrary user IDs outside this repository.
5228 const isOwner = reviewerId === resolved.owner.id;
5229 if (!isOwner) {
5230 const [collab] = await db
5231 .select({ id: repoCollaborators.id })
5232 .from(repoCollaborators)
5233 .where(
5234 and(
5235 eq(repoCollaborators.repositoryId, resolved.repo.id),
5236 eq(repoCollaborators.userId, reviewerId),
5237 isNotNull(repoCollaborators.acceptedAt)
5238 )
5239 )
5240 .limit(1);
5241 if (!collab) {
5242 return c.redirect(
5243 `/${ownerName}/${repoName}/pulls/${prNum}?info=${encodeURIComponent("Reviewer must be a repository collaborator.")}`
5244 );
5245 }
5246 }
5247
52255248 const { requestReview } = await import("../lib/reviewer-suggest");
52265249 const result = await requestReview(pr.id, resolved.repo.id, reviewerId, user.id);
52275250
52285251