SEC.RLS.OWNER_UNRESTRICTED — The table's owner is exempt from its policies
- Category: security
- Severity: medium
- Level: 0
- Confidence: deterministic
- Downtime class: none — the finding is about a table's state, not about a statement
- Stability: stable
- Suites: audit
- Applies to: PostgreSQL 18
The same missing word, seen from the other side
ENABLE ROW LEVEL SECURITY without FORCE exempts the table's owner. The connection SQLens audited
with is restricted by your policies exactly as intended — the owner is not, and somebody uses that
role: migrations, a maintenance command, a console session, the deploy pipeline. Each of those reads
and writes across tenants with no policy consulted and nothing reported.
Why medium here and high there
Not because the mechanism is weaker — it is identical — but because of who reaches it.
SEC.RLS.NOT_FORCED fires when the exemption applies to the
connection your application serves requests with, so ordinary traffic bypasses the isolation. This one
fires when it applies to a role a person or a job uses deliberately: a smaller and more supervised
surface.
A run whose reading could not establish which role it connected as reports through this rule — the weaker claim is the one that stays true when the stronger cannot be checked.
Bad
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant')::uuid);
-- `orders` is owned by app_owner; your web connection is app_readwrite
Good
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
Then give the migration role a deliberate way through if it needs one — a policy TO app_owner USING (true) is reviewable, where an implicit exemption is not.
Related
SEC.RLS.NO_POLICY covers a table with RLS on and no policy at all;
neither FORCE rule speaks about that state.