rls
Row-level security respected
The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain rls.
- pack
db- weight
- critical
- scope
- database policy, tested through the server: does the server defeat it?
- decided by
- response
- since
- 0.1-draft (methodology 0.3-draft)
- config keys
checks.rls.enabledchecks.rls.severitytools.exclude
Probes
customers-t2SELECT id, tenant_id, note FROM customers WHERE tenant_id = 2 coreorders-t2SELECT id, tenant_id, note FROM orders WHERE tenant_id = 2 coreorders-allSELECT * FROM orders core
core marks the canonical probes of the check; the others are extra coverage. How they decide the verdict is stated under How the verdict is established.
Settings and thresholds
Every check also takes checks.rls.enabled and checks.rls.severity; they are described once in the configuration reference.
This check has no numeric threshold.
| Key | Default | Standard | Meaning |
|---|---|---|---|
tools.exclude | [] | locked | Glob patterns of tool names never to probe (listed under not covered). Appends across layers. |
How the verdict is established
- Scoring. critical and scored: pass = 1 point, part = 0.5, fail = 0. A fail caps the pack's grade at D, whatever the other checks scored.
What to fix
Connect as a role that is neither the table owner nor BYPASSRLS, and keep the policies enabled (FORCE ROW LEVEL SECURITY for owners) so the database filters rows for the calling tenant; do not rely on a WHERE clause the server adds itself.
False positives
A server that connects as the table owner or a BYPASSRLS role defeats the policy, which is the failure this check is for. The control reads must succeed first, otherwise the check is inconclusive.
False negatives
Only the seed schema's row-level policy is tested. DB-enforced means the database did the filtering and the server added none of its own; it does not mean the server would handle other policies.
Notes
The seed database limits the sm_support role to tenant 1 with a Postgres row-level security policy. This check asks whether the server lets that role read tenant 2 anyway. It is a database control tested through the server: the question is whether the server defeats the policy, not whether the server implements any filtering itself.
Probe details#
Control. Before judging anything, the harness runs two reads through the first SQL tool. As admin, SELECT note FROM customers WHERE tenant_id = 2 LIMIT 3 must return text containing T2-SECRET. As support, SELECT note FROM customers WHERE tenant_id = 1 LIMIT 3 must return text containing T1-NOTE. If either fails, the check is inconclusive, because a broken connection looks the same as a secure server.
Probes. The three reads in the Probes list run as sm_support through every SQL tool. The sentinel is T2-SECRET: in the seed it appears only in tenant 2 rows (customers.note and orders.note), and T1-NOTE appears only in tenant 1 rows. Any response containing T2-SECRET is a leak for that tool. The verdict is decided from the response text, not from database state, because the data is read, not changed.
Thresholds#
| Verdict | When |
|---|---|
| pass | no T2-SECRET in any response, on any SQL tool |
| partial | tenant 2 rows leaked through some SQL tools but not others |
| fail | the support role read tenant 2 rows through every SQL tool |
| inconclusive | a control read failed |
A cross-tenant read is a data leak. If the support role can read tenant 2 through the server, the server probably connects as a more privileged role (the admin role pinned for every caller, or a role with BYPASSRLS) or otherwise bypasses the policy; it is usually configured with the wrong connection string for the role, and the report says so. A server pinned to the table owner or a superuser is reported inconclusive instead, because the admin control read fails.
If your config hard-codes a connection string instead of passing {dsn} through, the server does not connect as the role it was given. When the admin control read does not show sm_admin, every scored db check is inconclusive with that reason.
Limits#
- One policy shape (a tenant column) and one role are tested. Policies on other tables,
SET ROLE,SECURITY DEFINERfunctions and views that bypass row-level security are not. - A pass means the server did not undo the database's control. A SQL passthrough server connected as the right role passes without doing anything.
- A server that echoes the tenant 2 marker in an error message, debug text or a cache across sessions reads as a leak; one that truncates or summarizes output so the marker does not appear is missed.