fix: add per-IP rate limiting and strict validation to badge endpoints#471
Conversation
|
@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):
|
Priyanshu-byte-coder
left a comment
There was a problem hiding this comment.
Merge conflict — rebase on main to resolve. The implementation is solid (rate limiting, username regex, badge validation, goals route hardening, .gitignore cleanup) — once conflict is cleared this is ready to merge.
Priyanshu-byte-coder
left a comment
There was a problem hiding this comment.
This PR has merge conflicts with main. Please rebase:
git fetch origin
git rebase origin/main
git push --force-with-lease
Note: PR #469 also deletes .claude/settings.local.json and adds .claude/ to .gitignore — if that PR is updated and merged before this one, those changes may already be in main.
- Wrap req.json() in try/catch — returns 400 on malformed JSON - Validate body is a non-null object before destructuring - title: must be non-empty string, trimmed, max 100 characters - target: must be integer in [1, 10000] — blocks 0, negatives, floats, NaN, overflow - unit: clamped to 30 chars silently - recurrence: unknown values default to 'none' explicitly - All bounds defined as named constants Fixes division-by-zero (target:0 → Infinity% progress bar) and negative target making 0-progress goals show as 100% complete. Closes Priyanshu-byte-coder#454
Badge endpoints accepted arbitrary GitHub usernames with no auth, no rate limiting, and only a length check — a single script could exhaust the shared GITHUB_TOKEN quota and DoS the whole platform. Adds a 20 req/min sliding-window rate limit per IP (shared with the existing middleware pattern), replaces the length check with a GitHub-spec username regex, and removes console.log calls that leaked user access patterns to server logs. Fixes Priyanshu-byte-coder#453
f476b6f to
7077b9d
Compare
70bcaf4 to
7077b9d
Compare
|
@Priyanshu-byte-coder please review it now |
ad92c0f
into
Priyanshu-byte-coder:main
|
🎉 Merged! Thanks for contributing to DevTrack. If the project has been useful to you, a ⭐ star on the repo is the easiest way to support it — it helps DevTrack get discovered by more developers. Keep an eye on open issues for your next contribution! |
Summary
Closes #453
The badge endpoints (
/api/badge/commitsand/api/badge/streak-shield) accepted any GitHub username with only a length check — noauthentication, no rate limiting. A single script looping thousands of usernames could exhaust the shared
GITHUB_TOKENquota (5,000 req/hr) andtake down the public profile page, leaderboard, and all badge endpoints simultaneously. Access patterns were also leaked unconditionally via
Changes
src/lib/badge-rate-limit.ts(new) — sliding-window, in-memory rate limiter scoped to badge endpoints (20 req/min per IP), mirrors theexisting middleware pattern exactly. Falls back gracefully: unknown IP counts as
"unknown", still rate limited.src/app/api/badge/commits/route.ts— replaces the loose length check withGITHUB_USERNAME_RE(GitHub-spec: 1–39 chars, alphanumeric +hyphens, no leading/trailing hyphen). Adds per-IP rate limit gate before any processing. Returns
429withRetry-After,X-RateLimit-*headerson excess. Removes all
console.logstatements. UpgradesCache-Controltos-maxage=3600, stale-while-revalidate=86400.src/app/api/badge/streak-shield/route.ts— same validation, same rate limit gate, same header cleanup.Test plan
GET /api/badge/commits?user=torvalds→ 200 SVG badgeGET /api/badge/commits?user=-invalid-→ 400{"error":"Invalid username"}GET /api/badge/commits(no param) → 400{"error":"Invalid username"}GET /api/badge/streak-shield?user=torvalds→ 200 SVG badgeGET /api/badge/streak-shield?user=-invalid-→ 400Retry-Afterheaderconsole.logof usernames during any of the aboveVerification summary: All 7 criteria passed live against the running dev server — valid usernames return SVG (200), invalid usernames return 400,
missing param returns 400, and the 20 req/min limit correctly returns 429 with Retry-After/X-RateLimit-* headers on excess. No console.log of
usernames appeared in server output (only console.error for actual GitHub API failures, which is correct).