fix(api): enforce PR ownership on the ai-review-findings REST route - #8663
Conversation
GET /v1/repos/:owner/:repo/pulls/:number/ai-review-findings only called requireContributorAccess (which proves the caller IS the login query param), never that the login authored the target PR -- so any authenticated contributor could read ANY PR's structured AI-review findings by passing their own login. The equivalent MCP tool (server.ts getPrAiReviewFindings) already guards this by fetching the PR and calling assertContributorOwnsPullRequest. Mirror that guard order on the REST route: 404 when the PR doesn't exist, 403 when it exists but belongs to another author, before loadPrAiReviewFindings runs. Adds route tests for the 403 (wrong-author) and 404 (missing-PR) cases; the existing pass-through tests now seed the requesting login's own PR so they still reach the delegate.
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8663 +/- ##
==========================================
- Coverage 93.77% 91.01% -2.76%
==========================================
Files 798 97 -701
Lines 79475 24895 -54580
Branches 24082 4656 -19426
==========================================
- Hits 74525 22659 -51866
+ Misses 3565 1971 -1594
+ Partials 1385 265 -1120
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-25 23:37:00 UTC
Review summary Nits — 3 non-blocking
Flagged checks (non-blocking)
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
What & why
Closes #8659.
GET /v1/repos/:owner/:repo/pulls/:number/ai-review-findings(src/api/routes.ts) only calledrequireContributorAccess(c, login)— which verifies the caller's session matches theloginquery param, not thatloginauthored the target PR — then delegated straight toloadPrAiReviewFindings. Neither the route norloadPrAiReviewFindingsperforms an ownership check.Consequence: any authenticated contributor, passing their own login, could query this endpoint for any PR number in any repo they can read and receive that PR's structured AI-review findings (file paths, line numbers, severity, review body). The equivalent MCP tool (
server.tsgetPrAiReviewFindings) already blocks this exact pattern viagetPullRequest+assertContributorOwnsPullRequest; the REST surface for the identical data did not.The fix
Mirror the MCP tool's guard order on the REST route, before
loadPrAiReviewFindingsruns:getPullRequest; 404{ error: "not_found" }when it doesn't exist;assertContributorOwnsPullRequest(pullRequest.authorLogin, login)— 403{ error: "forbidden" }when the PR exists but belongs to another author.No change to
loadPrAiReviewFindingsor any other route.Tests
other-miner, requested as session-authenticatedminer1→ 403, no findings leaked (previously 200 with real data).ready/not_found/ai_review_off) now seed the requesting login's own PR so they still reach the delegate through the new ownership guard.Verified bug-catching: reverting the guard makes both the 403 and 404 tests fail (the leak reproduces).
Validation
test/unit/routes-pr-ai-review-findings.test.ts: 10 tests pass; typecheck clean;git diff --checkclean.main, mergeable-clean.