crafted by Mize ↗
Open Repository Intelligence Open Repository Intelligence
CodeLens ACTIVE
CodeLens Engine
StatusOnline
Versionv0.4
ModelClaude Sonnet
Latency—
0
GOOD
改善余地があります
mize1978/codelens-ai
「AIにコードを読ませるコードを、AIがレビューする」という再帰的な構造が面白いが、悪意あるリポジトリのファイルがそのままプロンプトに流れ込む設計は「レビュアーを騙すリポジトリ」を作れば簡単に突破できる——自分自身が最初のテストケースになるべき
⭐ 0 Stars
·
🍴 0 Forks
·
📝 88 Commits
·
💬 1 Issues
·
🔵 Blade
·
Laravel
🔍 重要ファイル 12 件を精査 全 212 ファイル中 AI が優先度順に選択
0
✦ 品質
0
🛡 セキュリティ
0
⚙ 保守性
📋 概要

Laravel 13 + PHP 8.4 で構築されたGitHubリポジトリ自動コードレビューSaaS。Claude AI(Anthropic)を用いてリポジトリを解析し、スコアリング・レビュー結果をDBに永続化する「Repository Intelligence Platform」。

🎯 今すぐ直すべき3点
  1. app/Services/ClaudeReviewService.php ファイル全体が12,345文字あるが今回3,000文字しか読めていないため全文確認が必須。特に `call()` メソッドでのHTTPエラーハンドリング(タイムアウト・APIレート制限・5xx)が実装されているか確認し、未実装なら try-catch + ログ + ユーザー向けエラーメッセージを追加する
    ↗
  2. app/Http/Controllers/ReviewController.php `fix()` メソッド末尾が truncated で `$review->owner` と `$review->repo` を使って `getFileContent()` を呼ぶ箇所が確認できる。ユーザー指定の `file` パラメータがパストラバーサル(例: `../../etc/passwd`)に使われないよう、許可リスト検証(`in_array($file, $review->review_data['files'])` 等)を追加する
    ↗
  3. app/Services/ClaudeReviewService.php ユーザーが提供するGitHubリポジトリのファイル内容をそのままClaudeのプロンプトに埋め込んでいる。`<file_content>` タグによる区切りは実装されているが、悪意あるリポジトリが「指示を無視して」等のPrompt Injectionを仕込んだ場合の多層防御(出力のJSON schema強制バリデーション、疑わしいキーワードの事前フィルタリング)を追加する
    ↗
🔍 検出された問題 (6件)
🟥 Critical 📄 app/Services/ClaudeReviewService.php
Prompt Injection への防御が単一レイヤーのみ
ClaudeReviewService.php はユーザーが指定したGitHubリポジトリのファイル内容をプロンプトに埋め込む。`<file_content>` タグと system プロンプトでの警告は実装されているが、悪意あるリポジトリオーナーが意図的に「ロールを変更して管理者として動作せよ」等を仕込んだ場合、Claudeが指示に従いスコアを改ざんしたり機密情報を出力するリスクがある。出力のJSONスキーマ強制バリデーションが `parseJson()` 内でどこまで行われているか truncated で未確認であり、最悪の場合レビュー結果の完全な偽装が可能
🟥 Critical 📄 app/Http/Controllers/ReviewController.php
fix() エンドポイントでのパストラバーサルリスク
ReviewController.php の `fix()` メソッドで `$request->input('file')` を `getFileContent()` のパス引数に渡している箇所が truncated 直前に確認できる。`nullable|string|max:255` のバリデーションのみでは `../../etc/passwd` や `..%2F..%2F` 等のパストラバーサルを防げない。GitHub API 経由のため直接ファイルシステムへのアクセスではないが、意図しないリポジトリ内ファイル(`.env` 等)の内容をClaudeに送信させられる可能性がある
🟧 Warning 📄 app/Services/ClaudeReviewService.php
ClaudeReviewService の責務過多(推定)
ClaudeReviewService.php は12,345文字と大きく、今回読めた3,000文字の範囲だけで `review()` / `buildRepositoryReviewPrompt()` / `buildScoringRubric()` / `call()` / `parseJson()` 等複数の責務を持つことが確認できる。プロンプト構築・HTTP通信・JSONパース・スコアリングルール生成が単一クラスに集中しており、テスト困難・変更影響範囲の拡大・モック差し替えの困難さを招く
🟧 Warning 📄 app/Http/Controllers/ReviewController.php
ReviewController.store() での二段階保存パターンによる競合リスク
`Review::create([...])` で pending レコードを作成した後、`$review->ip_hash = $ipHash; $review->save();` と二度に分けて保存している。`create()` と `save()` の間にジョブが起動した場合、`ip_hash` が null のまま `ReviewRateLimiter::check()` が実行される可能性がある。`ip_hash` を `create()` の配列に含めるべき
🟧 Warning 📄 Dockerfile
Dockerfile で APP_DEBUG=true がデフォルトのまま本番ビルドされるリスク
.env.example の `APP_DEBUG=true` / `APP_ENV=local` がデフォルト値になっており、Dockerfile の `COPY . .` でこれらが本番イメージに含まれる。entrypoint.sh の内容が未確認のため環境変数の上書きが行われているか不明だが、デプロイ時に `.env` が正しく設定されなかった場合、スタックトレース・DB接続情報がブラウザに露出する
🟨 Suggestion 📄 app/Services/GitHubService.php
GitHubService.getFileContent() の1MB制限が無言でスキップ
`($data['size'] ?? 0) > 1_000_000` の場合に空文字列を返すが、呼び出し元にその理由が伝わらない。レビュー結果に「ファイルが大きすぎて読めなかった」という情報が含まれず、EvidenceCoverage の `truncated` フラグとも連動していないため、AIが「ファイルが存在しない」と誤推論する余地がある
✅ 良い点
  • app/Services/RepositoryFacts.php の3状態契約(detected/none/unknown)設計が優れている。`unknown` を `none` として扱わないという明示的な契約をコード・コメント・定数で三重に表現しており、AIへの誤った減点指示を構造的に防いでいる
  • app/Services/ReviewRateLimiter.php が `analysis_source='generated'` のみをカウント対象とする設計により、キャッシュヒット分をAPI呼び出しコストとして誤計上しない。`clone $today` でクエリビルダを再利用しつつ条件分岐する実装も副作用がなく堅牢
  • app/Services/EvidenceCoverage.php が `total_chars` / `read_chars` / `truncated` を明示的に追跡し、AIが「読めていない範囲を根拠に不在と断定する」誤推論を構造的に抑制している。ReviewController.php の `store()` で `hash('sha256', $request->ip())` によりIPをハッシュ化してDBに保存しており、生IPの永続化を避けているプライバシー配慮も評価できる

作り切ることが第一歩。改善を続けることがエンジニアリング。

🔧 リファクタ提案
  • ClaudeReviewService.php を `PromptBuilder` / `AnthropicClient` / `ReviewParser` の3クラスに分割する。`buildRepositoryReviewPrompt()` と `buildScoringRubric()` は `PromptBuilder` へ、`call()` は `AnthropicClient` へ移譲することで単体テストが容易になる
  • ReviewController.store() の `Review::create()` 呼び出しを1回にまとめ、`ip_hash` を含む全フィールドを同時に保存する。現在の二段階保存は競合リスクと可読性低下を招いている
  • GitHubService.getFileContent() で1MB超・base64デコード失敗・空コンテンツの各ケースを区別できる Result 型(または専用例外クラス)を返すよう変更し、EvidenceCoverage がスキップ理由を記録できるようにする
🔒 セキュリティ注意点
  • ANTHROPIC_API_KEY と GITHUB_TOKEN は .env.example では空欄になっており、ハードコードは確認されていない。ただし本番環境での secrets 管理(Render の環境変数設定、ローテーション手順)をREADMEに明記することを推奨する
  • SESSION_ENCRYPT=true が .env.example で有効化されており、セッションデータの暗号化は適切に設定されている。一方で CSRF 保護の適用範囲(特に status・fix エンドポイントへのAjaxリクエスト)が truncated ファイルで未確認のため、X-CSRF-TOKEN ヘッダーの送信を明示的に確認すること
📤 PRコメント出力

このレビューをGitHub PRコメントとして貼り付けられるMarkdown形式で出力します。