repoward について
repowardは、読み取り履歴に基づく書き込み制限と人による承認で、信頼できない内容をきっかけとする非公開データの漏えいリスクを抑えるGitHub MCPサーバーです。GitHubへの経路をこのサーバーに限定したCIボットやホスト型エージェント向けで、現在はアルファ版であり、独立したセキュリティレビューは受けていません。
主な機能
- 01デフォルト拒否のアクセス方針を適用
- 02非公開リポジトリの読み取り後に公開先への書き込みを禁止
- 03信頼できない内容に関わる操作を人の承認必須に
- 04ハッシュチェーン付き監査記録を保持
- 05主体とツールごとにレート制限
- 06方針変更の再生と短期GitHub Appトークン
活用シーン
- 01CIボットのGitHub操作を制限
- 02非公開情報の読み取り後の公開先への書き込みを防止
- 03マージやブランチ削除の前に承認を要求
- 04方針変更が過去の判断に与える影響を確認
MCP 対応
repowardはstdioで動作するGitHub MCPサーバーです。Issue、コード、ファイル、CI状態の読み取りに加え、方針で制御されたブランチ、コミット、プルリクエスト、コメント操作のツールを公開します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
repoward
GitHub MCP サーバーです。AI エージェントが、攻撃者の Issue に指示されたとおりに動くのを防ぎます。
ステータス:アルファ版。 独立したセキュリティレビューは受けていません。 脅威モデル、特に実行場所に関する説明をお読みください。
問題
GitHub へのアクセス権を持つエージェントは、悪意のある公開 Issue を読み、非公開リポジトリを読み、その内容を公開プルリクエストに投稿できます。個々の呼び出しはそれぞれ許可されているように見えるため、ツールごとの権限設定では一連の操作を阻止できません。これは、GitHub MCP サーバーを使用するエージェントに対して Invariant Labs が公開した攻撃であり、repoward のテストスイートでは、実際のリポジトリを使ってこの攻撃を再現しています。
フローガード
書き込みのたびに、repoward は GitHub の情報を使って、このエージェントがすでに何を読んだかを確認します。
| エージェントが読んだもの | その場合 |
|---|---|
| 非公開リポジトリの内容 | 公開リポジトリへの書き込みは拒否されます |
OWNER、MEMBER、COLLABORATOR 以外の作成者による Issue、プルリクエスト、コメント、またはジョブログ | 書き込みのたびに人間の承認が必要です |
これは、モデルが攻撃に気づくことにも、テキストのスキャンにも依存しないため、データの言い換えやエンコードでは回避できません。状態は再起動後も保持され、どのツールからも消去できません。 人間は次の操作を行えます。
uvx repoward flow status
uvx repoward flow reset
このツールが行わないこと:
- このサーバーのツールのみを対象とします。シェル、ブラウザー、または別の MCP サーバーを使えば、依然として抜け道になります。そのため、GitHub へのアクセスと外部への通信経路がこのサーバーに限定されているエージェントを保護します。
- ウィンドウ(デフォルトでは 60 分)が終了した後も、データがエージェントのコンテキストに残ることがあります。長時間稼働するエージェントを再起動して、コンテキストをクリアしてください。
single_repo: trueを設定しない限り、ある非公開リポジトリを読み、別の非公開リポジトリに書き込むことは許可されます。- リポジトリのファイルとコード検索結果は、書き込み権限を持つ人がマージしたものなので、信頼できない入力とは見なされません。
author_associationは大まかなシグナルであり、信頼されたアカウントが侵害された場合も信頼されたものとして扱われます。- 公開リポジトリのメンテナーには、より多くの承認リクエストが届きます。これはデフォルト設定に伴うコストであり、
trusted_associationsで変更できます。
機能
- ポリシー: ロール、リポジトリの許可リスト、パスの拒否リストを含む、デフォルト拒否の YAML です。詳しくは policy.example.yaml をご覧ください。
- 単一の保護された経路: 不明なツールや無効な引数を含む、すべてのツール呼び出しが単一のパイプラインを通り、ログに記録されます。
- 監査ログ: すべての判断をハッシュチェーンで記録し、オプションで HMAC とチェックポイントを利用できます。
- レート制限: プリンシパルごと、およびツールごとに設定します。
- 読み取りツール:
github_list_issues、github_get_issue、github_search_code、github_get_file、ci_get_status、ci_get_job_logs。 - 書き込みツール:
github_create_branch、github_commit_files、github_create_pull_request、github_comment。書き込み先は、設定されたプレフィックス(デフォルトはagent/)を持つブランチに限られます。プルリクエストはデフォルトでドラフトのみです。コミットサイズには上限があり、強制実行や再試行は行われません。認識可能な認証情報を含むテキストは拒否されます。 - 危険なツール:
github_merge_pull_requestとgithub_delete_branchには人間の承認が必要です。承認は特定のコミットに対する特定の呼び出しだけを対象とし、一度だけ有効で、期限が切れます。ポリシーで有効にするまでは、マージは無効です。 - GitHub App トークン(オプション): 呼び出しごとに、単一のリポジトリと、そのツールに必要な権限のみに制限された短期間有効なトークンを取得します。GitHub 自身がその制限を適用します。
- プルリクエストレビューによる承認(オプション): 特定のコミットに対する実際の GitHub レビューによって、マージを承認できます。
- プルリクエストの受領記録(オプション): エージェントの実行状況を要約するコミットステータスです。
- ポリシーのリプレイ: ポリシーの編集をデプロイする前に、その編集によって過去の判断がどう変わるかを確認できます。
- 信頼できないコンテンツの処理: GitHub ユーザーのテキストは、不可視文字、隠し HTML コメント、画像を削除し、シークレットをマスキングしたうえで、応答ごとにランダムな ID を持つタグで囲んで返されます。これはリスクを軽減するための対策であり、保証ではありません。モデルがだまされる可能性は残るため、上記のルールはモデルの外部で適用されます。
対象ユーザー
CI ボットやホスト型エージェントなど、GitHub への唯一のアクセス経路がこのサーバーであるエージェントを対象としています。シェルアクセス権を持つエージェントは、git を実行したり、ローカルの認証情報を直接読み取ったりできます。そのため、このサーバーはそのようなエージェントに対するセキュリティ境界ではありません。
インストール
uv が必要です。スターターポリシーを作成します。クライアント設定も出力されるので、貼り付けてください。
uvx repoward init --repo your-org/your-repo
スターターポリシーでは、サーバーに読み取り専用のツールが付与されます。書き込みツールを使うには --role contributor を追加してください。ファイルは ~/.repoward、または REPOWARD_HOME が指す場所に保存されます。既存のポリシーが上書きされることはありません。
ポリシーがないとサーバーは起動しないため、まずこれを実行してください。その後、MCP クライアントに追加し、GitHub トークンの説明に従って設定したトークンを使用してください。 実際のトークンを含む設定をコミットしないようにしてください。
Claude Code
claude mcp add --env GITHUB_TOKEN=<your-token> --transport stdio repoward -- uvx repoward serve
Claude Desktop
Settings > Developer > Edit Config の順に選択し、claude_desktop_config.json に記述します。
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "<your-token>" }
}
}
}
Cursor
環境変数からトークンを読み込むよう、~/.cursor/mcp.json に記述します。
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "${env:GITHUB_TOKEN}" }
}
}
}
VS Code
トークンの入力を一度求めるよう、.vscode/mcp.json またはユーザープロファイルの mcp.json に記述します。
{
"inputs": [
{ "type": "promptString", "id": "github-token", "description": "GitHub token", "password": true }
],
"servers": {
"repoward": {
"type": "stdio",
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "${input:github-token}" }
}
}
}
Codex
環境変数からトークンを渡すよう、~/.codex/config.toml に記述します。
[mcp_servers.repoward]
command = "uvx"
args = ["repoward", "serve"]
env_vars = ["GITHUB_TOKEN"]
Gemini CLI
環境変数からトークンを読み込むよう、~/.gemini/settings.json に記述します。
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
}
}
}
OpenCode
環境変数からトークンを読み込むよう、プロジェクト内の opencode.json または ~/.config/opencode/opencode.json に記述します。
{
"mcp": {
"repoward": {
"type": "local",
"command": ["uvx", "repoward", "serve"],
"environment": { "GITHUB_TOKEN": "{env:GITHUB_TOKEN}" }
}
}
}
stdio MCP サーバーを起動できるその他のクライアントでは、同じコマンド uvx repoward serve を実行します。
クライアントにサーバーへの接続失敗が報告された場合は、ターミナルで uvx repoward serve を実行して原因を確認してください。よくある原因は、repoward init がまだ実行されておらず、ポリシーが存在しないことです。
便利なコマンド:
uvx repoward validate-policy
uvx repoward audit verify
リスクのある操作を承認する
承認が必要な呼び出しは実行されません。代わりに approval_id が返され、ユーザーがターミナルから判断します。
uvx repoward approvals list
uvx repoward approvals show <id>
uvx repoward approvals approve <id>
その後、エージェントは同じ呼び出しを繰り返します。新しいコミットをプッシュすると、呼び出しが指定するコミットが変わるため、以前の承認は適用されなくなります。また、GitHub はヘッドが移動したマージを拒否します。
プルリクエストレビューによる承認
通常の GitHub レビューによってマージを承認することもできます。そのため、承認者はこのマシンへのアクセス権だけでなく、自身の GitHub ID も必要です。ポリシーで有効にしてください。
approvals:
github_review: { enabled: true, min_permission: write }
レビューが有効とみなされるのは、マージ対象の正確なコミットを承認しており、プルリクエストの作成者でもこのサーバー自身の ID でもないユーザーによるもので、そのユーザーが必要なアクセス権を持つ場合に限られます。レビュー担当者の未解決の変更要求があれば、マージはブロックされます。各チェックはマージ時に再度行われます。
この機能を使うには、サーバーが GitHub App または別のボットアカウントとして動作する必要があります。自分のトークンを使う場合、自分自身のレビューは拒否されます。ログイン済みの gh が同じマシンにある場合など、エージェントがレビュー担当者の GitHub 認証情報にアクセスできると、この保護は失われます。
承認の限界
エージェントがこのサーバーと同じ OS ユーザーとして実行され、シェルを使える場合、これらのコマンドを実行したり、データベースを直接編集したりできます。承認を意味のあるものにするには、サーバーとそのファイルを別のユーザーまたはコンテナーで実行してください。
GitHub トークン
アクセスを許可するリポジトリに限定し、有効期限を短くしたファイングレイン個人アクセストークンを使用してください。リポジトリの権限を表示するには、Only select repositoriesを選択してください。読み取りツールには、以下の読み取り専用権限が必要です。
| 権限 | 用途 |
|---|---|
| Metadata | GitHub によって必須 |
| Contents | github_get_file |
| Issues | github_list_issues、github_get_issue |
| Pull requests | 同じツールでのプルリクエスト |
| Actions | ci_get_status、ci_get_job_logs |
| Commit statuses | ci_get_status |
書き込みツールを使用するには、Contents、Issues、Pull requests にも読み取りと書き込みのアクセス権を付与してください。Workflows 権限はオフのままにしてください。そうすると、ポリシーでパスが許可されていても、GitHub はワークフローファイルの変更を拒否します。
Contents: writeがあれば、トークンは任意のブランチにプッシュしてマージできます。GitHub はこれをagent/ブランチに限定できません。この制限はこのサーバー側で適用するため、プルリクエストを必須とし、トークンの所有者にバイパスを許可しないルールセットでデフォルトブランチも保護してください。トークンが漏洩したり、サーバーの設定に誤りがあったりしても、ルールセットによる保護は維持されます。
把握しておくべき制限:
ci_get_statusでは、Checks API 経由でのみ報告されるサードパーティのチェックは表示されません。GitHub は、これらをファイングレイン トークンでは利用できないとしています。github_search_codeの検索対象はデフォルトブランチと384 KB未満のファイルです。新しいリポジトリは、GitHub にインデックスされるまで何も返さないことがあり、実際の結果の処理については、これまでシミュレートされた応答でしかテストされていません。github_get_fileはシンボリックリンク、サブモジュール、バイナリファイル、1 MBを超えるファイルを拒否します。Git ツリーをたどるため、読み取りごとにパス内のフォルダーごとに1回リクエストします。ci_get_job_logsは、GitHub がログの保存先を認識できないホストに移動した場合、安全側に倒して失敗します。github_merge_pull_requestはマージキューとスタックされたプルリクエストに対応していません。github_delete_branchは、GitHub に条件付き削除の機能がないため、コミットを確認してから削除します。そのため、この2つの処理の間にわずかな時間差が残ります。
代わりに GitHub App を使用する
GitHub Appを使うと、各ツール呼び出しに専用の短命トークンが発行され、1つのリポジトリとそのツールに必要な権限に限定されます。また、App は独自の ID を持つため、レビューの承認に必要です。
Webhook をオフにし、必要な権限だけを設定して App を登録します。次に、ポリシーに含まれるリポジトリにインストールし、秘密鍵を生成します。
| 権限 | レベル | 用途 |
|---|---|---|
| Contents | 読み取り、または書き込みツール用の読み書き | ファイル、検索、ブランチ、コミット、マージ |
| Issues | 読み取り、またはコメント用の読み書き | Issue とコメント |
| Pull requests | 読み取り、または書き込みツール用の読み書き | プルリクエスト、レビュー、コメント |
| Actions | 読み取り | CI のステータスとジョブログ |
| Commit statuses | 読み取り、またはレシート用の読み書き | CI のステータスとレシート |
次の設定をポリシーに追加し、サーバーの環境でGITHUB_APP_PRIVATE_KEY_FILEが鍵を指すようにします。
github_auth:
mode: app
client_id: Iv1.your-client-id
installation_id: 12345678
秘密鍵は、新しいトークンを発行できる長期有効のシークレットです。エージェントから遠ざけてください。contents: writeトークンは、そのリポジトリ内の任意のブランチにプッシュできるため、上記のルールセットに関する推奨事項も引き続き適用してください。
監査ログが証明すること
このログは改ざんを検知できますが、改ざんを防止するものではありません。
| 攻撃 | 通常のチェーン | 攻撃者が読み取れない HMAC キーを使用 | ホスト外のチェックポイントを使用 |
|---|---|---|---|
| 1 件のレコードを編集する | 検知される | 検知される | 検知される |
| 1 件のレコードを編集し、それ以降のハッシュをすべて再計算する | 検知されない | 検知される | 最後のチェックポイントまで検知される |
| 最新のレコードを削除する | 検知されない | 検知されない | 最後のチェックポイントまで検知される |
HMAC を使用するには REPOWARD_AUDIT_HMAC_KEY を設定し、repoward audit checkpoint
の出力をサーバーが書き込めない場所に保存してください。コメント本文、タイトル、コミットメッセージ、ファイルの内容は
テキストではなく、長さとハッシュとして記録されます。書き込み中にクラッシュすると最後の行が不完全な状態になり、新しい
書き込みは、オペレーターがログを修復するまで拒否されます。シークレットの秘匿化はベストエフォートです。
プルリクエストのレシート
エージェントのプルリクエストをレビューする人は、実行がどのように進んだかを確認できます。ポリシーでこれを有効にしてください。
receipts:
enabled: true
エージェントがコミットまたはプルリクエストを作成するたびに、repoward は
repoward/receipt という名前のコミットステータスを投稿します。
policy 4c16a8364c1a; audit seq 12-40; 2 denied; 1 escalated; flow labels: yes
このステータスには、ポリシーのハッシュ、このサーバーの起動後の監査レコード、拒否された呼び出しまたは承認が必要だった呼び出しの数、エージェントの書き込みを制限するものがあるかどうかが含まれます。フローガードがオフの場合、flow labels は no、unknown、または off を示すこともあります。
Commit statuses: writeが必要です。GitHub App は、レシートごとにこの権限のみを持つ別のトークンを使用します。個人用トークンではトークン自体にこの権限が必要となるため、すべてのツール呼び出しでその権限が使われます。- レシートは投稿前に監査されます。投稿に失敗しても、エージェントのコミットまたはプルリクエストは成功し、失敗はログに記録されます。
- レシートは、変更が repoward を通じて行われたことを証明するものではありません。 この権限を持つ人なら誰でも同じステータスを投稿できるため、必須チェックにしないでください。
- リポジトリを見られる人なら誰でもレシートを読めます。 パブリックリポジトリでは、拒否された呼び出しの数やフローレーベルが設定されているかどうかも含まれるため、エージェントがプライベートな内容を読んだことを推測できる場合があります。
ポリシー変更を試す
監査ログにポリシーを適用して再生し、動作がどう変わるかを確認します。
repoward policy replay --policy new-policy.yaml
3 decisions: 1 unchanged, 1 changed, 1 unknown
Changed:
seq 2 github_comment org/app: allow -> approval_required (flow.untrusted_author)
Unknown (the log does not hold what this decision depends on):
seq 5 github_commit_files org/app: allow -> unknown (it depends on file sizes or text the log does not hold)
GitHub への接続や変更は一切行わず、検証に失敗したログは拒否します。ログが HMAC キーを使用している場合は、--key-env を追加して HMAC キーを格納した変数名を指定してください。
ログには内容が含まれないため、判断できないこともあります。判断は、ファイルの正確なサイズ、非表示のテキストに認証情報が含まれていたかどうか、またログが読み取っていないリポジトリの公開設定など、記録されていないフローガードの事実に左右される場合があります。再生ではこれらを unknown として報告し、推測は行いません。
再生では、エージェントが同じ呼び出しを行うことを前提とします。実際のエージェントは呼び出しを拒否された場合、別の動作をしていたはずです。そのため、レポートはポリシーの影響を把握するための手引きとして扱い、予測として扱わないでください。
貢献とセキュリティ
CONTRIBUTING.md を参照してください。脆弱性は SECURITY.md の説明に従って報告してください。
ライセンス
MIT。これは独立したプロジェクトであり、GitHub と提携関係になく、GitHub の推奨も受けていません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。