RowShield
Guides

A GitHub Actions RLS check: the design, and what works today

A pull request that adds a table should be answerable at merge time: will this ship protected? A GitHub Actions integration would put RowShield exactly there — scanning the target database after migrations apply and failing the job on findings at your chosen threshold.

That integration is planned, not shipped, and this page promises nothing about timing. What it can do honestly is publish the design, show the equivalent working today through plain steps calling the CLI, and describe the interim practice — scheduled scans and transition alerts — while the wrapper remains on the roadmap.

RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.

What the integration would check

The design follows the CLI contract exactly, because a wrapper earns trust by adding no semantics. Inputs: a connection-string secret, a severity threshold defaulting to high, and whether to print remediation SQL. Behaviour: run the standard nine-rule catalog evaluation against the migrated database.

Outputs map straight onto Actions conventions: exit 0 passes the step; exit 1 fails the job with findings annotated — disabled or absent row-level security, tautological policies, missing WITH CHECK, public buckets, exposed privileged keys among them; exit 2 marks the step failed-for-infrastructure rather than failed-for-policy, so a flaky network is not mistaken for a regression.

Adopting the pattern today

None of this requires the wrapper to exist, because the CLI is a plain executable that installs wherever Node runs, including hosted runners. No special permissions, no marketplace dependency, no additional party holding secrets beyond the repository you already have.

The same gate, expressed as an ordinary workflow step that any repository can adopt today without waiting for anything. One repository secret goes in, three exit codes come out, and no marketplace dependency or additional vendor access sits anywhere in between:

# Works today: an ordinary step, no special action required.
# SUPABASE_DB_URL is a repository secret holding a read-capable
# connection string.
- name: RLS policy check
  env:
    DATABASE_URL: ${{ secrets.SUPABASE_DB_URL }}
  run: |
    rowshield scan --db-url "$DATABASE_URL" --fail-on high --sql
    # exit 0 = clean, 1 = findings >= threshold, 2 = broken run

Where gates fail silently

Two blind spots deserve honesty regardless of tooling. First, a gate only sees databases it is pointed at: staging checks say nothing about production, and production drift — console hotfixes, restores — arrives without a pull request. Second, a service-role connection sees everything by definition, which is why behavioural truth comes from the probe instead: it requests the deployed surface with only the public anon key.

The interim practice covers the gap the gate cannot see: scheduled scans watch production between deploys, recording created, regressed and resolved transitions, with alerts routed to email, Slack Block Kit, Discord embeds or generic webhooks at per-destination severity thresholds. A gate judges the merge; the schedule watches everything that happens afterwards.

Planned, plainly

To repeat the standing sentence before anything else in this section: this integration is planned and not shipped; no date is promised, the design may change before arrival, and nothing here should be quoted as an available feature of any plan.

What the wrapper would eventually remove is ceremony rather than capability — installing the executable, writing the step, wiring annotations. Until it arrives, the page stays useful because every part except the convenience layer works today: the CLI step above is real, and the free audit at rowshield.dev/audit shows what a scan returns before you wire anything into CI at all.

Frequently asked

Is the RowShield GitHub Action available now?
No — it is planned, with no dates promised. The identical behaviour is available today as an ordinary workflow step invoking the CLI, as shown above, since the action would wrap the same command and exit-code contract.
How should the connection string be stored for CI?
As a repository secret, holding a read-capable role rather than a privileged one. The scan reads pg_catalog and bucket metadata only and never touches table rows, so the credential can match that scope.
Should the CI gate replace scheduled monitoring?
No — they cover different windows. A gate judges the moment of merge; scheduled scans watch production between deploys, where console edits, hotfixes and restores arrive without any pull request at all.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
github actions supabase rls checksupabase ci security steprls policy migration gatepostgres ci policy check