db-multitenancy(模块 M9)用于审计多租户系统的数据隔离与租户维度性能,同时覆盖隔离设计(安全)与索引/分区局部性(性能)两条轴线。系统为单租户时标记为 not_applicable(严重度 0,不计入评分),不得虚构租户要求。
检查内容:租户模型是否契合隔离要求(共享表 tenant_id 列 vs 每租户独立 schema vs 每租户独立数据库,例如严格隔离或受监管负载却只用共享行且无 RLS 即不匹配);租户所属表是否缺少 tenant_id/org_id/account_id 列,或该列可为空导致无归属行;隔离是否仅靠应用代码而无 RLS 策略或约束;索引与 PK/聚簇键是否未以 tenant_id 打头,导致无法按租户剪枝、跨租户全表扫描;UNIQUE/FK 是否未包含租户列,造成跨租户冲突或连接。
严重度:依赖的共享租户表 RLS 关闭/无强制隔离为 severity 5,fail,axis both,confidence established;租户表缺 tenant_id 为 severity 4,warn/fail,axis both;索引/PK 未以 tenant_id 打头为 severity 3,warn,axis performance;UNIQUE/FK 遗漏租户列为 severity 3–4,warn,axis both。
Tier-0 静态检查:通过 scripts/parse-schema.mjs 解析 DDL/快照,识别租户所属表,检查非空租户列、PK/UNIQUE/索引是否以其打头,并检测已声明的 RLS 策略;程序源解析结果仅为方向性,不能触发 sev-5 上限。
Tier-1 验证查询(只读,需 $DATABASE_URL): SELECT relname, relrowsecurity, relforcerowsecurity FROM pg_class WHERE relkind = 'r' AND relnamespace = 'public'::regnamespace; SELECT i.indexrelid::regclass AS index, a.attname AS first_col FROM pg_index i JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = i.indkey[0] WHERE i.indrelid = '<table>'::regclass;
没有实时数据库时,RLS/强制隔离相关结论保持 needs_api,不得静默通过。
发现按 schema/finding.schema.json 输出,例如 M9.documents.rls_off_shared_tenant、M9.invoices.no_tenant_id、M9.events.index_not_tenant_leading;每条 finding 的 evidence.observed 需逐字引用 DDL/索引/策略,verification.reproduce 为可运行查询,method 为 ddl_parse/schema_introspect/index_check,expected_impact 带区间与置信度标注。
诚实性要求:sev-5 隔离上限须以该表确实承担租户隔离为前提,无实时或已声明 RLS 证据时只能为 needs_api 或方向性,不得静默封顶;应用层 WHERE tenant_id = ? 是真实缓解但不是数据库保证,一次遗漏即泄漏,不可等同于 RLS。