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
2 files changed+25−1f4abb8e8d638f7701a4b555f8d18b50c9be89510
2 changed files+25−1
Modifiedsrc/lib/reviewer-suggest.ts+1−0View fileUnifiedSplit
@@ -88,6 +88,7 @@ export async function suggestReviewers(
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
@@ -14,7 +14,7 @@
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,
@@ -24,6 +24,7 @@ import {
2424 users,
2525 issues,
2626 issueComments,
27 repoCollaborators,
2728} from "../db/schema";
2829import { Layout } from "../views/layout";
2930import { RepoHeader } from "../views/components";
@@ -5222,6 +5223,28 @@ pulls.post(
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