Make the update check immune to GitHub rate limiting, refresh the About icons - #203
Merged
Conversation
- The About update check surfaced a bare "(403)" when GitHub rate limited the request. The unauthenticated API allows 60 requests/hour per IP, so a shared egress (VPN, CGNAT) exhausts the pool through no fault of the app; 403/429 now maps to a localized explanation, and the releases-page link shows after any failed check as the manual path — the web page is not behind the API rate limit. - Redraw the external-link glyph on the About rows: the old geometry ran the arrow strokes within a sub-stroke distance of the box outline, so they fused at render size. The box now sits bottom-left with the arrow leaving through the open corner.
- The complete fix for the 403: on Android the update check now asks the native layer first, which reads the release tag from the Location header of the github.com releases/latest redirect. The web endpoint is not behind the GitHub API 60-requests/hour-per-IP quota, so the check keeps working on shared-egress networks where the unauthenticated API answers 403. The API fetch remains the browser-shell path and the fallback for any native failure, keeping the rate-limit message and the manual releases link as the last line. - AppUpdateChecker is pure JVM over the HttpTransport seam (redirects already surface as-is): refuses non-redirects, locationless responses, and any target outside this repository tag page (the URL is opened in a browser and must never be attacker-shaped); strips query/fragment, decodes the tag, bounds the probe body. Five JVM tests. - UrlConnectionTransport moves out of CloudDocumentsPlugin into a shared package-private class; AndroidAppInfo gains the getLatestRelease method on a single-thread executor, and the web wrapper becomes the single registration point for the plugin.
The code-brackets glyph read as "source code", not GitHub. The row now carries the Octicons mark-github glyph (MIT; GitHub permits the mark as a link to a repository). It is a filled shape, so it opts out of the shared stroke styling and sits at 24px to balance the visual weight.
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.
Findings from the owner's post-release phone test of v0.2.0, on the About page.
1. "Could not check updates (403)" — diagnosed, then solved outright
Diagnosis — environmental, not a client bug. The checker called GitHub's
releases/latestAPI unauthenticated, which is limited to 60 requests/hour per source IP. The phone was on a VPN: the shared exit IP's pool was exhausted by other clients on that node, and GitHub answers 403. Evidence: the identical request from this machine returns 200 withX-RateLimit-Limit: 60, and the same build returned "latest version" on the emulator over a different egress. A public client cannot ship a token, so the API quota itself cannot be raised.The complete fix — stop depending on the API at all. On Android the check now asks the native layer first:
AppUpdateChecker(pure JVM over the existingHttpTransportseam, which already surfaces redirects unfollowed) issues one GET togithub.com/…/releases/latestand reads the release tag from theLocationheader of the redirect. The web endpoint is not behind the API quota, so the check works on shared-egress networks where the API refuses. Hardening: refuses non-redirects, locationless responses, and any target outside this repository's tag page (the URL is later opened in a browser and must never be attacker-shaped); strips query/fragment, URL-decodes the tag, bounds the probe body at 64 KB.Defense in depth kept. The API fetch remains the browser-shell path and the fallback for any native failure; there, 403/429 now maps to a localized rate-limit explanation (×10 locales) instead of a bare status code, and the releases-page link row appears after any failed check as the manual path.
Plumbing:
UrlConnectionTransportmoves out ofCloudDocumentsPlugininto a shared package-private class;AndroidAppInfogainsgetLatestReleaseon a single-thread executor; the web wrapper (src/lib/androidAppInfo.ts) becomes the plugin's single registration point (Capacitor rejects duplicate names), andadvancedDiagnosticsreuses it.2. About icons
mark-githubOctocat (MIT-licensed glyph; GitHub permits the mark as a link to a repository). Filled shape, so it opts out of the shared stroke styling at a balanced 24 px.Verification
AppUpdateCheckerTest: redirect parse, relative-Location + query/fragment/URL-decode handling, refusal of the no-releases redirect and foreign hosts, refusal of non-redirect/locationless responses, bounded probe + Accept header).vue-tsc -b+ Vite build green, focused lint clean, locale contract satisfied.mobile-home-navigation2/2 (browser shell keeps the fetch path: link hidden before a check, shown with the right href after a mocked available result).AndroidAppInfo.getLatestReleasenative call and zeroapi.github.comtraffic — the tag came from the redirect. Screenshot-verified the Octocat row and the redrawn external-link glyph.Notes