fix: harden badge endpoints against rate limit exhaustion and username disclosure (GHSA-6c5j-4w43-2v8f #4)#596
Closed
advikdivekar wants to merge 1 commit into
Conversation
…sclosure Both badge routes accepted arbitrary usernames with only a 50-char length check, made unauthenticated GitHub API calls consuming shared GITHUB_TOKEN quota with no rate limiting, and logged every queried username to server logs. - Extend middleware matcher to cover /api/badge/:path* so both routes share the existing per-IP rate limiter (10 req/min for anonymous callers) - Replace weak length check with strict GitHub username regex (alphanumeric + hyphens, no leading/trailing hyphens, max 39 chars) - Remove all console.log statements that disclosed queried usernames - Add per-username response caching via cacheGet/cacheSet (TTL 3600s) to short-circuit repeated API calls for the same username
|
@advikdivekar is attempting to deploy a commit to the PRIYANSHU DOSHI's projects Team on Vercel. A member of the Team first needs to authorize it. |
GSSoC Label Checklist 🏷️@Priyanshu-byte-coder — please apply the appropriate labels before merging: Difficulty (pick one):
Quality (optional):
Validation (required to score):
|
Contributor
Author
|
Closing — patch will be submitted through the private advisory fork (GHSA-6c5j-4w43-2v8f) to avoid public disclosure before coordinated release. |
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.
Problem
Both badge endpoints (
/api/badge/commitsand/api/badge/streak-shield) were fully unauthenticated, accepted arbitrary?user=values with only a 50-character length check, and made direct GitHub API calls consuming the sharedGITHUB_TOKENquota with no rate limiting.Three concrete issues:
Rate limit exhaustion: An automated script looping over badge endpoints with no auth requirement could exhaust the 5,000 req/hr
GITHUB_TOKENquota in ~83 minutes, taking down the leaderboard, public profiles, and all badge features for every user until the window reset.Username disclosure via logs: Both routes called
console.logfor every request — every queried username and its commit/streak count was written to Vercel function logs, creating a persistent record accessible to anyone with log access.Unvalidated username characters: The only check was a 50-char length limit. Characters like
/,?,+, and#were passed directly into GitHub API URL path and query segments.Root cause: Badge routes were excluded from the existing rate limiting middleware (matcher only covered
/api/metrics/*) and had no format validation.What changed
src/middleware.tsmatcherto["/api/metrics/:path*", "/api/badge/:path*"]ANONYMOUS_LIMIT(10 req/min per IP) — no other middleware changes neededsrc/app/api/badge/commits/route.tsandsrc/app/api/badge/streak-shield/route.tsconsole.logstatements that disclosed queried usernames and their datacacheGet/cacheSetfrom@/lib/metrics-cache(TTL 3600s) — repeated requests for the same username within the cache window skip the GitHub API call entirelybadge:commits:{username}andbadge:streak:{username}— public data, no per-user scoping neededHow to verify
curl "/api/badge/commits?user=valid-user"→ 200 SVG responsecurl "/api/badge/commits?user=invalid/user"→ 400 JSON errorRetry-AfterheaderRegression check
console.errorfor actual GitHub API errors: preservedFixes GHSA-6c5j-4w43-2v8f vulnerability #4 (Medium).