mcpField
R
rls-doctor

rls-doctor

本地服务
github.com/subhajitlucky安全03 周前更新

关于 rls-doctor

rls-doctor 是用于审计 PostgreSQL 和 Supabase 行级安全(RLS)的命令行工具,结合静态检查与事务探测来验证实际行访问权限。它面向审查数据库隔离的团队,也能生成并验证修复方案,但不会应用修复。

主要功能

  1. 01审计策略、授权与角色访问
  2. 02以应用角色探测行访问
  3. 03离线分析 SQL 迁移
  4. 04与临时 PostgreSQL 比较静态检查
  5. 05生成并验证修复补丁
  6. 06导出报告与覆盖收据

适用场景

  1. 01检查租户能否读取彼此的行
  2. 02无需数据库凭据审查 RLS 迁移
  3. 03在 CI 中运行安全检查
  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否要作为可访问身份处理的其他应用角色,例如 app_user 或 web_user。
connectionStringstring否Postgres 连接字符串。若未提供,则使用 DATABASE_URL 或 SUPABASE_DB_URL。建议使用只读凭据。绝不要提交或回显此值。

详细介绍

RLS Doctor

CI npm npm downloads

别相信你的 RLS,用证据证明它。

RLS Doctor 演示

用户 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 Doctor 终端预览


证明跨租户数据泄露,而不是靠猜

大多数 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

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。