mcpField
R
rls-doctor

rls-doctor

ローカル
github.com/subhajitluckyセキュリティ03 週間前に更新

rls-doctor について

rls-doctorは、PostgreSQLとSupabaseの行レベルセキュリティ(RLS)を監査するコマンドラインツールです。静的チェックとトランザクション内のプローブで実際の行アクセスを検証し、データベースの分離を確認するチーム向けに、修正案の生成と検証も行いますが、適用はしません。

主な機能

  1. 01ポリシー、権限、ロールアクセスの監査
  2. 02アプリケーションロールで行アクセスを検証
  3. 03SQLマイグレーションのオフライン分析
  4. 04一時 PostgreSQL で静的チェックを比較
  5. 05修正パッチの生成と検証
  6. 06レポートとカバレッジレシートの出力

活用シーン

  1. 01テナント間で行を読めるか確認
  2. 02DB認証情報なしでRLSマイグレーションをレビュー
  3. 03CIでセキュリティ検査を実行
  4. 04適用前にポリシー修正案を検証

MCP 対応

stdio経由で読み取り専用のMCPツールを提供し、クライアントから監査機能を利用できます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"subhajitlucky-rls-doctor": {
"command": "npx",
"args": [
"-y",
"rls-doctor"
]
}
}
}
起動コマンドnpx -y rls-doctor

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmrls-doctor0.5.8

ツール

6

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
formatenumいいえレポート形式。デフォルトは "text" です。text | json
schemasarrayいいえ監査するスキーマ名。デフォルトは ['public'] です。
appRolesarrayいいえ到達可能な ID として扱う追加のアプリケーションロール。例:app_user または web_user。
connectionStringstringいいえPostgres 接続文字列。指定がない場合は DATABASE_URL または SUPABASE_DB_URL を使用します。読み取り専用の認証情報を推奨します。この値をコミットまたはエコーしないでください。

詳細

RLS Doctor

CI npm npm downloads

RLS を信用せず、証明しましょう。

RLS Doctor demo

ユーザー 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 Doctor terminal preview


推測するのではなく、テナント間の情報漏えいを証明する

多くの 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 でシークレットを渡すと、シェルの履歴やプロセス一覧に表示されます。

コマンド

コマンド機能
check1 つ以上のスキーマを監査します
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

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。