Skip to content

pgrls/pgrls-action

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

pgrls-action

CI License: MIT

GitHub Action that runs pgrls — a static analyzer for Postgres Row-Level Security — against a database in CI, so policy bugs (broken tenant/per-user scoping, inverted auth checks, write-side holes, performance traps) fail the build instead of shipping. 51 lint rules, 17 with mechanical auto-fixes, MIT-licensed.

pgrls lints a live database, not SQL files. The action installs pgrls from PyPI and runs pgrls lint; your workflow is responsible for standing up a Postgres instance and applying your schema/migrations to it first (a service container is the usual way — see below).

New in pgrls 0.16.0 — SEC038 (semantic anonymous-read, Z3-backed)

SEC038 is the semantic sibling of the always-on SEC004: instead of matching the literal auth.uid() IS NULL OR … shape, it uses the Z3 SMT solver to prove that a read-capable policy's USING clause is unconditionally true for an unauthenticated session — catching inverted-auth variants (e.g. NOT (auth.uid() IS NOT NULL) OR …) that pattern-matching misses. As of pgrls 0.16.0 the Z3 solver ships in the base install, so SEC038 runs out of the box — no extra setup — alongside the always-on syntactic SEC004. Pin pgrls/pgrls-action@v1 to track the current rule set.

Quick start

name: RLS lint
on: [pull_request]

jobs:
  pgrls:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_PASSWORD: postgres
        ports: ["5432:5432"]
        options: >-
          --health-cmd pg_isready --health-interval 10s
          --health-timeout 5s --health-retries 5
    env:
      DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
    steps:
      - uses: actions/checkout@v4

      # Apply your schema / migrations to the throwaway Postgres so pgrls
      # can introspect the real RLS state. Swap in your migration tool
      # (sqitch, flyway, alembic, dbmate, prisma, `psql -f schema.sql`, …).
      - run: psql "$DATABASE_URL" -f schema.sql

      - uses: pgrls/pgrls-action@v1
        with:
          schemas: public
          fail-on: error

pgrls reads $DATABASE_URL from the job environment automatically, so most workflows don't need the database-url input.

Code scanning (SARIF)

Upload findings to GitHub code scanning so they appear in the Security tab and inline on the PR:

      - uses: pgrls/pgrls-action@v1
        with:
          format: sarif
          output: pgrls.sarif
        # Don't let a nonzero exit skip the upload; the SARIF carries the findings.
        continue-on-error: true

      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: pgrls.sarif

The default format: github instead emits inline run annotations (no upload step needed).

Supabase

Supabase projects keep their schema as migrations under supabase/migrations/, and their RLS policies lean on Supabase-provided objects — auth.uid(), auth.jwt(), the anon / authenticated roles, request.jwt.claim.*. pgrls is built for exactly these shapes (SEC004 / SEC038 catch a policy whose USING is true for an unauthenticated request; SEC033 flags scoping on the end-user-writable user_metadata claim; PERF001 flags an unwrapped auth.uid() re-evaluated per row).

The natural way to lint a Supabase project in CI is to stand up the local stack with the Supabase CLIsupabase start applies your migrations and creates the auth schema and roles your policies reference — then point pgrls at the local database:

name: RLS lint (Supabase)
on: [pull_request]

jobs:
  pgrls:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write   # required by upload-sarif
    steps:
      - uses: actions/checkout@v4

      - uses: supabase/setup-cli@v1
        with:
          version: latest

      # Boots local Postgres on :54322 and applies supabase/migrations,
      # plus the auth schema/roles your RLS policies depend on.
      - run: supabase start

      - uses: pgrls/pgrls-action@v1
        with:
          database-url: postgres://postgres:postgres@localhost:54322/postgres
          schemas: public
          format: sarif
          output: pgrls.sarif
        # SARIF carries the findings — don't let a nonzero exit skip the upload.
        continue-on-error: true

      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: pgrls.sarif

Findings show up in the Security tab and inline on the PR. To fail the build instead of (or as well as) reporting, drop the SARIF/upload-sarif plumbing and set fail-on: error (or warning) on the pgrls-action step.

Assumes a standard Supabase project layout (a supabase/ directory with config.toml and migrations/, as created by supabase init). supabase start runs entirely locally against Docker on the runner — no SUPABASE_ACCESS_TOKEN or project link needed.

Inputs

All inputs are optional.

Input Maps to Default
database-url --database-url $DATABASE_URL
schemas --schemas (comma-separated) pgrls default
config --config ./pgrls.toml if present
format --format (text/json/sarif/markdown/github/junit) github
fail-on --fail-on (error/warning/info) pgrls default (warning)
min-severity --min-severity
rule --rule (comma-separated → repeated) all rules
exclude-rule --exclude-rule (comma-separated → repeated)
baseline --baseline
output --output (write report to a file) stdout
args extra raw args appended to pgrls lint
version pgrls version to install from PyPI latest
python-version actions/setup-python version 3.x

Exit behavior

The action fails the step exactly when pgrls lint exits nonzero. By default pgrls fails on any finding at warning or above; set fail-on: error to gate only on errors, or wrap the step with continue-on-error: true to report without failing.

Versioning

Pin a major (pgrls/pgrls-action@v1) to get non-breaking updates, or a full tag (@v1.0.0) for an exact pin. The action installs the latest pgrls from PyPI unless you set the version input.

License

MIT — see LICENSE. pgrls itself: https://github.com/pgrls/pgrls.

About

GitHub Action that runs pgrls in CI — 51 lint rules for Postgres Row-Level Security (broken row scoping, inverted auth checks, write-side holes, perf traps); 17 with mechanical auto-fixes

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors