Skip to content
Back to skills

Rls Policy Auditor

ASecurity

Audit and (where asked) author row-level-security policies for a tenant-scoped database — inspect SELECT/INSERT/UPDATE/DELETE policies per table for missing tenant scope, deny-by-default gaps, policy recursion, unsafe SECURITY DEFINER helpers, over-broad GRANTs, service-role/superuser leakage, and frontend-derived (client-supplied) tenant scope, and ALWAYS deliver a negative-test plan proving wrong-tenant/wrong-role/missing-auth access is denied per command. Use when asked to review, write, o...

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 11, 2026
securitygosqlexpressgitapidatabasefrontendsecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add ModernNomad-98/Project-Aegis --skill rls-policy-auditor --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rls Policy Auditor?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rls Policy Auditor
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/modernnomad-98-rls-policy-auditor/badge)](https://www.skillsdirectory.com/skills/modernnomad-98-rls-policy-auditor)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rls-policy-auditor
description: Audit and (where asked) author row-level-security policies for a tenant-scoped database — inspect SELECT/INSERT/UPDATE/DELETE policies per table for missing tenant scope, deny-by-default gaps, policy recursion, unsafe SECURITY DEFINER helpers, over-broad GRANTs, service-role/superuser leakage, and frontend-derived (client-supplied) tenant scope, and ALWAYS deliver a negative-test plan proving wrong-tenant/wrong-role/missing-auth access is denied per command. Use when asked to review, write, or fix RLS/tenant-scoping policies, when RLS is enabled but unverified, or before shipping a tenant table. Do NOT use to design the roles-by-permissions matrix (authorization-matrix-designer), for app/API-layer isolation tests (multi-tenant-security-tester), general migration review (secure-migration-reviewer), or non-database threat modeling (threat-modeler).
---

# RLS Policy Auditor

**Reading key:** RLS means row-level security; JWT means JSON Web Token;
SQL means Structured Query Language; DDL means data definition language;
API means application programming interface, the programmatic surface
another system calls. `GRANT` assigns database privileges; `BYPASSRLS` lets
a database role skip row policies. `SECURITY DEFINER` runs a PostgreSQL
function with its owner's privileges rather than the caller's; if that owner
is not subject to RLS — for example, a table owner without `FORCE ROW LEVEL
SECURITY`, superuser, or `BYPASSRLS` role — the helper can bypass caller row
restrictions, so it requires a pinned `search_path`, least privilege, and
caller-scoped returns.
PostgreSQL's [row-security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
and [policy](https://www.postgresql.org/docs/current/sql-createpolicy.html)
documentation define the effective behavior described here.

## Purpose

Produce a database-level verdict on whether row-level security actually
enforces tenant and role scope, plus the negative tests that prove it. This
skill absorbs both policy authoring and negative-test design
([reconciliation §3](../../../docs/reconciliation/step-0-reconciliation-v4.md)):
it inspects each table's SELECT/INSERT/UPDATE/DELETE policies for the
classic failure modes, writes or corrects policies where asked, and ALWAYS
delivers a per-command negative-test plan. RLS enabled needs an effective
policy review: no applicable policy defaults to deny for a role subject to
RLS, while an over-broad permissive policy or client-supplied tenant scope
can expose rows. The deliverable is severity-ranked
findings with the offending policy quoted, remediation, and executable
negative tests as SQL/session sequences.

## Use When

- Use when: asked to review, audit, write, or fix RLS policies or tenant
  scoping at the database layer.
- Use when: RLS is enabled on tenant tables but nobody has verified the
  policies deny cross-tenant access.
- Use when: before shipping a new tenant-owned table, or after adding
  `SECURITY DEFINER` helpers or new GRANTs.
- Use when: converting an RLS finding into a permanent negative test.
- Do NOT use when: the isolation tests belong at the app/API layer across
  many surfaces — that is `multi-tenant-security-tester` (DB and app layers
  are complementary; hand off the non-DB surfaces).
- Do NOT use when: reviewing a whole migration for privilege/destructive
  changes beyond RLS — that is `secure-migration-reviewer` (route the RLS
  portion here).
- Do NOT use when: the ask is design-time threat enumeration —
  `threat-modeler`.
- Do NOT use when: tenant semantics are undefined — `tenant-modeler` first.
- Do NOT use when: designing the roles × permissions × resources matrix
  itself — that is `authorization-matrix-designer`; this skill audits and
  authors the database RLS policies that enforce tenant scope.

## Inputs to Inspect

1. The schema and migrations: `CREATE TABLE`, `ALTER TABLE … ENABLE ROW LEVEL
   SECURITY`, every `CREATE POLICY`, `CREATE FUNCTION`, and `GRANT`. No policy
   text or schema → Stop Conditions.
2. How tenant/user context reaches the database: session settings
   (`current_setting`), JWT claims, `auth.uid()`/`auth.jwt()` equivalents,
   connection roles — and whether that context is server-established or
   client-influenced.
3. The tenant model (`tenant-modeler`) and authorization matrix
   (`authorization-matrix-designer`) — policies are audited against THESE
   definitions of tenant and role, not re-derived here.
4. Roles and their GRANTs: application role, service/`service_role`, anon,
   and any role that BYPASSRLS or owns the tables.
5. Helper functions used inside policies: their `SECURITY` mode, `search_path`,
   and whether they re-query the same table (recursion risk).
6. Existing RLS tests, if any, and prior incidents.

## Workflow

1. **Confirm real policy text exists** and pin the tenant/role definitions
   being enforced. Undefined boundary → stop and route to `tenant-modeler`.
2. **Inventory tables** that hold tenant-owned data; for each, record whether
   RLS is ENABLED, whether it is FORCED (owners bypass otherwise), and which
   policies exist per command. RLS disabled on a tenant table is a finding.
   If RLS is not forced, determine whether the table-owner role can serve
   user-influenced requests; owner bypass is a risk only with a reachable path.
3. **Audit each command separately — SELECT, INSERT, UPDATE, DELETE** — using
   [references/rls-audit-checklist.md](references/rls-audit-checklist.md):
   - **SELECT/USING:** does every row require the caller's tenant scope?
   - **INSERT/effective check:** can a row be written with another tenant's
     id, or no tenant id? No applicable policy denies; an INSERT policy uses
     `WITH CHECK` to permit rows.
   - **UPDATE:** audit both existing-row `USING` and resulting-row checks.
     If `WITH CHECK` is omitted from an UPDATE/ALL policy, PostgreSQL uses
     its `USING` expression for the new row too; inspect the effective
     expression, not just whether the clause is written.
   - **DELETE/USING:** can a caller delete another tenant's rows?
4. **Hunt the classic failure modes:** missing tenant scope; deny-by-default
   gap (permissive policy that ORs open access); **recursion** (policy calls a
   function that selects the same table under RLS); unsafe **SECURITY DEFINER**
   (no fixed `search_path`, broader than needed, returns rows bypassing
   caller scope); over-broad **GRANT** (e.g. `GRANT ALL … TO anon`);
   **service-role leakage** (service role reachable from a client-influenced
   path, or BYPASSRLS role used in request handling); **frontend-derived
   scope** (tenant id taken from a client value the user controls rather than
   a server-established session/JWT claim).
5. **Rank findings.** A confirmed cross-tenant read or write via policy is
   CRITICAL; each needs the concrete path (which command, which caller, which
   client-controlled input). No demonstrable path → cap at medium and name
   the confirming test.
6. **Author/correct policies where asked** — deny-by-default, tenant scope
   from server context, explicit `WITH CHECK` where it makes the write rule
   clearer or different from `USING`, `SECURITY INVOKER`
   helpers with fixed `search_path` unless a DEFINER is justified and minimal.
   Present as a migration for `secure-migration-reviewer` to gate; do not
   apply to a live DB from this skill.
7. **Write the negative-test plan (mandatory)** per command and per failure
   mode: set the session to tenant A, attempt B's rows for SELECT/UPDATE/
   DELETE (expect zero rows / zero affected), attempt INSERT/UPDATE writing
   B's tenant id (expect rejection for the scoped app role), and include
   the positive control (A affects only A's rows). Test anonymous access
   against its intended policy. For service/owner/BYPASSRLS roles, verify
   they are unreachable from client-influenced paths and document any
   separately authorized privileged operation and expected access. Provide
   runnable SQL session sequences.
8. **Deliver** findings, corrected policies (if authored), and the negative
   tests, with an honest list of tables/commands not audited.

## Output Format

```
RLS AUDIT — <database/scope>
Enforced against: <tenant + role definitions (from tenant-modeler / authz matrix)>
Context source: <server session/JWT — or client-influenced (finding)>
Table inventory: <table — RLS enabled? forced? policies per command>
Findings (severity-ranked):
  [CRITICAL|HIGH|MEDIUM|LOW] <table.command> — <failure mode>
    Policy: <quoted policy or "absent">
    Path: <command + caller + client-controlled input → cross-tenant effect>
    Fix: <corrected policy / direction>
Authored/corrected policies (if requested): <SQL — as a migration, ungated>
Negative-test plan (per command):
  <id> — SET context = tenant A; <attempt on B> — EXPECT <0 rows / rejected>
         + positive control: <A on A's rows> — EXPECT allow
Not audited: <tables/commands + why>
Handoffs: <secure-migration-reviewer to gate the migration; multi-tenant-security-tester for app-layer>
```

## Validation Checklist

- [ ] Every tenant table's RLS enabled/forced status recorded; unprotected
      tables flagged.
- [ ] SELECT, INSERT, UPDATE, DELETE audited SEPARATELY per table.
- [ ] Effective INSERT and UPDATE new-row checks verified, including
      UPDATE/ALL implicit `USING` fallback when `WITH CHECK` is absent.
- [ ] Recursion, SECURITY DEFINER search_path, broad GRANTs, service-role
      leakage, and frontend-derived scope each explicitly checked.
- [ ] Every finding quotes the offending policy (or "absent") and states a
      concrete cross-tenant path; CRITICALs have a demonstrable path.
- [ ] Per-command negative tests and positive controls cover the scoped
      application and anonymous roles; privileged-role reachability and
      intended bypass are checked separately.
- [ ] Authored policies are deny-by-default and use server-derived scope;
      delivered as a migration, not applied live.
- [ ] Not-audited list present.

## Security Rules

- RLS enabled with no applicable policy defaults to deny for roles subject to
  RLS; this may block legitimate access but is not cross-tenant exposure.
  Audit the effective command/role policy: permissive policies OR together,
  restrictive policies AND with that grant, and bypass roles/owners may
  avoid RLS unless FORCE is used where applicable.
- Client-supplied / frontend-derived tenant scope in a policy is a finding
  regardless of demonstrated exploit — tenant scope must come from
  server-established session/JWT context.
- `SECURITY DEFINER` helpers without a pinned `search_path` are a finding
  (search-path hijacking); DEFINER is used only where justified and minimal.
- Service-role / BYPASSRLS credentials must not be reachable from request
  handling influenced by end users.
- No RLS finding is suppressed without written rationale via
  `human-approval-boundary`; every audit ships negative tests
  ([historical master prompt, section 6](../../../docs/prompts/claude-skills-master-generation-prompts-v4.md)).

## Gotchas

- A correct SELECT policy does not prove INSERT safety: inspect the applicable
  INSERT check and role, and test writing another tenant's id.
- UPDATE requires safe existing-row and new-row checks. An omitted explicit
  `WITH CHECK` on UPDATE/ALL reuses `USING`; tenant hopping requires an
  effective check that permits the resulting wrong-tenant row.
- Policies calling a helper that selects the same table re-enter RLS and
  either recurse or silently return nothing under load — test, don't assume.
- `FORCE ROW LEVEL SECURITY` matters when owner-role requests must be
  constrained; without it the owner bypasses policies. Check whether that
  role is reachable from a user-influenced request path.
- PostgreSQL RLS with zero applicable policies denies all rows to roles
  subject to RLS. A permissive policy can grant rows, so read every policy
  and test with the actual application role.
- A DEFINER helper that returns rows to the caller can launder around the
  caller's own RLS — audit what the helper returns, not just that it exists.

## Stop Conditions

- No policy text, schema, or migration is available → stop; this skill does
  not audit RLS from a description.
- Tenant/role semantics are undefined or contested → stop; `tenant-modeler` /
  `source-of-truth-reconciler` first.
- A CRITICAL cross-tenant policy hole is confirmed in a live database → report
  the minimal reproduction immediately; containment is the human's call
  (`human-approval-boundary`) before continuing.
- Asked to APPLY authored policies to a live database → stop; deliver them as
  a migration for `secure-migration-reviewer` and human approval — this skill
  audits and authors text, it does not run DDL on live systems.

## Supporting Files

- [references/rls-audit-checklist.md](references/rls-audit-checklist.md) —
  per-command audit questions, the seven failure-mode catalog with detection
  and fix, and the negative-test session-sequence templates.
- `evals/evals.json` — trigger + behavior cases.
- `evals/trigger-evals.json` — discrimination against
  `multi-tenant-security-tester`, `secure-migration-reviewer`, and the shipped
  `tenant-isolation-reviewer` (tenant security cluster).

Files in this skill

  • SKILL.md10.7 KB
  • evals/evals.json2.7 KB
  • evals/trigger-evals.json2.2 KB
  • references/rls-audit-checklist.md4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.