Skip to content

fix(github): evict a cached App JWT on rejection to stop fleet-wide token-minting poisoning - #2491

Closed
luciferlive112116 wants to merge 2 commits into
JSONbored:mainfrom
luciferlive112116:fix/app-jwt-eviction-2453
Closed

fix(github): evict a cached App JWT on rejection to stop fleet-wide token-minting poisoning#2491
luciferlive112116 wants to merge 2 commits into
JSONbored:mainfrom
luciferlive112116:fix/app-jwt-eviction-2453

Conversation

@luciferlive112116

Copy link
Copy Markdown
Contributor

Summary

  • Evict the cached App JWT and retry installation-token mint once when GitHub returns 401, mirroring the existing withInstallationTokenRetry pattern for installation tokens.
  • Prevents a transient rejected App JWT from poisoning every installation-token mint fleet-wide for up to 8 minutes.

Fixes #2453

Test plan

  • REGRESSION (#2453): evicts a rejected App JWT and retries the mint once instead of failing outright
  • REGRESSION (#2453): does not infinite-loop when the retried App JWT is ALSO rejected

…oken-minting poisoning

Fixes JSONbored#2453

Co-authored-by: Cursor <cursoragent@cursor.com>
@dosubot dosubot Bot added the size:M label Jul 2, 2026
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.95%. Comparing base (853ec9a) to head (079d503).
⚠️ Report is 31 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2491      +/-   ##
==========================================
+ Coverage   95.93%   95.95%   +0.02%     
==========================================
  Files         225      226       +1     
  Lines       25336    25432      +96     
  Branches     9217     9245      +28     
==========================================
+ Hits        24306    24404      +98     
  Misses        417      417              
+ Partials      613      611       -2     
Files with missing lines Coverage Δ
src/github/app.ts 98.07% <100.00%> (+0.06%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb

loopover-orb Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-02 06:48:29 UTC

2 files · 1 AI reviewer · no blockers · readiness 55/100 · CI pending · blocked

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change factors the installation-token POST into a helper, retries a 401 once after evicting the cached App JWT, and preserves the existing non-ok error path after the bounded retry. The visible production path is coherent and avoids an unbounded loop; the main weakness is that the regression test proves retry behavior but does not prove the retried request used a newly signed JWT rather than the same cached credential.

Nits — 8 non-blocking
  • nit: test/unit/github-app.test.ts:190 has an extra leading space before `expect(mints).toBe(1);`, which should be normalized by the formatter.
  • nit: test/unit/github-app.test.ts:202 only counts `/access_tokens` calls, so it would still pass for a retry implementation that forgot `expireCachedAppJwt`; capture the `authorization` headers and assert the second request differs after pre-warming the App JWT cache.
  • nit: src/github/app.ts:276 leaves the retried 401 JWT cached when the second mint attempt is also rejected, so consider evicting again before throwing to avoid the next mint reusing a JWT already known to be rejected.
  • In test/unit/github-app.test.ts:202, pre-warm `createInstallationToken` or otherwise create a cached App JWT, then assert the two mint attempts send different bearer values so the test covers the cache eviction contract, not just the retry count.
  • In src/github/app.ts:276, if the retried response is still 401, delete the App JWT cache entry before falling through to the existing error construction.
  • Pull request duplicates other open work — Check for an existing pull request or issue covering this change and coordinate or consolidate before continuing.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2453
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:M; 1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 30 registered-repo PR(s), 12 merged, 7 issue(s).
Contributor context ✅ Confirmed Gittensor contributor luciferlive112116; Gittensor profile; 30 PR(s), 7 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
Contributor next steps
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Await review-lane availability.
  • Triage stale or unlinked PRs.
  • Refresh registry data or choose a registered active repo.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 2, 2026
@JSONbored JSONbored added maintainer-only Owner-only work — yields no Gittensor points. and removed gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 2, 2026
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 2, 2026

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer-only issue, and also, test gaps:

the main weakness is that the regression test proves retry behavior but does not prove the retried request used a newly signed JWT rather than the same cached credential.

Closing.

@JSONbored JSONbored closed this Jul 2, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. maintainer-only Owner-only work — yields no Gittensor points.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

fix(github): evict a cached App JWT on rejection to stop fleet-wide token-minting poisoning

2 participants