Skip to content

feat: detect and protect if pg_grapqhl on#158

Merged
samrose merged 2 commits into
mainfrom
pg_grapqhl
Apr 26, 2026
Merged

feat: detect and protect if pg_grapqhl on#158
samrose merged 2 commits into
mainfrom
pg_grapqhl

Conversation

@samrose
Copy link
Copy Markdown
Contributor

@samrose samrose commented Apr 26, 2026

Summary

Added a WARN-level security lint that fires when both:

  • pg_graphql extension is installed (the /graphql/v1 endpoint exists), and
  • the anon role has SELECT on a user-schema table

For each matching table, the lint reports the schema/table and links to the
new doc page that walks through the full Introspection Lockdown Guide
(table-level revoke, blanket revoke, default-privileges fix, graphql.resolve
revoke, and authenticated re-grant pattern).

Files

  • lints/0026_pg_graphql_anon_table_exposed.sql — view in lint schema; uses
    pg_catalog.has_table_privilege('anon', oid, 'SELECT') gated by an EXISTS
    against pg_extension
  • docs/0026_pg_graphql_anon_table_exposed.md — full guide (rationale, options
    1–3, example, verification, quick reference, false-positive note)
  • test/sql/0026_pg_graphql_anon_table_exposed.sql + test/expected/0026_…out —
    baseline / negative (no extension) / positive (extension + anon SELECT) /
    resolution (revoke)
  • bin/installcheck — added -f lints/0026*.sql
  • test/sql/queries_are_unionable.sql + expected — added new view to the union
    check
  • splinter.sql — regenerated via bin/compile.py

Test result: All 25 tests passed. on supabase/postgres:15.1.1.13.

@samrose samrose requested a review from dnywh April 26, 2026 12:12
Comment thread docs/0026_pg_graphql_anon_table_exposed.md Outdated
Comment thread lints/0026_pg_graphql_anon_table_exposed.sql Outdated
… positive case and confirms both rows appear with the correct object_type
@samrose samrose merged commit 965ca61 into main Apr 26, 2026
2 checks passed
charislam pushed a commit to supabase/supabase that referenced this pull request Apr 26, 2026
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES
   
## What kind of change does this PR introduce?
                  
Feature — wires up the new advisor lint `pg_graphql_anon_table_exposed`
so it renders properly in Studio and ships with
self-hosted Supabase. The lint itself was added to splinter in
supabase/splinter#158 (already merged).
## What is the current behavior?
   
Splinter's `main` exposes lint `0026_pg_graphql_anon_table_exposed`,
which detects tables, views, and materialized views
whose schema is visible through the public `/graphql/v1` introspection
endpoint when the `anon` role has `SELECT` on them.
The hosted advisor (mgmt-api) auto-fetches `splinter.sql` from
raw.githubusercontent.com, so the lint will start firing on
  cloud projects, but:

- Studio has no `lintInfoMap` entry for it, so the row renders without
an icon, title mapping, "Fix" CTA, or category
  classification.
- Self-hosted Supabase ships with a vendored copy of the lint SQL in
`packages/pg-meta`; without an update there, self-hosted
users never see the lint at all.
   
## What is the new behavior?
                  
  Two minimal additions:

- **`apps/studio/components/interfaces/Linter/Linter.utils.tsx`** — adds
a `lintInfoMap` entry for
`pg_graphql_anon_table_exposed`: title `"pg_graphql Anon Role Exposes
Objects in Introspection"`, `Eye` icon, `security`
category, `"View object"` CTA pointing at the table editor scoped by
`metadata.schema` and `metadata.name`, docs link to the
  splinter docs page.
- **`packages/pg-meta/src/sql/studio/advisor/lints.ts`** — vendors the
lint's SQL block into `getLintsSQL()` so self-hosted
deployments include it. Follows the file's documented copy-paste
convention from splinter: every backtick inside SQL string
literals is escaped (`` ` `` → `` \` ``), and the hardcoded docs URL is
replaced with `${literal(\`${docsUrl}/...\`)}`.
No changes to the OpenAPI surface, no changes to the `LINT_TYPES`
literal union (auto-generated; matches the precedent of how
lints 0023–0025 were added — Studio's `LintInfo.name` is typed as
`string`, not the strict enum).
  ## Additional context

- Splinter PR (merged): supabase/splinter#158
- Splinter lint source:
https://github.com/supabase/splinter/blob/main/lints/0026_pg_graphql_anon_table_exposed.sql
- Splinter docs page:
https://github.com/supabase/splinter/blob/main/docs/0026_pg_graphql_anon_table_exposed.md
- The hosted advisor flow that fetches splinter.sql automatically lives
in the platform mgmt-api (`getLintSql` in
`advisors-utils.ts`), with a 1-hour cache TTL — cloud projects will pick
up the new lint independently of this PR; this PR is
   about the Studio display mapping and the self-hosted SQL bundle.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a new security linter check that identifies tables and views
exposed to anonymous GraphQL access, with warnings and remediation
guidance to help resolve the issue.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@dnywh dnywh deleted the pg_grapqhl branch April 26, 2026 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants