rls

Row-level security respected

shipped

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-t2 SELECT id, tenant_id, note FROM customers WHERE tenant_id = 2 core
  • orders-t2 SELECT id, tenant_id, note FROM orders WHERE tenant_id = 2 core
  • orders-all SELECT * 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.

KeyDefaultStandardMeaning
tools.exclude[]lockedGlob 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#

VerdictWhen
passno T2-SECRET in any response, on any SQL tool
partialtenant 2 rows leaked through some SQL tools but not others
failthe support role read tenant 2 rows through every SQL tool
inconclusivea 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 DEFINER functions 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.