Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/app/api/og/[username]/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ describe("OG Image Route", () => {
expect(await res.text()).toBe("Invalid username");
});

it("should generate image for valid username with authorization header when GITHUB_TOKEN is set", async () => {
vi.stubEnv('GITHUB_TOKEN', 'test_token');
const mockFetch = vi.spyOn(global, "fetch").mockImplementation(() => Promise.resolve(new Response(JSON.stringify({ name: "Valid User" }), { status: 200 })));

const req = new NextRequest("http://localhost/api/og/validuser");
await GET(req, { params: Promise.resolve({ username: "validuser" }) });

expect(mockFetch).toHaveBeenCalledWith("https://api.github.com/users/validuser", expect.objectContaining({
headers: expect.objectContaining({
Authorization: "Bearer test_token"
})
}));
vi.unstubAllEnvs();
});

it("should generate image for valid username", async () => {
const mockFetch = vi.spyOn(global, "fetch").mockImplementation(() => Promise.resolve(new Response(JSON.stringify({ name: "Valid User", bio: "Short bio", avatar_url: "https://example.com/avatar.png", followers: 100, public_repos: 50 }), { status: 200 })));

Expand Down
1 change: 1 addition & 0 deletions src/app/api/og/[username]/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
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.

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

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

Accept: "application/vnd.github.v3+json",
"User-Agent": "github-user-summary",
},
Expand Down Expand Up @@ -84,7 +85,7 @@
}}
>
{avatarUrl && (
<img

Check warning on line 88 in src/app/api/og/[username]/route.tsx

View workflow job for this annotation

GitHub Actions / Lint

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
src={sanitizeUrl(avatarUrl)}
alt=""
width={120}
Expand Down
Loading