fix: reject cross-repo linked issues in mirror-path issue multiplier#1246
Open
kinjitakabe wants to merge 1 commit into
Open
fix: reject cross-repo linked issues in mirror-path issue multiplier#1246kinjitakabe wants to merge 1 commit into
kinjitakabe wants to merge 1 commit into
Conversation
PR entrius#1038 fixed cross-repo `Closes other/repo#N` leakage on the legacy OSS scoring path (entrius#1019), explicitly scoping out the mirror path because the live mirror payload did not carry repository identity on linked issues. After entrius#1202 stripped legacy and made mirror the sole PR-scoring path, that scoping-out is now load-bearing: a miner can earn 1.33×–1.66× by referencing a coordinated-account issue in any other repo. Layer 1 of a 2-layer fix: - Add `repository_full_name: Optional[str]` to `MirrorLinkedIssue`, lowercased at parse time to match `MirrorPullRequest.repo_full_name`. - Gate `_is_valid_linked_issue` on the same-repo invariant when the field is populated; fail open when `None` so existing mirror snapshots behave as before. Layer 2 (separate / coordinated): das-github-mirror populates `repository_full_name` on each `linked_issues` entry. The guard arms automatically once the field is served — no further validator-side change required.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
repository_full_name: Optional[str]toMirrorLinkedIssue; lowercase at parse time to matchMirrorPullRequest.repo_full_name._is_valid_linked_issueon the same-repo invariant when the field is populated; fail open onNone(older mirror snapshots, current production payload).Closes the mirror-path half of #1019 / PR #1038. After #1202 the mirror path is the only PR scoring path; the cross-repo leak that was tolerable when legacy was authoritative is now the entire surface.
Failure mode this closes
_is_valid_linked_issuewalks every anti-gaming gate from the legacy path (transferred, self-issue, created-after-PR, state_reason=COMPLETED, edited_after_merge, CLOSED, close-window) except the repository-identity gate — becauseMirrorLinkedIssuehad no repository field to gate on.A miner can earn 1.33×–1.66× on every PR by:
#XinB/throwaway-repo. State: OPEN.P_Mto a registered repo withCloses B/throwaway-repo#Xin the body. The cross-repoClosesdoes not auto-close#Xbecause A has no write onB/throwaway-repo—#Xstays OPEN at merge.P_Mmerging, B manually closes#XasCOMPLETED.P_M.linked_issues = [MirrorLinkedIssue(number=X, state='CLOSED', state_reason='COMPLETED', author_github_id=B, ...)]— no repository identity on the payload.B/throwaway-repo(trivial when B owns it).Per-PR uplift: 1.33×–1.66× on
earned_score. Orchestration cost per fake link: one alt GitHub account + one trivial issue. Detection: indistinguishable from a legitimate same-repoCloses #Nin any log line.Layer 1 / Layer 2
This is intentionally a two-layer change:
Layer 1 — validator-side (this PR)
MirrorLinkedIssue.repository_full_name: Optional[str] = None, populated fromdata.get('repository_full_name')and lowercased to match the PR side._is_valid_linked_issue, placed after the existing transferred / author / created-at gates and before state_reason / close-window:repository_full_name is None— no behavior change for the current production mirror snapshot. No-op until Layer 2 lands.Layer 2 — das-github-mirror (separate / coordinated)
das-github-mirrorpopulatesrepository_full_nameon eachlinked_issuesentry. This validator-side guard arms automatically the moment the field is served. No further validator-side change required.A follow-up issue can later tighten the guard to fail-closed-on-unknown once the mirror is fully repopulated, but that would over-correct today and zero every issue multiplier in the system.
Tests
tests/validator/oss_contributions/mirror/test_scoring.py::TestLinkedIssueCrossRepo:test_cross_repo_linked_issue_rejected— PR inentrius/gittensor-ui, issue payload saysoutsider/throwaway;_is_valid_linked_issuereturns False.test_same_repo_linked_issue_passes— same-repo populated → guard does not fire.test_repo_identity_missing_falls_open—None(current production shape) → guard does not fire; regression guard for the no-op-today invariant.test_cross_repo_case_insensitive— mixed-caseOUTSIDER/Throwawayis lowercased at parse time;!=comparison stays case-correct.test_cross_repo_multiplier_stays_neutral— end-to-end:_calculate_issue_multiplierreturns1.0.test_same_repo_multiplier_still_applies— end-to-end regression guard: known same-repo still earnsSTANDARD_ISSUE_MULTIPLIER.Existing fixture
_linked_issue(...)gained one optional kwarg (repository_full_name=None); 61 prior tests unchanged.Test plan
pytest tests/validator/oss_contributions/mirror/test_scoring.py— 67/67 pass (6 new).pytest tests/— 731/731 pass, no regressions.pyright— 0 errors / 0 warnings.ruff check gittensor/ tests/— clean.Related
_PR_TIMELINE_QUERY'sclosingIssuesReferencesgotrepository { nameWithOwner }).Out of scope
Fixes #1243