0
✦ 品質
🛡 セキュリティ
⚙ 保守性
📋 概要
Laravel 13 + Claude AI を使ったGitHubリポジトリのAIコードレビューサービス。GitHubのAPIでコードを取得し、Anthropic Claude APIで品質・セキュリティ・保守性スコアと修正提案を生成する。
🎯 今すぐ直すべき3点
-
app/Services/ClaudeReviewService.php fixIssue() メソッド内の $fileContents 組み立て部分で、ユーザー入力の $issueTitle/$issueDesc がプロンプト文字列に直接埋め込まれている(sanitizeInput後も)。<user_input>タグ内に '</user_input>' を含む文字列を渡せばタグ境界を突破できるため、str_replace で '</user_input>' をエスケープする処理を追加すること。↗
-
app/Http/Controllers/ReviewController.php store() メソッドで ip_hash を2回計算している($ipHash 変数が宣言されるが Review::create には $request->ip() を直接 hash() して渡している)。$ipHash を一箇所で計算して再利用するよう修正し、デッドコードを除去すること。↗
-
app/Services/GitHubService.php parseUrl() の InvalidArgumentException メッセージに trim前のユーザー入力URLがそのまま含まれる('Invalid GitHub URL: ' . $url)。悪意あるURLでログやエラーレスポンスに任意文字列が混入するログインジェクションのリスクがあるため、URLをログ出力前に mb_substr($url, 0, 100) でトリミング& htmlspecialchars でエスケープすること。↗
🔍 検出された問題 (8件)
🟥 Critical
📄 app/Services/ClaudeReviewService.php
プロンプトインジェクション対策が不完全(fixIssue)
ClaudeReviewService.php の fixIssue() で $issueTitle/$issueDesc を sanitizeInput() した後、<user_input>...</user_input> タグで囲んでプロンプトに埋め込んでいるが、入力に '</user_input>' という文字列が含まれるとタグを早期終了させてプロンプト本体部分に任意のテキストを注入できる。攻撃者がシステムプロンプトの指示を上書きしてClaudeに任意の応答をさせられるリスクがある。
🟥 Critical
📄 Dockerfile
Dockerfileで秘密鍵なしに composer install が実行される可能性
Dockerfile の Stage 2 で COPY . . の後に composer install を実行しているが、ビルドコンテキストに .env が存在する場合そのまま含まれてしまう。.dockerignore で .env を除外しているかが確認できず、本番イメージにAPIキー等が焼き込まれるリスクがある。.dockerignore に .env を明示的に追記し、secrets mount を使った composer install に切り替えること。
🟧 Warning
📄 app/Http/Controllers/ReviewController.php
デッドコード:$ipHash 変数が宣言されるが使用されない
ReviewController.php の store() メソッドで $ipHash = hash('sha256', $request->ip()) と宣言されているが、直後の Review::create() では 'ip_hash' => hash('sha256', $request->ip()) と再度計算されており $ipHash は一切参照されない。コードの可読性を下げ、将来的に片方だけ変更された場合に不整合が生じる危険がある。
🟧 Warning
📄 app/Services/GitHubService.php
GitHubService::getFileTree() のブランチフォールバックが不完全
getFileTree() で main が失敗したとき master に自動フォールバックしているが、$stats['default_branch'] を取得した後に ProcessReviewJob でその値を渡しているため、実際には main/master 以外のデフォルトブランチ(例: trunk, develop)でも main→master フォールバックが走り、正しくないツリーを取得する可能性がある。getRepoStats() で取得した default_branch を常に優先してフォールバック不要にすること。
🟧 Warning
📄 app/Services/ClaudeReviewService.php
ClaudeReviewService::review() のシステムプロンプトがこのアプリ自身のプロンプトインジェクション対策をほぼコピーしている
ClaudeReviewService.php の $system ヒアドキュメントの内容が、このシステム自身がClaude APIから受け取った Human プロンプトとほぼ同一内容(ロール変更禁止など)であることが確認できる。自己参照的な構造はプロンプトの意図を曖昧にし、将来のメンテナンスで混乱を招く。アプリ固有のドメインルールに絞ったシステムプロンプトに書き直すこと。
🟧 Warning
📄 .env.example
SESSION_ENCRYPT=true だが SESSION_DRIVER=database でのキー管理が不明
.env.example で SESSION_ENCRYPT=true が設定されているが、APP_KEY が空のままビルドされた場合(例: CI/CD環境でartisan key:generateを忘れた場合)、セッション暗号化が機能しないまま起動する。Dockerfile の docker-entrypoint.sh でキー生成の確認を行っているか不明なため、entrypoint で php artisan key:generate --no-interaction の冪等実行を保証すること。
🟨 Suggestion
📄 app/Models/Review.php
Review::getOverallScoreAttribute() でゼロ除算の可能性
3スコアすべてがnullの場合でも (0+0+0)/3=0 で問題ないが、将来スコア項目を増減した際に除数のハードコード値 '3' を変更し忘れるリスクがある。array_filter と count を使った動的な平均計算に変更することで保守性が上がる。
🟨 Suggestion
📄 composer.json
composer.json の name が 'laravel/laravel' のままでリリースされている
composer.json の name フィールドが Laravel スケルトンのデフォルト値 'laravel/laravel' のまま。Packagist に誤って公開した場合に公式パッケージを上書きする名前空間衝突が起きるほか、プロジェクトの識別性がない。'mize1978/codelens-ai' に変更すること。
✅ 良い点
- app/Jobs/ProcessReviewJob.php の sanitizeError() メソッドが Bearer トークンを正規表現でマスクしており、ジョブ失敗時にAPIキーがDBのerror_messageカラムに漏洩するリスクを防いでいる。
- app/Http/Controllers/ReviewController.php の store()/fix() でIPをSHA-256ハッシュ化してから RateLimiter のキーに使用しており、生IPアドレスのDBへの永続化を回避している設計が適切。
- app/Models/Review.php の review_data カラムに 'array' キャストを設定し、ClaudeAPIのJSONレスポンスをそのままEloquentで型安全に扱えるようにしている点が保守性に貢献している。
🔧 リファクタ提案
- ClaudeReviewService.php の call() / parseJson() / buildReviewPrompt() が1ファイルに混在している可能性が高い。PromptBuilder クラスを分離し、ClaudeReviewService はHTTPとレスポンスパースのみを担当する単一責任構造に変更すること。
- ProcessReviewJob.php の handle() メソッドが GitHub取得・ファイル選択・Claude呼び出し・DB更新を一手に担っており、見通しが悪い。fetchFiles() / buildReviewPayload() などプライベートメソッドに分割し、try-catch の粒度を細かくしてステップ別のエラーハンドリングを可能にすること。
- ReviewController.php の fix() メソッドで Review モデルの owner/repo をそのまま GitHubService に渡してファイルを再取得しているが、review_data['analyzed_files'] に取得済みパスが保存されているため、二重取得を避けてキャッシュ活用ができる設計にリファクタすること。
🔒 セキュリティ注意点
- ANTHROPIC_API_KEY と GITHUB_TOKEN が .env.example に空で定義されているが、本番の Render.com 環境変数経由で注入されていることを前提としている。Dockerfile の ENV 命令でハードコードされていないことは確認できるが、docker-entrypoint.sh の内容が未確認のため、同ファイルでのシークレット echo やログ出力がないことを必ず確認すること。
- GitHubService::getFileContent() でファイルサイズを 1MB でフィルタしているが、base64_decode 前の content 文字列長チェックがないため、巨大な base64 文字列のデコードで一時的に大量メモリを消費する DoS ベクターになり得る。data['size'] チェックを content デコード前に行っている現実装は正しいが、API レスポンス自体が偽装された size を返す可能性も踏まえて strlen($data['content']) の上限チェックも追加すること。
📤 PRコメント出力
このレビューをGitHub PRコメントとして貼り付けられるMarkdown形式で出力します。