关于 rls-doctor
rls-doctor 是用于审计 PostgreSQL 和 Supabase 行级安全(RLS)的命令行工具,结合静态检查与事务探测来验证实际行访问权限。它面向审查数据库隔离的团队,也能生成并验证修复方案,但不会应用修复。
主要功能
- 01审计策略、授权与角色访问
- 02以应用角色探测行访问
- 03离线分析 SQL 迁移
- 04与临时 PostgreSQL 比较静态检查
- 05生成并验证修复补丁
- 06导出报告与覆盖收据
适用场景
- 01检查租户能否读取彼此的行
- 02无需数据库凭据审查 RLS 迁移
- 03在 CI 中运行安全检查
- 04应用前验证策略修复方案
MCP 相关
它通过 stdio 提供只读 MCP 工具,供客户端调用审计能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| format | enum | 否 | 报告呈现格式。默认为 "text"。text | json |
| schemas | array | 否 | 要审计的架构名称。默认为 ['public']。 |
| appRoles | array | 否 | 要作为可访问身份处理的其他应用角色,例如 app_user 或 web_user。 |
| connectionString | string | 否 | Postgres 连接字符串。若未提供,则使用 DATABASE_URL 或 SUPABASE_DB_URL。建议使用只读凭据。绝不要提交或回显此值。 |
详细介绍
RLS Doctor
别相信你的 RLS,用证据证明它。

用户 A 能读取用户 B 的行吗?行级安全是 Postgres 最强大的隔离工具之一,也最容易配置不完整。你启用 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 用户,然后统计该身份可以读取哪些行。
- [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 传递密钥会使其暴露在 Shell 历史记录和进程列表中。
命令
| 命令 | 功能 |
|---|---|
check | 审计一个或多个架构 |
explain <table> | 查看单个表的完整详情 |
probe | 模拟应用角色并验证实际行访问权限 |
shadow | 在一次性 Postgres 中重放迁移,并比较静态分析与实时结果 |
prove | 离线验证隔离声明:输出证明、见证或未决结果 |
fix | 生成修复方案,并在写入前通过 shadow 验证 |
demo | 启动一次性 Postgres,并验证跨租户数据泄露 |
mcp | 通过 stdio 提供只读 MCP 工具 |
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
在通用 Postgres(非 Supabase)上,请传入实际角色,以便按照与 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-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。
检查内容
| 检查项 | 严重性 | 重要原因 |
|---|---|---|
| 类似公开角色的无条件写入 | 严重 | public/anon 策略对 INSERT、UPDATE、DELETE 或 ALL 使用无条件谓词 |
可触达的 TRUNCATE | 高 | RLS 永远不会保护 TRUNCATE |
| RLS 已禁用且可触达 | 高 | 面向应用的角色具有行访问权限和架构 USAGE,或者可以 SET ROLE 切换到超级用户,且没有行级检查 |
| 类似公开角色的无条件读取 | 高 | public/anon 策略对 SELECT/ALL 使用无条件 USING |
应用可触达 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 会进行规范化),并分别报告当前访问权限,以及影响未来表的默认权限。
生态系统研究
我们对 60 个公开 Supabase 项目的迁移文件进行了静态审计:63% 至少包含一种高或严重级别的 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,因此即使表不是文件,代码扫描也能跨运行追踪调查结果。
或者在 CI 中不使用 Action 运行
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 报告始终包含 score 对象及其
findingPenalty 明细。
无需数据库即可审计迁移文件
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、ALTER TABLE 中的 RLS 和
FORCE ROW LEVEL SECURITY、针对表、架构和角色的
GRANT/REVOKE、ALTER DEFAULT PRIVILEGES、对上述对象的
DROP,以及 DO 块中的无条件角色创建。
Shadow 验证
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。--proof <path> 会写入一个规范 JSON 工件,其中包含对声明进行 SHA-256 摘要校验的结果。参见 docs/proofs.md。
携带证明的修复
fix 会为暴露的表生成规范修复,并在写入任何内容前于 shadow 环境中验证:
rls-doctor fix --schema-file supabase/migrations/0001_init.sql \
--table public.orders --patch fix-orders.sql --receipt fix-orders.json
只有在验证证明修复解决了目标 witness,且未引入新的中危及以上发现时,才会写入补丁——始终进行静态验证;Docker 可用时,还会针对实时的一次性 PostgreSQL 进行验证。回执会将发现指纹绑定到确切的补丁哈希以及修复前后的判定结果。此工具绝不会应用补丁;补丁由人工或经授权的 Agent 应用,然后重新运行 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(经过静态验证的修复方案;绝不写入文件)。失败时会返回结构化 JSON,格式为 { error: { code, message, nextAction } }。Codex CLI、Gemini CLI 和通用 stdio 客户端的设置说明见 docs/agents.md。
退出码
0:命令已完成,且没有发现达到--fail-on阈值。1:命令已完成,且有发现达到该阈值。2:输入无效或发生操作故障(连接、解析、IO)。 绝不要将退出码2视为干净结果。
适用时,各命令会说明额外的退出码(例如,回执遭篡改时,verify-receipt 以 2 退出)。
Agent 技能
npx skills add subhajitlucky/rls-doctor
帮助兼容的编码 Agent 了解何时运行审计、如何使用只读数据库凭据,以及如何避免泄露连接字符串。
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 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。