Skip to content

🔒 Fix: Remove test-auth endpoints from default production build#1100

Closed
is0692vs wants to merge 2 commits into
stagingfrom
fix/dynamic-test-auth-env-13354622219961251330
Closed

🔒 Fix: Remove test-auth endpoints from default production build#1100
is0692vs wants to merge 2 commits into
stagingfrom
fix/dynamic-test-auth-env-13354622219961251330

Conversation

@is0692vs

@is0692vs is0692vs commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

🎯 What:
Changed the conditional mounting of E2E testing endpoints from a build-time check (process.env.NODE_ENV) to a dynamic runtime check based on the environment variables of the actual incoming request context (c.env.ENABLE_TEST_AUTH).

⚠️ Risk:
Using process.env.NODE_ENV at module scope could result in testAuth endpoints being unintentionally included in production environments, depending on how the application is built and bundled (especially in environments like Cloudflare Workers where the Node process object is not safely defined at runtime and NODE_ENV might be mistakenly transpiled as non-production during some staging/development deployments that accidentally roll to production). An attacker could leverage these endpoints to forge session tokens or modify organization metadata without authentication.

🛡️ Solution:
Removed the build-time conditional if (process.env.NODE_ENV !== "production"). Instead, added a route handler app.all("/api/test-auth/*", async (c) => { ... }) that intercepts calls to /api/test-auth/*. The handler reads the c.env.ENABLE_TEST_AUTH value from the request environment context and responds with 404 Not Found if it is not explicitly enabled. If it is enabled, the code uses a lazy dynamic import to load and execute the test router within a temporary Hono context.


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

Greptile Summary

test-auth エンドポイントの有効化判定を、ビルド時の process.env.NODE_ENV チェックから、Cloudflare Workers のランタイムバインディング c.env.ENABLE_TEST_AUTH を用いる動的ルートハンドラへ置き換えています。これにより CI 環境の NODE_ENV 設定ミスによる意図しないルート公開リスクは解消されます。

  • ランタイムチェックへの切替: app.all(\"/api/test-auth/*\", ...) ハンドラが ENABLE_TEST_AUTH !== \"true\" の場合に 404 を返すため、Cloudflare バインディングを明示的に設定しない限りエンドポイントにはアクセスできなくなった。
  • バンドルへの残留: await import(\"./routes/test-auth\") は動的インポートだが wrangler/esbuild は既定ですべてを単一ファイルにバンドルするため、JWT 生成・ユーザー upsert ロジックを含む test-auth.ts は本番バイナリにも含まれる。PR タイトルの「本番ビルドから除去」は実態と異なる。
  • 構造上の非効率: リクエストごとに new Hono() インスタンスを生成してルートを登録する形になっており、モジュールスコープのシングルトンとして持つ形より冗長。

Confidence Score: 3/5

ランタイムチェックへの移行という方向性は正しいが、動的インポートにより JWT 生成コードが本番バンドルに残り続ける点が PR タイトルの主張と食い違っている。

セキュリティ上の意図は正しく c.env.ENABLE_TEST_AUTH による Runtime Gate は Cloudflare Workers バインディングに依存するため信頼性は高い。ただし PR タイトルで「本番ビルドから除去」と明示しているにもかかわらず esbuild のデフォルト挙動により test-auth.ts(JWT 署名・DB 書き込みロジック含む)は本番バンドルに残ったままになる。この乖離はセキュリティ監査で誤解を生みうる。加えてリクエストごとの new Hono() 生成と空の catch ブロックなどコード品質面での改善余地がある。

apps/api/src/index.ts — 動的インポートのバンドル挙動と per-request Hono 生成の扱いを要確認。

Important Files Changed

Filename Overview
apps/api/src/index.ts test-auth エンドポイントの保護をビルド時チェックからランタイムチェックへ移行。セキュリティの方向性は正しいが、動的インポートにより JWT 生成コードが本番バンドルに含まれ続けるため PR タイトルの「本番ビルドから除去」という主張と実態が乖離している。

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant OuterApp as Hono App (index.ts)
    participant CSRF as CSRF Middleware
    participant Handler as /api/test-auth/* Handler
    participant TempApp as tempApp (新規 Hono)
    participant TestAuth as testAuth Router

    Client->>OuterApp: POST /api/test-auth/test-token
    OuterApp->>CSRF: CSRF チェック
    alt "ENABLE_TEST_AUTH=true かつ x-test-auth-secret 有効"
        CSRF-->>OuterApp: bypass (next)
    else
        CSRF-->>Client: 403 Forbidden
    end
    OuterApp->>Handler: "マッチ /api/test-auth/*"
    Handler->>Handler: c.env.ENABLE_TEST_AUTH チェック
    alt "ENABLE_TEST_AUTH != true"
        Handler-->>Client: 404 Not Found
    else
        Handler->>Handler: dynamic import routes/test-auth
        Handler->>TempApp: new Hono() + tempApp.route
        Handler->>TempApp: tempApp.fetch(c.req.raw, c.env, executionCtx)
        TempApp->>TestAuth: ルーティング
        TestAuth->>TestAuth: "ENABLE_TEST_AUTH & TEST_AUTH_SECRET 再チェック"
        TestAuth->>TestAuth: x-test-auth-secret 検証 (timingSafeEqual)
        alt 認証成功
            TestAuth-->>Client: 200 OK
        else
            TestAuth-->>Client: 401 Unauthorized
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant OuterApp as Hono App (index.ts)
    participant CSRF as CSRF Middleware
    participant Handler as /api/test-auth/* Handler
    participant TempApp as tempApp (新規 Hono)
    participant TestAuth as testAuth Router

    Client->>OuterApp: POST /api/test-auth/test-token
    OuterApp->>CSRF: CSRF チェック
    alt "ENABLE_TEST_AUTH=true かつ x-test-auth-secret 有効"
        CSRF-->>OuterApp: bypass (next)
    else
        CSRF-->>Client: 403 Forbidden
    end
    OuterApp->>Handler: "マッチ /api/test-auth/*"
    Handler->>Handler: c.env.ENABLE_TEST_AUTH チェック
    alt "ENABLE_TEST_AUTH != true"
        Handler-->>Client: 404 Not Found
    else
        Handler->>Handler: dynamic import routes/test-auth
        Handler->>TempApp: new Hono() + tempApp.route
        Handler->>TempApp: tempApp.fetch(c.req.raw, c.env, executionCtx)
        TempApp->>TestAuth: ルーティング
        TestAuth->>TestAuth: "ENABLE_TEST_AUTH & TEST_AUTH_SECRET 再チェック"
        TestAuth->>TestAuth: x-test-auth-secret 検証 (timingSafeEqual)
        alt 認証成功
            TestAuth-->>Client: 200 OK
        else
            TestAuth-->>Client: 401 Unauthorized
        end
    end
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/api/src/index.ts:159-166
**テスト用コードが本番バンドルに含まれてしまう**

PR タイトルは「本番ビルドから test-auth エンドポイントを除去」と謳っていますが、`await import("./routes/test-auth")` は動的インポートであり、Cloudflare Workers の bundler(esbuild/wrangler)はデフォルトで動的インポートも含めてすべてを単一ファイルにバンドルします。つまり JWT 生成・ユーザー upsert などの機密ロジックが書かれた `test-auth.ts` は本番バンドルに必ず含まれます。ランタイムチェック自体は正しい方向ですが、「バンドルからの除去」という PR タイトルは実態と合っていません。本番バンドルから確実に除外したい場合は、esbuild の `--define` による条件付きコンパイルや test-auth を別 Worker に切り出すことを検討してください。

### Issue 2 of 3
apps/api/src/index.ts:162-165
`c.executionCtx` へのアクセス失敗を空の `catch {}` で無言に捨てると、ローカル開発などで予期しないエラーが起きた際に原因が分からなくなります。なぜ無視できるのかを示すコメントを追加することを推奨します。

```suggestion
  let executionCtx;
  try {
    // c.executionCtx は Cloudflare Workers 以外の環境(miniflare など)では
    // アクセス時に例外を投げる場合があるため、undefined にフォールバックする。
    executionCtx = c.executionCtx;
  } catch {}
```

### Issue 3 of 3
apps/api/src/index.ts:160-161
**リクエストごとに Hono インスタンスを生成している**

`ENABLE_TEST_AUTH=true` の状態でリクエストが来るたびに `new Hono()` インスタンスが生成され、`tempApp.route(...)` でルートが登録されます。モジュールキャッシュにより `testAuth` 自体の再インポートはありませんが、Hono ルーターの構築コストが毎回発生します。モジュールスコープに `tempApp` シングルトンを持つ形にすると構築コストを初回のみに抑えられます。

Reviews (1): Last reviewed commit: "fix: resolve empty block and image loadi..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

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 Jul 12, 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)
open-shelf Ignored Ignored Jul 12, 2026 8:11am

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@dosubot dosubot Bot added the javascript Pull requests that update javascript code label Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next review available in: 34 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2b0c8128-1736-470e-8400-0c38d872dbb1

📥 Commits

Reviewing files that changed from the base of the PR and between 1a4aa6d and 1cef92d.

📒 Files selected for processing (2)
  • apps/api/src/index.ts
  • apps/web/src/app/papers/[id]/paper-detail-client.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dynamic-test-auth-env-13354622219961251330

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.

@github-actions
github-actions Bot changed the base branch from main to staging July 12, 2026 08:04
@github-actions

Copy link
Copy Markdown

このリポジトリでは staging 先行フローを採用しています。PR のターゲットを staging に変更しました。staging で動作確認後、stagingmain の PR を作成してください。

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request refactors the registration of test authentication routes in apps/api/src/index.ts to dynamically handle requests under /api/test-auth/* based on the ENABLE_TEST_AUTH environment variable. The reviewer noted that recreating the Hono instance and registering routes on every request introduces performance overhead, and suggested caching the initialized Hono instance at the module level to optimize performance.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread apps/api/src/index.ts
Comment on lines +155 to +167
app.all("/api/test-auth/*", async (c) => {
if (c.env.ENABLE_TEST_AUTH !== "true") {
return c.json({ error: "Not Found" }, 404);
}
const { default: testAuth } = await import("./routes/test-auth");
app.route("/api/test-auth", testAuth);
}
const tempApp = new Hono<{ Bindings: Env; Variables: Variables }>();
tempApp.route("/api/test-auth", testAuth);
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {}
return tempApp.fetch(c.req.raw, c.env, executionCtx);
});

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.

medium

Recreating the Hono instance and calling tempApp.route on every single request introduces significant performance overhead due to router compilation and dynamic import resolution. This can noticeably slow down E2E test suites.

By caching the initialized Hono instance at the module level, we ensure that the dynamic import and router setup only happen once on the first request to /api/test-auth/*.

Suggested change
app.all("/api/test-auth/*", async (c) => {
if (c.env.ENABLE_TEST_AUTH !== "true") {
return c.json({ error: "Not Found" }, 404);
}
const { default: testAuth } = await import("./routes/test-auth");
app.route("/api/test-auth", testAuth);
}
const tempApp = new Hono<{ Bindings: Env; Variables: Variables }>();
tempApp.route("/api/test-auth", testAuth);
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {}
return tempApp.fetch(c.req.raw, c.env, executionCtx);
});
let testAuthApp: Hono<{ Bindings: Env; Variables: Variables }> | null = null;
app.all("/api/test-auth/*", async (c) => {
if (c.env.ENABLE_TEST_AUTH !== "true") {
return c.json({ error: "Not Found" }, 404);
}
if (!testAuthApp) {
const { default: testAuth } = await import("./routes/test-auth");
testAuthApp = new Hono<{ Bindings: Env; Variables: Variables }>();
testAuthApp.route("/api/test-auth", testAuth);
}
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {}
return testAuthApp.fetch(c.req.raw, c.env, executionCtx);
});
References
  1. Ensure that test-auth routes are conditionally imported and registered to avoid exposure in production environments.

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
Comment thread apps/api/src/index.ts
Comment on lines 159 to +166
const { default: testAuth } = await import("./routes/test-auth");
app.route("/api/test-auth", testAuth);
}
const tempApp = new Hono<{ Bindings: Env; Variables: Variables }>();
tempApp.route("/api/test-auth", testAuth);
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {
// Ignore errors when executionCtx is not available

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 テスト用コードが本番バンドルに含まれてしまう

PR タイトルは「本番ビルドから test-auth エンドポイントを除去」と謳っていますが、await import("./routes/test-auth") は動的インポートであり、Cloudflare Workers の bundler(esbuild/wrangler)はデフォルトで動的インポートも含めてすべてを単一ファイルにバンドルします。つまり JWT 生成・ユーザー upsert などの機密ロジックが書かれた test-auth.ts は本番バンドルに必ず含まれます。ランタイムチェック自体は正しい方向ですが、「バンドルからの除去」という PR タイトルは実態と合っていません。本番バンドルから確実に除外したい場合は、esbuild の --define による条件付きコンパイルや test-auth を別 Worker に切り出すことを検討してください。

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/api/src/index.ts
Line: 159-166

Comment:
**テスト用コードが本番バンドルに含まれてしまう**

PR タイトルは「本番ビルドから test-auth エンドポイントを除去」と謳っていますが、`await import("./routes/test-auth")` は動的インポートであり、Cloudflare Workers の bundler(esbuild/wrangler)はデフォルトで動的インポートも含めてすべてを単一ファイルにバンドルします。つまり JWT 生成・ユーザー upsert などの機密ロジックが書かれた `test-auth.ts` は本番バンドルに必ず含まれます。ランタイムチェック自体は正しい方向ですが、「バンドルからの除去」という PR タイトルは実態と合っていません。本番バンドルから確実に除外したい場合は、esbuild の `--define` による条件付きコンパイルや test-auth を別 Worker に切り出すことを検討してください。

How can I resolve this? If you propose a fix, please make it concise.

Comment thread apps/api/src/index.ts
Comment on lines +162 to +165
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {

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.

P2 c.executionCtx へのアクセス失敗を空の catch {} で無言に捨てると、ローカル開発などで予期しないエラーが起きた際に原因が分からなくなります。なぜ無視できるのかを示すコメントを追加することを推奨します。

Suggested change
let executionCtx;
try {
executionCtx = c.executionCtx;
} catch {
let executionCtx;
try {
// c.executionCtx は Cloudflare Workers 以外の環境(miniflare など)では
// アクセス時に例外を投げる場合があるため、undefined にフォールバックする。
executionCtx = c.executionCtx;
} catch {}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/api/src/index.ts
Line: 162-165

Comment:
`c.executionCtx` へのアクセス失敗を空の `catch {}` で無言に捨てると、ローカル開発などで予期しないエラーが起きた際に原因が分からなくなります。なぜ無視できるのかを示すコメントを追加することを推奨します。

```suggestion
  let executionCtx;
  try {
    // c.executionCtx は Cloudflare Workers 以外の環境(miniflare など)では
    // アクセス時に例外を投げる場合があるため、undefined にフォールバックする。
    executionCtx = c.executionCtx;
  } catch {}
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread apps/api/src/index.ts
Comment on lines +160 to +161
const tempApp = new Hono<{ Bindings: Env; Variables: Variables }>();
tempApp.route("/api/test-auth", testAuth);

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.

P2 リクエストごとに Hono インスタンスを生成している

ENABLE_TEST_AUTH=true の状態でリクエストが来るたびに new Hono() インスタンスが生成され、tempApp.route(...) でルートが登録されます。モジュールキャッシュにより testAuth 自体の再インポートはありませんが、Hono ルーターの構築コストが毎回発生します。モジュールスコープに tempApp シングルトンを持つ形にすると構築コストを初回のみに抑えられます。

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/api/src/index.ts
Line: 160-161

Comment:
**リクエストごとに Hono インスタンスを生成している**

`ENABLE_TEST_AUTH=true` の状態でリクエストが来るたびに `new Hono()` インスタンスが生成され、`tempApp.route(...)` でルートが登録されます。モジュールキャッシュにより `testAuth` 自体の再インポートはありませんが、Hono ルーターの構築コストが毎回発生します。モジュールスコープに `tempApp` シングルトンを持つ形にすると構築コストを初回のみに抑えられます。

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@is0692vs

Copy link
Copy Markdown
Contributor Author

test-authエンドポイント制御というPR目的に無関係な paper-detail-client のImage変更を混在させているため閉じます。機能はクリーンなPRに分離してください。

@is0692vs is0692vs closed this Jul 13, 2026
@is0692vs
is0692vs deleted the fix/dynamic-test-auth-env-13354622219961251330 branch July 13, 2026 01:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update javascript code size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant