Skip to content

🔒 Fix potential Server-Side DoS via Unauthenticated GitHub API Call - #516

Open
is0692vs wants to merge 2 commits into
mainfrom
fix/og-github-api-auth-9905796814203422129
Open

🔒 Fix potential Server-Side DoS via Unauthenticated GitHub API Call#516
is0692vs wants to merge 2 commits into
mainfrom
fix/og-github-api-auth-9905796814203422129

Conversation

@is0692vs

@is0692vs is0692vs commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🎯 What: Adds Authorization header to the GitHub API call in src/app/api/og/[username]/route.tsx if process.env.GITHUB_TOKEN is set.
⚠️ Risk: The unauthenticated API call is subject to a strict rate limit (60 req/hr), making the endpoint vulnerable to Server-Side DoS by exhausting the rate limit.
🛡️ Solution: Passes the GITHUB_TOKEN to the GitHub API call, upgrading to the authenticated rate limit (5000 req/hr).


PR created automatically by Jules for task 9905796814203422129 started by @is0692vs

Greptile Summary

GitHubユーザー情報を取得するOG画像APIについて、GITHUB_TOKEN が設定されている場合にBearer認証を追加する変更です。

  • 未認証時のGitHub API制限を、認証済みトークンのクォータへ切り替えます
  • トークン未設定時は従来どおりAuthorizationヘッダーを省略します
  • ただし、共有トークンの集約クォータを保護する制限がなく、分散リクエストによる枯渇経路が残ります

Confidence Score: 4/5

共有GitHubトークンのクォータを分散リクエストで枯渇させられるため、この経路を修正してからマージする必要があります。

IP単位の制限は複数IPからの合計リクエスト数を抑えられず、2つのIPから許容量いっぱいに異なるユーザー名を要求するだけで、共有トークンの通常の時間クォータを超えて後続のOG画像を不完全にできます。

Files Needing Attention: src/app/api/og/[username]/route.tsx

Security Review

共有トークンの追加によってGitHub APIクォータが全利用者で共有されますが、制限は送信元IP単位のままです。異なるユーザー名を使う複数IPからのリクエストで共有クォータを枯渇させ、後続のOG画像を最大24時間、不完全な内容にできます。

How this was verified: IP単位の毎分50件制限から、異なるユーザー名へのGitHub API呼び出しと共有トークンの非OK応答時のフォールバックまでを追跡しました。

Important Files Changed

Filename Overview
src/app/api/og/[username]/route.tsx GitHub API呼び出しへ条件付きBearer認証を追加していますが、共有トークンの集約クォータを保護できず、分散リクエストによる枯渇経路が残っています。

Sequence Diagram

sequenceDiagram
  participant A as 複数の送信元IP
  participant OG as OG画像API
  participant RL as IP単位レート制限
  participant GH as GitHub API
  A->>OG: 異なるusernameへリクエスト
  OG->>RL: 各IPの制限を確認
  RL-->>OG: 各IPで毎分50件を許可
  OG->>GH: 共有GITHUB_TOKENで取得
  GH-->>OG: クォータ枯渇後は403
  OG-->>A: 欠損したOG画像を200で返却
Loading
Prompt To Fix All With AI
### Issue 1
src/app/api/og/[username]/route.tsx:47
**共有トークンのクォータ枯渇**

複数の送信元IPからキャッシュされていない異なるユーザー名へ毎分50件ずつ要求すると、IP単位の制限を通過した合計リクエストが単一の `GITHUB_TOKEN` に集中し、通常の認証済み時間クォータを超えます。クォータ枯渇後のGitHubの403応答はデフォルト値へフォールスルーするため、アバターや統計が欠落したOG画像が200で返され、最大24時間キャッシュされます。

**How this was verified:** IP単位の毎分50件制限から、異なるユーザー名への共有トークン付きAPI呼び出しと非OK応答時のフォールバックまでを追跡しました。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Fix potential Server-Side DoS by adding ..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

…b API call

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
github-user-summary Ignored Ignored Aug 1, 2026 4:33am

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@dosubot dosubot Bot added the bug Something isn't working label Aug 1, 2026
@dosubot

dosubot Bot commented Aug 1, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-09-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about github-user-summary Add Dosu to your team

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@is0692vs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74095874-0373-42ca-b99a-1e4cf87b7284

📥 Commits

Reviewing files that changed from the base of the PR and between e914034 and f54d143.

📒 Files selected for processing (2)
  • src/app/api/og/[username]/route.test.ts
  • src/app/api/og/[username]/route.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Authenticate GitHub OG endpoint requests to prevent rate-limit DoS

🐞 Bug fix ✨ Enhancement 🕐 Less than 10 minutes

Grey Divider

AI Description

• Add optional Authorization header to GitHub user lookup when GITHUB_TOKEN is set
• Reduce risk of server-side DoS caused by unauthenticated GitHub API rate limits
• Preserve existing behavior when no token is configured
Diagram

graph TD
  U([Client]) --> R["Next.js OG route"] --> F["fetch github.com/users"] --> GH{{"GitHub API"}}
  R --> E[("GITHUB_TOKEN env")]

  subgraph Legend
    direction LR
    _u([Client]) ~~~ _svc["Server route"] ~~~ _ext{{External API}} ~~~ _cfg[(Config/Secret)]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add server-side caching for GitHub user lookups
  • ➕ Reduces GitHub API calls regardless of authentication
  • ➕ Mitigates burst traffic and improves latency
  • ➖ Requires cache invalidation/TTL decisions
  • ➖ Adds operational complexity (cache store or Next.js revalidation strategy)
2. Introduce per-IP / per-username rate limiting on the OG route
  • ➕ Directly protects the endpoint from abuse even with a token
  • ➕ Keeps external dependency usage bounded
  • ➖ Needs storage for counters (edge KV/Redis) and tuning
  • ➖ Can block legitimate high-traffic use cases
3. Use a GitHub App installation token instead of a PAT
  • ➕ Better security posture and rotation story than long-lived tokens
  • ➕ Clearer scoping and auditing
  • ➖ More setup and code to mint/refresh tokens
  • ➖ Overkill if a single low-scope token suffices

Recommendation: The PR’s approach is the right minimal mitigation: authenticate the GitHub API call when a token is available to avoid trivial rate-limit exhaustion. Consider adding caching and/or route-level rate limiting if the endpoint is expected to see high or bursty traffic, since a token alone doesn’t eliminate external dependency pressure.

Files changed (1) +1 / -0

Bug fix (1) +1 / -0
route.tsxConditionally add Bearer auth to GitHub Users API request +1/-0

Conditionally add Bearer auth to GitHub Users API request

• Adds an Authorization header to the GitHub API fetch when GITHUB_TOKEN is present in the environment. This upgrades the request to authenticated rate limits while keeping existing behavior unchanged when no token is configured.

src/app/api/og/[username]/route.tsx

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 30 rules

Grey Divider


Action required

1. Untrimmed GitHub token 🐞 Bug ≡ Correctness
Description
The OG route sends Authorization: Bearer ${process.env.GITHUB_TOKEN} without trimming/validating,
so a whitespace-padded secret (common when copy/pasting) will produce an invalid auth header and the
call will still be effectively unauthenticated—undermining the PR’s DoS mitigation. The current
...(process.env.GITHUB_TOKEN && {...}) pattern is also brittle; prefer a normalized token +
explicit ternary/object construction.
Code

src/app/api/og/[username]/route.tsx[47]

+        ...(process.env.GITHUB_TOKEN && { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }),
Evidence
The new OG route header uses the raw env var in a conditional spread, while existing GitHub API code
in the repo trims the token before setting Authorization, indicating the intended/safer pattern
and showing the OG route is currently inconsistent and more error-prone.

src/app/api/og/[username]/route.tsx[44-52]
src/lib/cardDataFetcher.ts[67-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/app/api/og/[username]/route.tsx` conditionally adds the GitHub `Authorization` header using the raw `process.env.GITHUB_TOKEN` value. If the token contains leading/trailing whitespace, the route will send an invalid `Authorization` header and GitHub will treat the request as unauthenticated (or reject it), negating the intended rate-limit increase.

### Issue Context
The repo already has a GitHub fetcher that normalizes the token via `.trim()` before setting `Authorization`.

### Fix Focus Areas
- src/app/api/og/[username]/route.tsx[44-52]

### Suggested change
1. Normalize once:
  - `const token = process.env.GITHUB_TOKEN?.trim();`
2. Build headers with a stable object type:
  - `...(token ? { Authorization: `Bearer ${token}` } : {})`

This matches the approach in `src/lib/cardDataFetcher.ts` and avoids sending an invalid header for whitespace-only tokens.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. No tests for GITHUB_TOKEN 📘 Rule violation ▣ Testability
Description
The new conditional Authorization header branch is not covered by tests, so regressions
(missing/always-sent auth header) could go unnoticed. Add tests that assert request headers with and
without process.env.GITHUB_TOKEN.
Code

src/app/api/og/[username]/route.tsx[47]

+        ...(process.env.GITHUB_TOKEN && { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }),
Evidence
The PR introduces a new runtime branch that adds Authorization based on
process.env.GITHUB_TOKEN, but the route tests only assert that fetch was called with
expect.any(Object) and never validate the presence/absence of the Authorization header.

Rule 226115: Add or update tests for behavioral changes in parsing, validation, aggregation, auth, and caching
src/app/api/og/[username]/route.tsx[45-52]
src/app/api/og/[username]/route.test.ts[45-79]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/app/api/og/[username]/route.tsx` now conditionally adds an `Authorization` header when `process.env.GITHUB_TOKEN` is set, but the existing tests do not assert this behavior.

## Issue Context
This PR changes auth behavior for the GitHub API request; tests should verify both branches:
- When `GITHUB_TOKEN` is set, `fetch` receives `Authorization: Bearer <token>`.
- When `GITHUB_TOKEN` is unset/empty, `Authorization` is not present.

## Fix Focus Areas
- src/app/api/og/[username]/route.tsx[45-52]
- src/app/api/og/[username]/route.test.ts[45-79]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

try {
const res = await fetch(`https://api.github.com/users/${encodeURIComponent(username)}`, {
headers: {
...(process.env.GITHUB_TOKEN && { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security 共有トークンのクォータ枯渇

複数の送信元IPからキャッシュされていない異なるユーザー名へ毎分50件ずつ要求すると、IP単位の制限を通過した合計リクエストが単一の GITHUB_TOKEN に集中し、通常の認証済み時間クォータを超えます。クォータ枯渇後のGitHubの403応答はデフォルト値へフォールスルーするため、アバターや統計が欠落したOG画像が200で返され、最大24時間キャッシュされます。

How this was verified: IP単位の毎分50件制限から、異なるユーザー名への共有トークン付きAPI呼び出しと非OK応答時のフォールバックまでを追跡しました。

Context Used: 日本語で!!! (source)

Knowledge Base Used: Card Data Pipeline

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/api/og/[username]/route.tsx
Line: 47

Comment:
**共有トークンのクォータ枯渇**

複数の送信元IPからキャッシュされていない異なるユーザー名へ毎分50件ずつ要求すると、IP単位の制限を通過した合計リクエストが単一の `GITHUB_TOKEN` に集中し、通常の認証済み時間クォータを超えます。クォータ枯渇後のGitHubの403応答はデフォルト値へフォールスルーするため、アバターや統計が欠落したOG画像が200で返され、最大24時間キャッシュされます。

**How this was verified:** IP単位の毎分50件制限から、異なるユーザー名への共有トークン付きAPI呼び出しと非OK応答時のフォールバックまでを追跡しました。

**Context Used:** 日本語で!!! ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

**Knowledge Base Used:** [Card Data Pipeline](https://app.greptile.com/hiroki-org/-/custom-context/knowledge-base/hiroki-org/github-user-summary/-/docs/card-data-pipeline.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

try {
const res = await fetch(`https://api.github.com/users/${encodeURIComponent(username)}`, {
headers: {
...(process.env.GITHUB_TOKEN && { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. No tests for github_token 📘 Rule violation ▣ Testability

The new conditional Authorization header branch is not covered by tests, so regressions
(missing/always-sent auth header) could go unnoticed. Add tests that assert request headers with and
without process.env.GITHUB_TOKEN.
Agent Prompt
## Issue description
`src/app/api/og/[username]/route.tsx` now conditionally adds an `Authorization` header when `process.env.GITHUB_TOKEN` is set, but the existing tests do not assert this behavior.

## Issue Context
This PR changes auth behavior for the GitHub API request; tests should verify both branches:
- When `GITHUB_TOKEN` is set, `fetch` receives `Authorization: Bearer <token>`.
- When `GITHUB_TOKEN` is unset/empty, `Authorization` is not present.

## Fix Focus Areas
- src/app/api/og/[username]/route.tsx[45-52]
- src/app/api/og/[username]/route.test.ts[45-79]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

try {
const res = await fetch(`https://api.github.com/users/${encodeURIComponent(username)}`, {
headers: {
...(process.env.GITHUB_TOKEN && { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Untrimmed github token 🐞 Bug ≡ Correctness

The OG route sends Authorization: Bearer ${process.env.GITHUB_TOKEN} without trimming/validating,
so a whitespace-padded secret (common when copy/pasting) will produce an invalid auth header and the
call will still be effectively unauthenticated—undermining the PR’s DoS mitigation. The current
...(process.env.GITHUB_TOKEN && {...}) pattern is also brittle; prefer a normalized token +
explicit ternary/object construction.
Agent Prompt
### Issue description
`src/app/api/og/[username]/route.tsx` conditionally adds the GitHub `Authorization` header using the raw `process.env.GITHUB_TOKEN` value. If the token contains leading/trailing whitespace, the route will send an invalid `Authorization` header and GitHub will treat the request as unauthenticated (or reject it), negating the intended rate-limit increase.

### Issue Context
The repo already has a GitHub fetcher that normalizes the token via `.trim()` before setting `Authorization`.

### Fix Focus Areas
- src/app/api/og/[username]/route.tsx[44-52]

### Suggested change
1. Normalize once:
   - `const token = process.env.GITHUB_TOKEN?.trim();`
2. Build headers with a stable object type:
   - `...(token ? { Authorization: `Bearer ${token}` } : {})`

This matches the approach in `src/lib/cardDataFetcher.ts` and avoids sending an invalid header for whitespace-only tokens.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

…b API call

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
@pull-request-size pull-request-size Bot added size/S and removed size/XS labels Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant