Skip to content

fix: isolate compare route cache per user to prevent cross-user data exposure (GHSA-6c5j-4w43-2v8f #3)#595

Closed
advikdivekar wants to merge 1 commit into
Priyanshu-byte-coder:mainfrom
advikdivekar:fix/ghsa-compare-cache-isolation
Closed

fix: isolate compare route cache per user to prevent cross-user data exposure (GHSA-6c5j-4w43-2v8f #3)#595
advikdivekar wants to merge 1 commit into
Priyanshu-byte-coder:mainfrom
advikdivekar:fix/ghsa-compare-cache-isolation

Conversation

@advikdivekar
Copy link
Copy Markdown
Contributor

Problem

The /api/metrics/compare route used next: { revalidate: 3600 } on four GitHub API fetch calls. Next.js's built-in fetch cache keys responses by URL only — the Authorization header is not part of the cache key.

This meant: if User A (with private repositories) queried a GitHub username, their response was cached under the GitHub API URL. User B querying the same username within the one-hour window received User A's cached response, which could include commit counts and repository metadata from User A's private repositories.

Root cause: next: { revalidate } is incompatible with per-user authenticated requests. Any user's private data fetched with their token becomes visible to any other user who queries the same target URL.

What changed

src/app/api/metrics/compare/route.ts

  • Replaced all four next: { revalidate: 3600 } options with cache: "no-store" to opt out of the shared URL-keyed fetch cache
  • Wrapped the full data-fetch and computation block in withMetricsCache (existing utility from @/lib/metrics-cache) with a cache key of metrics:{session.githubId}:compare:{username}
  • The githubId component ensures no two users ever share a cache entry
  • Added isMetricsCacheBypassed support so clients can force a cache refresh with ?refresh=1
  • Gracefully degrades to live fetches when Redis is unavailable

How to verify

  1. Sign in as User A (account with private repositories), use the Friend Comparison widget to look up any GitHub username
  2. Sign in as User B in a separate session, look up the same username within one hour
  3. User B sees only data their token can access — no private repo data from User A leaks through
  4. ?refresh=1 query param bypasses the cache and fetches fresh data

Regression check

  • Friend Comparison widget: still returns correct data
  • Cache degrades gracefully when UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN are not set
  • Auth check and username validation: unchanged

Fixes GHSA-6c5j-4w43-2v8f vulnerability #3 (High).

…er data exposure

Next.js fetch cache keys by URL only, excluding Authorization headers. With
next: { revalidate }, User A's private repo data fetched with their token was
cached and served to User B querying the same GitHub username within the
one-hour window.

Replace all four next: { revalidate: 3600 } calls with cache: "no-store" and
wrap the full computation in withMetricsCache keyed by
metrics:{githubId}:compare:{username}. Each user gets an isolated cache entry
— no two users can share a response. Gracefully degrades to live fetches when
Redis is unavailable.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

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

@github-actions github-actions Bot added gssoc26 GSSoC 2026 contribution type:bug GSSoC type bonus: bug fix labels May 21, 2026
@github-actions
Copy link
Copy Markdown

GSSoC Label Checklist 🏷️

@Priyanshu-byte-coder — please apply the appropriate labels before merging:

Difficulty (pick one):

  • level:beginner — 20 pts
  • level:intermediate — 35 pts
  • level:advanced — 55 pts
  • level:critical — 80 pts

Quality (optional):

  • quality:clean — ×1.2 multiplier
  • quality:exceptional — ×1.5 multiplier

Validation (required to score):

  • gssoc:approved — counts for points
  • gssoc:invalid / gssoc:spam / gssoc:ai-slop — does not score

Type labels (type:*) are auto-detected from files and title. Review and adjust if needed.
Points formula: (difficulty × quality_multiplier) + type_bonus

@advikdivekar
Copy link
Copy Markdown
Contributor Author

Closing — patch will be submitted through the private advisory fork (GHSA-6c5j-4w43-2v8f) to avoid public disclosure before coordinated release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc26 GSSoC 2026 contribution type:bug GSSoC type bonus: bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant