rls-doctor について
rls-doctorは、PostgreSQLとSupabaseの行レベルセキュリティ(RLS)を監査するコマンドラインツールです。静的チェックとトランザクション内のプローブで実際の行アクセスを検証し、データベースの分離を確認するチーム向けに、修正案の生成と検証も行いますが、適用はしません。
主な機能
- 01ポリシー、権限、ロールアクセスの監査
- 02アプリケーションロールで行アクセスを検証
- 03SQLマイグレーションのオフライン分析
- 04一時 PostgreSQL で静的チェックを比較
- 05修正パッチの生成と検証
- 06レポートとカバレッジレシートの出力
活用シーン
- 01テナント間で行を読めるか確認
- 02DB認証情報なしでRLSマイグレーションをレビュー
- 03CIでセキュリティ検査を実行
- 04適用前にポリシー修正案を検証
MCP 対応
stdio経由で読み取り専用のMCPツールを提供し、クライアントから監査機能を利用できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
6掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| format | enum | いいえ | レポート形式。デフォルトは "text" です。text | json |
| schemas | array | いいえ | 監査するスキーマ名。デフォルトは ['public'] です。 |
| appRoles | array | いいえ | 到達可能な ID として扱う追加のアプリケーションロール。例:app_user または web_user。 |
| connectionString | string | いいえ | Postgres 接続文字列。指定がない場合は DATABASE_URL または SUPABASE_DB_URL を使用します。読み取り専用の認証情報を推奨します。この値をコミットまたはエコーしないでください。 |
詳細
RLS Doctor
RLS を信用せず、証明しましょう。

ユーザー A はユーザー B の行を読み取れるでしょうか?行レベルセキュリティは、Postgres の最も強力な分離ツールの 1 つであり、設定が不完全なまま放置されやすいものの 1 つでもあります。RLS を有効にしてポリシーを追加し、そのまま先に進むこともできます。しかし、ポリシーは OR で結合され、WITH CHECK は暗黙に USING にフォールバックし、テーブル所有者は RLS を完全にバイパスし、TRUNCATE はそもそも保護されません。
rls-doctor はカタログを読み取り、実際に何にアクセスできるのかを教えてくれます。また、probe を使うと分析をやめて、アプリのロールになりすまし、必ずロールバックされるトランザクション内で実際に見せてくれます。
npx rls-doctor check # reads DATABASE_URL or SUPABASE_DB_URL
npx rls-doctor demo # no database needed: disposable Postgres, proven leak
RLS Doctor Report
Generated: 2026-07-12T00:00:00.000Z
Schemas: public
Summary: 1 tables, 0 policies, highest risk HIGH
Findings: critical 0, high 1, medium 0, low 0, info 0
RLS Score: 90/100
public.orders
RLS disabled; force RLS disabled; 0 policies
[HIGH] RLS-disabled table is reachable by an application role
public.orders has no row-level checks; Application role authenticated and
can exercise SELECT granted to authenticated.
Fix: Enable RLS and add least-privilege policies for each application role.
Suggested SQL:
alter table "public"."orders" enable row level security;
推測するのではなく、テナント間の情報漏えいを証明する
多くの RLS レビューでは、ポリシーを読み、問題がないことを期待します。probe は動作を検証します。
rls-doctor probe --connection "$DATABASE_URL" --schema public \
--app-roles authenticated --subjects \
00000000-0000-0000-0000-000000000001 \
00000000-0000-0000-0000-000000000002
トランザクション内で request.jwt.claims を設定するため、auth.uid() は実際の各 Supabase ユーザーに解決され、その ID で誰の行を読み取れるかを数えます。
- [high] probe-cross-owner-read rls_doctor_demo.orders:
Role authenticated reads rows across 2 distinct owner_id values
- [high] probe-cross-subject-read rls_doctor_demo.orders:
Role authenticated reads rows belonging to other users
データを変更することはありません。 probe は begin ... read only を開始し、必ずロールバックするため、本番の読み取りレプリカに対しても安全です。
インストール
npx rls-doctor check # no install
npm install -g rls-doctor # global
Node.js 20 以降が必要です。読み取り専用の監査用認証情報で DATABASE_URL または SUPABASE_DB_URL を使うことをおすすめします。--connection でシークレットを渡すと、シェルの履歴やプロセス一覧に表示されます。
コマンド
| コマンド | 機能 |
|---|---|
check | 1 つ以上のスキーマを監査します |
explain <table> | 単一のテーブルの詳細を表示します |
probe | アプリのロールになりすまし、実際の行アクセスを証明します |
shadow | 破棄可能な Postgres でマイグレーションを再生し、静的解析と実環境を比較します |
prove | オフラインで分離に関する主張を証明します。証明、証拠、または未判定を提示します |
fix | 修正を生成し、書き込む前に shadow で検証します |
demo | 破棄可能な Postgres を起動し、テナント間の情報漏えいを証明します |
mcp | 読み取り専用の MCP ツールを stdio 経由で提供します |
rls-doctor check --schema public --fail-on high
rls-doctor explain public.profiles
rls-doctor probe --app-roles authenticated --owner-columns tenant_id
check のオプション
-c, --connection <url> Postgres connection string
-s, --schema <schema...> Schemas to audit. Default: public
--json Machine-readable JSON
--format <format> text, json, or sarif. Default: text
--fail-on <severity> info, low, medium, high, critical, none. Default: high
--baseline <path> Compare with a prior JSON report; only new findings fail
--update-baseline Write the current report to --baseline and exit 0
--schema-file <path> Audit a SQL schema or migration file offline; no database
--receipt <path> Write a portable coverage receipt (digest-verified)
--receipt-key <path> Sign the receipt with an Ed25519 private key (PEM)
--score Print only the RLS Score
--badge Print a shields.io badge URL for the RLS Score
--statement-timeout <ms> Catalog query timeout. Default: 10000
--app-roles <role...> Extra roles to treat as reachable application identities
Supabase ではない一般的な Postgres では、実際のロールを渡してください。そうすると、それらを通じた権限付与やメンバーシップが authenticated と同じ重大度で分析されます。
rls-doctor check --connection "$DATABASE_URL" --app-roles app_user web_user --fail-on high
probe のオプション
--app-roles <role...> Roles to impersonate (required)
--subjects <uuid...> Supabase user ids to simulate via request.jwt.claims
--owner-columns <col...> Owner/tenant columns to compare
(default: owner_id user_id tenant_id account_id)
--sample-limit <n> Rows sampled per table (default: 1000)
probe の検出結果:probe-cross-owner-read と probe-cross-subject-read(高)、probe-role-unavailable(中)、probe-error(低)、および情報レベルの probe-reads-rows、probe-subject-reads-own-rows、probe-reads-no-rows、probe-read-denied。
検出する内容
| チェック | 重大度 | 重要な理由 |
|---|---|---|
| 公開相当の無条件書き込み | 重大 | INSERT、UPDATE、DELETE、または ALL に対して無条件の述語を持つ public/anon ポリシーです |
到達可能な TRUNCATE | 高 | RLS は TRUNCATE を保護しません |
| RLS が無効で到達可能 | 高 | アプリ向けロールが行アクセス権とスキーマ USAGE を持つか、スーパーユーザーに SET ROLE でき、行チェックがありません |
| 公開相当の無条件読み取り | 高 | SELECT/ALL に対して無条件の USING を持つ public/anon ポリシーです |
SUPERUSER/BYPASSRLS へのアプリケーション経路 | 高 | これらの属性は FORCE が有効でも RLS を回避します |
| 広範なデフォルトテーブル権限 | 書き込みは高/読み取りは中 | その所有者が作成する将来のテーブルに、この権限が継承されます |
WITH CHECK の省略 | 中 | 書き込み制約は暗黙に USING にフォールバックします |
| ポリシーなしで RLS が有効 | 中 | 所有者以外のロールはデフォルトで拒否され、アプリからのアクセスが機能しなくなります |
| 複数の許可ポリシー | 中 | 同じロール/コマンドのポリシーは OR で結合されます |
| RLS が無効で、現時点では到達不能 | 中 | 後から権限が付与されるとテーブルが公開される可能性があります |
FORCE RLS が無効 | 情報 | テーブル所有者は RLS を回避します |
チェックは PostgreSQL のコマンドセマンティクスに従います。SELECT は USING を評価し、INSERT は WITH CHECK を評価し、DELETE は USING を評価し、UPDATE は両方を評価します。UPDATE または ALL ポリシーで WITH CHECK が省略されている場合、Postgres は USING 式にフォールバックします。rls-doctor はその実効的なチェックを分析します。ALL は該当する各コマンドとして評価されます。
直接、継承、および SET ROLE のメンバーシップ経路(PostgreSQL 16 のメンバーシップごとのオプションを含み、15 は正規化されます)をたどり、現在のアクセスと将来のテーブルに影響するデフォルト権限を別々に報告します。
エコシステム調査
公開されている Supabase プロジェクト 60 件のマイグレーションを静的に監査したところ、63% に少なくとも 1 つの高または重大な RLS パターンが含まれていました(スコアの中央値は 38/100)。 そのうち 57% では、カバレッジの制限が正直に報告されています。集計済み、匿名化済み、再現可能な調査です。レポート と開示ポリシーをお読みください。
GitHub Action
検出結果は Security タブに表示されます。
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v5
- uses: subhajitlucky/[email protected]
with:
connection: ${{ secrets.READONLY_DATABASE_URL }}
schema: public
format: sarif
upload: "true"
fail-on: high
app-roles: authenticated app_user
SARIF の結果には logicalLocations(schema.table)と安定した partialFingerprints が含まれるため、テーブルがファイルではなくても、コードスキャンは実行ごとに検出結果を追跡できます。
または Action を使わずに CI で実行する
name: RLS Audit
on:
pull_request:
branches: [main]
jobs:
rls-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22.x
- run: npx rls-doctor check --schema public --json --fail-on high
env:
DATABASE_URL: ${{ secrets.READONLY_DATABASE_URL }}
詳細ガイド:docs/guides/github-actions.md
ベースライン
既存のデータベースで、初日から失敗させずに導入できます。
rls-doctor check --schema public --baseline rls-baseline.json --update-baseline
rls-doctor check --schema public --baseline rls-baseline.json --fail-on high
フィンガープリントは検出結果の ID、スキーマ、テーブル、詳細から導出されます。そのため、述語や権限が変更されると、新しい検出結果として正しく再報告されます。JSON レポートには baseline: { new, unchanged, resolved } が含まれます。
RLS スコア
すべてのレポートには決定論的なスコアが含まれます。100 から重大度ごとのペナルティ(重大 25、高 10、中 4、低 1、情報 0)を差し引き、0~100 の範囲に収めます。評価区分は、緑 ≥ 80、黄 ≥ 50、赤 < 50 です。このスコアがレポートに取って代わることはありません。データベースが安全だと判断する前に、検出結果を確認してください。
rls-doctor check --score # RLS Score: 34/100
rls-doctor check --badge # https://img.shields.io/badge/RLS%20Score-34%2F100-red
--score と --badge はスコアまたはバッジのみを出力します。終了コードは引き続き --fail-on に従います。JSON レポートには常に findingPenalty の内訳を含む score オブジェクトが含まれます。
データベースなしでマイグレーションを監査する
check --schema-file は、SQL ファイルからテーブル、ポリシー、RLS、権限付与、ロール、デフォルト権限の状態を再構築します。接続も認証情報も不要です。サポートされていないステートメントは、推測による検出結果ではなく、カバレッジの制限として扱われ、RLS スコアにはオフラインでのカバレッジペナルティが反映されます。
rls-doctor check --schema-file supabase/migrations/0001_init.sql --fail-on high
サポート対象:CREATE SCHEMA/TABLE/ROLE/USER/POLICY、RLS と
FORCE ROW LEVEL SECURITY に関する ALTER TABLE、テーブル、スキーマ、ロールに対する
GRANT/REVOKE、ALTER DEFAULT PRIVILEGES、同じ対象の DROP、および
DO ブロック内の無条件のロール作成。
シャドウ検証
shadow は使い捨ての PostgreSQL コンテナでマイグレーションファイルを再生し、
実際のカタログ監査を実行します。シードデータが提供されている場合は probe も実行し、
オフライン分析とデータベースの実際の状態を比較します。
rls-doctor shadow --schema-file supabase/migrations/*.sql --seed seed.sql --fail-on high
- 静的分析とライブ分析で一致した検出事項の数を出力し、どちらか一方にしかない不一致も一覧表示します。これはパーサーの対応範囲に関する事実であり、推測された検出事項ではありません。
- コンテナは必ず削除されます。監査対象のデータベースには一切触れません。
--receiptはscope=shadowおよびenvironment: shadow (disposable world)と記されたレシートを書き出します。
形式的証明モード
prove は、小規模で健全なフラグメントに基づき、スキーマファイルから行分離に関する主張を判定します。データベースは不要です。
rls-doctor prove --schema-file supabase/migrations/0001_init.sql --table orders
テーブル、コマンド、ロールごとに、次のいずれかを返します。
- proved — ポリシーが許可するすべての行が
owner_col = auth.uid()を満たす(またはポリシーが行を一切許可しない) - witness — ポリシーが所有権の境界外の行を許可する可能性がある。理由も示されます(
using (true)、RLS が無効、不一致の列) - undecided — 決定可能なフラグメントの範囲外(NOT、サブクエリ、キャスト、関数呼び出し)。理由とともに一覧表示され、推測されることはありません。
witness が 1 つでもある場合、終了コードは 1 です。--proof <path> は、主張の SHA-256 ダイジェストを含む正規化された JSON 成果物を書き出します。
docs/proofs.md を参照してください。
証明付き修復
fix は、公開状態にあるテーブルの正規修復を生成し、何かを書き込む前にシャドウ環境で検証します。
rls-doctor fix --schema-file supabase/migrations/0001_init.sql \
--table public.orders --patch fix-orders.sql --receipt fix-orders.json
パッチは、検証によって対象の witness が解消され、新たな medium+ の検出事項が発生しないと証明された場合にのみ書き込まれます。これは常に静的に検証され、Docker が利用可能な場合は、使い捨ての PostgreSQL に対しても検証されます。レシートには、検出事項のフィンガープリントと、正確なパッチハッシュおよび修復前後の判定結果が紐付けられます。このツールがパッチを適用することはありません。人間または権限を与えられたエージェントが適用し、その後 check と prove を再実行します。docs/repairs.md を参照してください。
終了時の動作
0— 設定されたしきい値に達する検出事項がありません1— 検出事項が--fail-on以上の重大度、または Commander の使用方法/オプション解析エラー2— 捕捉された実行時エラー:認証情報の不足、接続/カタログエラー、無効なアクション値、指定されたテーブルが見つからない
--fail-on none は、検出事項に基づく失敗を常に無効にします。問題のないレポートでは highestSeverity: "none" となるため、検出事項がない場合、--fail-on info でも終了コードは 0 です。
カバレッジレシート
各監査では、持ち運び可能なカバレッジレシートを出力できます。何をチェックしたか、何をチェックしなかったか、決定論的なスコア、検出事項のフィンガープリントが含まれ、正規化された本文の SHA-256 ダイジェストと任意の Ed25519 署名が付与されます。
rls-doctor check --receipt receipt.json
rls-doctor check --schema-file m.sql --receipt receipt.json --receipt-key key.pem
rls-doctor verify-receipt receipt.json # exit 0 valid, 2 tampered
レシートにポリシー式、接続文字列、認証情報が含まれることはありません。docs/receipts.md を参照してください。
MCP サーバー
claude mcp add rls-doctor --scope user -- npx -y [email protected] mcp
各ツールには readOnlyHint: true、destructiveHint: false、idempotentHint: true が付与されており、読み取り専用です。ツールは describe_capabilities(ツールの契約、承認ルール、カバレッジの制限)、check_rls、probe_access、explain_table、prove_isolation(SQL ファイルからのオフライン証明)、propose_repair(静的検証済みの修復案。ファイルへの書き込みは行いません)です。エラーは { error: { code, message, nextAction } } の形式の構造化 JSON を返します。Codex CLI、Gemini CLI、汎用 stdio クライアントのセットアップについては、docs/agents.md を参照してください。
終了コード
0:コマンドが完了し、--fail-onのしきい値に達する検出事項がありません。1:コマンドが完了し、検出事項がしきい値に達しました。2:入力が無効、または運用上の障害(接続、解析、IO)が発生しました。 終了コード2を問題なしとして扱わないでください。
該当する場合、各コマンドのドキュメントに追加のコードが記載されています(たとえば、verify-receipt は改ざんされたレシートに対して 2 で終了します)。
エージェントスキル
npx skills add subhajitlucky/rls-doctor
互換性のあるコーディングエージェントに、監査を実行するタイミング、読み取り専用のデータベース認証情報の使用方法、接続文字列の漏えいを避ける方法を教えます。
Supabase
rls-doctor は、カタログから参照できる PostgreSQL の RLS、権限付与、ロールパスを監査します。ホスト型 Supabase の管理設定、ビュー、関数は監査しないため、Data API の設定は別途確認してください。安全でないポリシーと、より安全なポリシーの例については、docs/guides/supabase-rls-patterns.md を参照してください。
すぐに試す
npx rls-doctor demo を実行すると、使い捨ての PostgreSQL コンテナーを起動し、意図的に安全でないスキーマを適用して、check と probe を実行します。テナントをまたいだ読み取りを実証した後、コンテナーを削除します。設定も認証情報も不要で、何も書き込まれません。Docker を利用できない場合は、記録済みの実行結果に切り替わります。
demo/ には使い捨てのフィクスチャーもあります。unsafe-schema.sql(意図的にリスクのあるポリシー)と safe-schema.sql(より安全な参考例)です。
npm run demo # prints local demo steps
デモ用フィクスチャーを本番環境に対して実行しないでください。
アーキテクチャ
CLI command
-> catalog loader
-> audit analyzer
-> text / JSON reporters
-> shell exit code
詳しい説明については、docs/architecture.md を参照してください。
安全性と対象範囲
rls-doctor がクエリするのは Postgres のカタログのみです。自身が出力するエラーから接続認証情報をサニタイズします。プローブのトランザクションは読み取り専用で、必ずロールバックされます。
これは対象を絞ったカタログ監査であり、証明やコンプライアンスのための製品ではありません。次のことは行いません。
- 任意の SQL 述語が正しいことを証明したり、
canスタイルのチェックをシミュレートしたりする - ビュー、関数、またはホスト型 Supabase の管理設定を監査する
- 提案された SQL を自動的に実行する — 提案はレビュー用のテンプレートです
- コンプライアンスに関する主張を行う
これはセキュリティレビューを支援するツールであり、アプリケーションレベルの認可テスト、権限付与のレビュー、または本番環境の包括的なセキュリティ監査に代わるものではありません。
ドッグフーディング
CI ではオフラインで、RLS Doctor 自身を使って安全な参考スキーマをチェックします。
rls-doctor check --schema-file demo/safe-schema.sql --fail-on high
安全なフィクスチャーは、高い重大度で問題がない状態を保つ必要があります。安全でないフィクスチャーは失敗することが想定されています。これは demo とテストスイートで使用されます。
開発
npm ci && npm run ci
npm run test:integration は独自の使い捨て Postgres コンテナーを起動し、破壊的なフィクスチャーの使用を許可して、check/explain を実行した後、コンテナーを削除します。scripts/run-integration.js を直接実行する場合、RLS_DOCTOR_ALLOW_DESTRUCTIVE_TESTS=1 が設定されていないと実行を拒否します。このガードは使い捨てデータベースでのみ使用してください。
ロードマップ
- Markdown レポート出力
- ブランチ間のポリシー差分
- SQL マイグレーション提案(オプション)
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。