# セキュリティ方針

CodeRouter は、ローカルファーストのルーターです。コーディングエージェント (Claude Code など) と、1 つ以上の LLM エンドポイントの間に立ちます。エンドポイントの中には**状態を持ち、課金が発生し、長命のシークレットで認証される**リモート有料 API も含まれます。以下のポリシーはすべて、このスレットモデルから派生しています。

このドキュメントは 2 つのことを扱います。

1. **CodeRouter 自体が安全を保つためにしていること** — コードまたはプロセスで強制される設計上の不変項・CI ゲート・方針
2. **運用者が CodeRouter を走らせるときにすべきこと** — CodeRouter 側では決められない選択 (鍵をどこに置くか、どのプロバイダを信頼するか、マシンをどうネットワークに繋ぐか)

v1.0 時点のベースラインは「**多層防御、最小限のアタックサーフェス**」です。ここに書かれていることはどれも絶対ではありません。このプロジェクトは**1 人で端から端まで監査できるサイズに保つ**ことを意図していて、その小ささの維持から安全特性が生まれています。

---

## 1. シークレットと資格情報

**方針**: API キーは設定ファイルに書きません。`providers.yaml` は環境変数名で参照し (`api_key_env: OPENROUTER_API_KEY`)、ローダが起動時に解決します。指定された環境変数が存在しなければ、該当プロバイダは**スタブ化せずにスキップ**されます。呼び出し側には明示的なエラーが返り、別階層に静かにフォールスルーすることはありません。

**根拠**: 設定ファイルは事故でコミットされます。環境変数は `echo` ではシェルに出てきても `git` には乗りません。この分離は慣習ではなく**機構**で担保されています。`ProviderConfig` 型には生の API キーを受ける欄がそもそも無いので、コミット対象のファイルに鍵を書き込む場所が物理的に存在しません。

**CI での強制**: `secret-scan` ジョブが push / PR の都度、全コミット履歴に対して `gitleaks` を走らせます。検出されたらビルド失敗。

**運用者のチェックリスト**:

- 鍵は `~/.zshenv` / `~/.bashrc` / `launchctl setenv`、あるいはシークレットマネージャ (1Password CLI `op run --env-file=...`、macOS Keychain など) に置く。リポジトリ内の `.env` には置かない
- プロバイダ鍵は定期的にローテート。ルーターは鍵と進行中のリクエストを紐付けるような状態を一切持ちません
- Issue や PR のコメントに鍵を貼らない

---

## 2. サプライチェーン衛生

2023〜2025 年にかけて、Python と GitHub Actions のエコシステムでサプライチェーン攻撃が日常化しました。パッケージハイジャック (`ctx`)、タイポスクワット、タグの指し替え (`tj-actions/changed-files`)、メンテナアカウントの乗っ取りなど。CodeRouter の方針は**どの上流の単発侵害も黙って通さない**よう、多層化してあります。

### 2.1 最小限のランタイム依存

ランタイム依存は意図的に **5 パッケージ**に絞っています: `fastapi`、`uvicorn[standard]`、`httpx`、`pydantic`、`pyyaml`。

プロバイダ SDK (`anthropic`、`openai`、`litellm`、`langchain`) は**コードレベルで禁止**されています。CI の `test` ジョブがソースに対して `import anthropic|openai|litellm|langchain` を grep し、一致すればビルド失敗。ルーターは `httpx` で各 wire プロトコルを直接喋ります。

これは設計上の不変項 (plan.md §5.4) であると同時に、**アタックサーフェスの選択**でもあります。よく監査されている 5 つの著名パッケージは、便利 SDK 経由で引き込まれる膨大な推移的依存グラフより信頼できるからです。

### 2.2 Lockfile による固定インストール

`uv.lock` はすべての直接・推移依存をバージョンとハッシュで厳密にピン留めしています。CI では `uv sync --frozen --extra dev` を使っていて、`pyproject.toml` と lockfile にドリフトがあれば install 自体を拒否します。新しい推移依存が `main` に現れるには、**誰かがレビューした明示的な lockfile 更新**が必要です。

### 2.3 複数ソースの CVE 監査

アドバイザリ DB の対応範囲が完全には重ならないので、push 時に 2 つのスキャナを走らせています:

| スキャナ | データソース | カバー範囲 |
|---|---|---|
| `pip-audit` | PyPA Advisory Database (Python の一次フィード) | PyPA にミラーされた CVE |
| OSV-Scanner | Google OSV (GHSA + 言語横断) | PyPA 未反映の GHSA、エコシステム横断のアドバイザリ |

検出が 1 件でもあればビルド失敗。両フィードにはアドバイザリ反映のタイムラグが実在していて、**典型的に OSV の方が PyPA より数時間〜1 日早い**ため、CI が 1 分余分に走る価値はあります。

### 2.4 PR 時の依存レビュー

`actions/dependency-review-action` は**プルリクエストのときだけ**走り、その PR が High / Critical のアドバイザリが付いた**新規依存**を導入していたらビルド失敗にします。マージ後に気づくのではなく、PR 時点で止めるのが狙い。

### 2.5 GitHub Actions も依存である

`dependabot.yml` では 2 つのエコシステムを設定しています: `pip` (Python グラフ) と `github-actions` (`.github/workflows/*.yml` で参照しているアクションのバージョン)。Actions はランタイムライブラリと同じく週次で bump されます。

Action のバージョンは現状、メジャータグ (`@v4`、`@v3`、`@v2`) で参照しています。より厳しくピン留めしたい場合は、各タグをコミット SHA に置き換えます (`@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4` の形式)。Dependabot は SHA ピン留めされたエントリも追従して更新します。

---

## 3. ネットワーク姿勢

CodeRouter は既定で `127.0.0.1` にバインドします (`coderouter serve --host`)。運用者が明示的にオプトインしない限り `0.0.0.0` には出ません。信頼境界は「**ループバックのみ**」と定義され、全ルートで Host ヘッダ検証 (DNS rebinding 対策) が働きます — loopback 系以外の Host を持つリクエストは 403 で拒否され、意図的な外部公開時のみ `CODEROUTER_ALLOWED_HOSTS` (カンマ区切り) で許可ホスト名を追加します。チャット入口 (`/v1/messages` / `/v1/chat/completions`) に認証は**ありません**。launcher API の状態変更エンドポイント (start / stop / delete) のみ、`CODEROUTER_LAUNCHER_TOKEN` を設定すると `X-CodeRouter-Token` ヘッダによるトークン認証をオプトインできます (未設定なら従来どおり無認証)。リクエストボディは既定 64 MB が上限で (超過は 413)、`CODEROUTER_MAX_BODY_BYTES` で変更できます。

**運用者のチェックリスト**:

- マルチユーザホストで `0.0.0.0` にバインドするのは、認証を強制するリバースプロキシを別途挟まない限り避ける。loopback 以外のホスト名でアクセスさせる場合は `CODEROUTER_ALLOWED_HOSTS` に当該ホスト名を追加する (無ければ Host 検証で 403)
- launcher が loopback の外から到達しうる構成では `CODEROUTER_LAUNCHER_TOKEN` を設定する
- ネットワーク越しに公開する必要がある (リモート開発など) 場合は、ポートを開くのではなく SSH や VPN 越しにトンネルする
- 上流プロバイダの URL は config ロード時にチェックされます。`base_url` のタイポは即座に失敗するので、間違ったエンドポイントに静かに到達することはありません

---

## 4. CI が強制すること・しないこと

| ゲート | CI で強制? | 根拠 |
|---|---|---|
| `pytest` (453 テスト) | はい | コアの回帰サーフェス |
| `ruff check` | はい | 実バグを低コストで検出 |
| 禁止 SDK の grep | はい | アーキテクチャ不変項 (§2.1) |
| `uv sync --frozen` | はい | Lockfile ドリフト = 失敗 (§2.2) |
| `gitleaks` | はい | シークレット漏洩検出 (§1) |
| `pip-audit` | はい | PyPA の CVE フィード (§2.3) |
| OSV-Scanner | はい | OSV の CVE フィード (§2.3) |
| `dependency-review-action` | PR 時のみ | PR 時点で新規の脆弱依存をブロック (§2.4) |
| `ruff format --check` | **いいえ** | 体裁のみ。ローカルで走らせる |
| `mypy --strict` | **いいえ** | 必要ならローカルで。機能上の基準は `pytest` |

開発中はスタイルと strict 型付けに価値があります。ただし CI 上ではセキュリティゲートと注意のリソースを取り合う関係になるため、**1 人プロジェクトでの明示的な判断**として、これらはローカル関心事に留めています。

### ローカルですべての CI ゲートを走らせる

```bash
uv sync --frozen --extra dev
uv run ruff check .
uv run pytest -v
uv export --frozen --no-emit-project --no-hashes --extra dev --format requirements-txt -o requirements-audit.txt
uv run --with pip-audit pip-audit --strict -r requirements-audit.txt
grep -RnE "^\s*(import|from)\s+(anthropic|openai|litellm|langchain)" coderouter/ && echo FAIL || echo OK
```

---

## 5. 脆弱性の報告

ユーザの鍵を侵害しうる、リクエスト内容を漏洩しうる、またはルーターから上流プロバイダアカウントに pivot できるような問題を見つけた場合は、**公開 Issue を立てない**でください。

1. GitHub Security Advisory を開く: `https://github.com/zephel01/CodeRouter/security/advisories/new`
2. 可能であれば再現手順を含める
3. 数日以内の acknowledgment を想定してください。これは個人プロジェクトであり、24×7 のサービスではありません

セキュリティに該当しないバグは通常の Issue トラッカへ。

---

## 6. 方針の更新履歴

- **v1.0 (2026-04)** — 初版の security.md。v1.0.0 アンブレラ後、CI を回帰 + サプライチェーンに再スコープ。`pip` と `github-actions` の両方で Dependabot を有効化。OSV-Scanner と dependency-review-action を追加。`mypy --strict` と `ruff format --check` を CI から外す
