fix(review): treat a confirmed-nonexistent linked issue as a hard-rule violation#2402
Conversation
…e violation fetchLinkedIssueFacts collapsed a confirmed 404 and a transient fetch failure into the same undefined result. resolveLinkedIssueHardRule then treated an all-undefined fetch batch as "no violation" (fail open) regardless of WHY every fetch came back empty — so a contributor citing a fabricated issue number (e.g. "Fixes #999999") satisfied the "has a linked issue" check and was fully exempt from the ownerAssignedClose/missingPointLabelClose/maintainerOnlyLabelClose hard rules, even on a repo with all three set to block. The codebase already closed this exact evasion class for reference overflow (too-many-refs = a violation, not a silent skip); it was never applied to the all-404 case. fetchLinkedIssueFacts now returns a tri-state result (found / not_found / fetch_error) instead of collapsing both into undefined. resolveLinkedIssueHardRule treats an ALL-not_found fetch batch the same way it already treats overflow: an unsafe-to-verify violation. A single fetch_error in the mix still fails open, since a genuine outage could be masking a real, rule-violating issue.
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-02 00:13:14 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 6 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 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 Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2402 +/- ##
==========================================
+ Coverage 95.71% 95.73% +0.01%
==========================================
Files 222 222
Lines 24661 24667 +6
Branches 8949 8952 +3
==========================================
+ Hits 23605 23614 +9
+ Misses 433 430 -3
Partials 623 623
🚀 New features to boost your workflow:
|
…cated 404 GitHub returns 404 both for a genuinely nonexistent issue and for a real-but-inaccessible one (private repo, no grant) -- it deliberately doesn't distinguish the two. fetchLinkedIssueFacts treated every 404 as confirmed absence regardless of token, so a missing/under-scoped installation token falling back to the public token could get a false-positive "not_found" on a real linked issue, tripping the linked-issue hard-rule close. Only classify a 404 as not_found when the token used is a genuine, non-public credential; otherwise treat it as fetch_error (fails open), consistent with every other unprovable-outcome path in this function.
What
fetchLinkedIssueFacts(src/github/backfill.ts) collapsed a confirmed 404 (issue genuinely doesn't exist) and a transient fetch failure (network/5xx/rate-limit) into the sameundefinedresult.resolveLinkedIssueHardRulethen treated an all-undefinedfetch batch as "no violation" (fail open) regardless of why every fetch came back empty. So a contributor citing a fabricated issue number (Fixes #999999) satisfied the "has a linked issue" advisory check and was fully exempt from theownerAssignedClose/missingPointLabelClose/maintainerOnlyLabelClosehard rules — even on a repo with all three set toblock. The codebase already closed this exact evasion class for reference overflow (too many closing refs = a violation, not a silent skip); the same principle was never applied to the all-404 case, and there was even an existing test explicitly titled "is fail-open: undefined when every fetch fails (404)" documenting the gap.Fix
fetchLinkedIssueFactsnow returns a tri-state result —{ status: "found", facts }/{ status: "not_found" }/{ status: "fetch_error" }— instead of collapsing both 404 and transient errors intoundefined. Distinguishes via the already-thrownGitHubApiError'sstatusCode(same-module class, no export needed) rather than a blind.catch(() => undefined).resolveLinkedIssueHardRule(src/review/linked-issue-hard-rules.ts) now treats an ALL-not_foundfetch batch the same way it already treats overflow: an unsafe-to-verify violation, with a reason citing that the reference couldn't be found. A singlefetch_erroranywhere in the mix still fails open (the same way as before) — we can't rule out a real, rule-violating issue hiding behind a genuine outage.resolveLinkedIssueAuthorLoginsinsrc/queue/processors.ts) for the new tri-state shape; its outer.catch(() => null)was removed sincefetchLinkedIssueFactsno longer rejects (the pure.then()callback that replaced it can't throw either).Deliberately out of scope: the issue's "Consider hardening the
missing_linked_issueadvisory finding similarly" suggestion — that finding is a synchronous, pure regex-count check with no live GitHub fetch today, and extending it would be a separate, larger architectural change (making an otherwise-pure advisory path async). Left for a follow-up if desired.Tests
violated: trueinstead — the corrected behavior.undefined), confirming the fix doesn't regress outage safety.fetchLinkedIssueFactsitself (test/unit/backfill.test.ts, no prior dedicated coverage existed): found-with-fallback-defaults (sparse payload missingnumber/state), confirmed not_found on 404, fetch_error on 5xx.npx tsc --noEmitclean.linked-issue-hard-rules.test.ts— 28 passed;gate-check-policy.test.ts— 85 passed;backfill.test.ts— 143 passed.queue.test.ts— 202 passed.npm run test:coverage: 5605 passed, 4 skipped (pre-existing/unrelated), 0 failed.npm audit --audit-level=moderate: 0 vulnerabilities.Advances #1936. Closes #2136.