Skip to content

fix(db): deny retention metrics user access#1961

Merged
riderx merged 4 commits into
mainfrom
codex/fix-retention-metrics-rls
Apr 27, 2026
Merged

fix(db): deny retention metrics user access#1961
riderx merged 4 commits into
mainfrom
codex/fix-retention-metrics-rls

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Apr 27, 2026

Summary (AI generated)

  • keep public.daily_revenue_metrics and public.processed_stripe_events as backend-only tables under the repo's deny-all RLS pattern
  • replace the incorrect service_role policy approach with explicit deny-all policies on both internal Stripe retention tables
  • extend the RLS pgTAP coverage so those deny-all policies stay explicit
  • harden the CLI metadata test against upload retries by varying bundle contents per attempt and retrying the post-upload compatibility read

Motivation (AI generated)

The retention metrics tables are backend-only Stripe webhook ledgers. In environments where RLS is auto-enabled for new public tables, they can end up with RLS enabled but no policies, which triggers the Supabase linter and leaves the intended access model implicit.

For backend-only tables in this repo, service_role is not modeled through RLS policies because it bypasses RLS. The established pattern is to keep grants restricted and add an explicit deny-all policy so user-context access stays impossible while the linter remains satisfied.

The branch also needed a small test-only fix because the existing CLI metadata test could fail on Vitest retries: once an initial upload partially succeeded, the retry re-used identical bundle contents and hit the duplicate-checksum guard before it could re-check metadata.

Business Impact (AI generated)

This removes noisy security-linter findings on admin retention analytics tables, makes the backend-only access model explicit in the same way as similar internal tables, and reduces migration review friction without changing customer-facing behavior.

The metadata test hardening lowers false-negative CI failures, which keeps schema changes from getting blocked by an unrelated flaky retry path.

Test Plan (AI generated)

  • bunx supabase db reset --yes --workdir .context/supabase-worktrees/f5206569
  • bunx supabase test db supabase/tests/26_test_rls_policies.sql --workdir .context/supabase-worktrees/f5206569
  • bunx eslint tests/cli-meta.test.ts
  • GitHub CI passes
  • local tests/cli-meta.test.ts upload path is healthy enough to re-run without name resolution failed

Generated with AI

Summary by CodeRabbit

Release Notes

  • Chores
    • Enhanced database security by strengthening Row Level Security policies on critical data tables to prevent unauthorized access.
    • Improved system reliability through enhanced test coverage for security policies and added retry mechanisms to ensure stability in compatibility checks.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0b55070-3e68-4484-b2bb-18fc6cc65315

📥 Commits

Reviewing files that changed from the base of the PR and between ac736fb and d13788a.

📒 Files selected for processing (2)
  • supabase/migrations/20260427110612_retention_metrics_service_role_rls.sql
  • supabase/tests/26_test_rls_policies.sql
🚧 Files skipped from review as they are similar to previous changes (2)
  • supabase/tests/26_test_rls_policies.sql
  • supabase/migrations/20260427110612_retention_metrics_service_role_rls.sql

📝 Walkthrough

Walkthrough

Enables Row Level Security on two retention metric tables and replaces existing policies with a blanket deny-all policy; adds pgTAP checks verifying RLS and the deny policy; and adds a retry/backoff loop plus a bundle-writing helper to CLI metadata tests.

Changes

Cohort / File(s) Summary
Supabase RLS Migration
supabase/migrations/20260427110612_retention_metrics_service_role_rls.sql
Enables RLS on public.daily_revenue_metrics and public.processed_stripe_events; drops existing "Allow service_role full access" and "Deny all access" policies and recreates a "Deny all access" policy on both tables with FOR ALL, USING (false), and WITH CHECK (false).
RLS Tests
supabase/tests/26_test_rls_policies.sql
Increases pgTAP plan by 4 assertions (39→43); adds assertions ensuring each table has exactly one policy named Deny all access; adds checks that relrowsecurity is enabled for both tables.
CLI Metadata Tests
tests/cli-meta.test.ts
Wraps SDK/table assertions in a 5-attempt retry loop with incremental 250ms backoff, capturing and rethrowing the last error; adds writeBundleContent helper to generate dist/index.html with metadata-${testSemver} marker and CapacitorUpdater.notifyAppReady() before uploading baseline bundle.

Sequence Diagram(s)

(No sequence diagrams generated — changes are database migrations, test assertions, and test retry/helpers without a multi-component control-flow that benefits from a diagram.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Cap-go/capgo#1327 — similar SQL migration that enables RLS and creates a "Deny all access" policy on internal public tables.

Suggested labels

codex

Poem

🐰 I hopped through SQL rows tonight,

Planted fences, set rules tight.
Tests will try, then try once more,
A helper writes the bundle door,
Metrics safe — I nibble light. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(db): deny retention metrics user access' directly reflects the main change: adding deny-all RLS policies to restrict user access to backend-only retention metrics tables.
Description check ✅ Passed The PR description includes Summary, Motivation, Business Impact, and Test Plan sections covering the changes across database migrations, tests, and CLI hardening, though the author-provided description appears to be AI-generated rather than originally written.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-retention-metrics-rls

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 SQLFluff (4.1.0)
supabase/migrations/20260427110612_retention_metrics_service_role_rls.sql

User Error: No dialect was specified. You must configure a dialect or specify one on the command line using --dialect after the command. Available dialects:
ansi, athena, bigquery, clickhouse, databricks, db2, doris, duckdb, exasol, flink, greenplum, hive, impala, mariadb, materialize, mysql, oracle, postgres, redshift, snowflake, soql, sparksql, sqlite, starrocks, teradata, trino, tsql, vertica

supabase/tests/26_test_rls_policies.sql

User Error: No dialect was specified. You must configure a dialect or specify one on the command line using --dialect after the command. Available dialects:
ansi, athena, bigquery, clickhouse, databricks, db2, doris, duckdb, exasol, flink, greenplum, hive, impala, mariadb, materialize, mysql, oracle, postgres, redshift, snowflake, soql, sparksql, sqlite, starrocks, teradata, trino, tsql, vertica


Comment @coderabbitai help to get the list of available commands and usage tips.

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented Apr 27, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing codex/fix-retention-metrics-rls (d13788a) with main (847a8b9)

Open in CodSpeed

@riderx riderx marked this pull request as ready for review April 27, 2026 11:54
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@supabase/tests/26_test_rls_policies.sql`:
- Around line 231-247: Add explicit checks that row-level security is enabled
for the two tables by asserting relrowsecurity is true for
public.daily_revenue_metrics and public.processed_stripe_events: locate the
existing tests that call policies_are for those tables and add corresponding
assertions that query pg_class.relrowsecurity (or use a helper/assert function)
to ensure relrowsecurity = true for both tables so the test fails if RLS is ever
turned off.

In `@tests/cli-meta.test.ts`:
- Around line 17-26: The retry loop in tests/cli-meta.test.ts currently only
catches assertion failures because sdk.checkBundleCompatibility(...) is called
outside the try, so if that SDK call throws the loop exits immediately; move the
sdk.checkBundleCompatibility call inside the try (or wrap it in its own
try/catch) so that both thrown errors from checkBundleCompatibility and
assertion failures are caught and the retry/backoff can continue, and apply the
same change to the similar block around lines 47-53 that calls
sdk.checkBundleCompatibility or equivalent.
- Around line 32-44: The test's ❌ branch only checks for presence of
localVersion and doesn't assert incompatibility; update the else branch to
assert mismatch or use the SDK compatibility field if present. Specifically, in
the block that inspects column4, replace the current else assertions with either
expect(packageEntry!.localVersion).not.toEqual(packageEntry!.remoteVersion) (or
use semver comparison/coerce if you expect range resolution) or, if the SDK
payload exposes a compatibility flag (e.g., packageEntry!.compatible or
packageEntry!.isCompatible), assert
expect(packageEntry!.compatible).toBe(false); keep references to packageEntry,
column4, column2 and column3 to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c10e06a0-3f56-4d33-8384-c44712e79716

📥 Commits

Reviewing files that changed from the base of the PR and between b917a8a and 34c8133.

📒 Files selected for processing (3)
  • supabase/migrations/20260427110612_retention_metrics_service_role_rls.sql
  • supabase/tests/26_test_rls_policies.sql
  • tests/cli-meta.test.ts

Comment thread supabase/tests/26_test_rls_policies.sql
Comment thread tests/cli-meta.test.ts
Comment thread tests/cli-meta.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/cli-meta.test.ts (1)

37-44: ⚠️ Potential issue | 🟠 Major

branch still doesn’t assert incompatibility.

On Line 42 and Line 43, the path only checks presence. Since Line 32 and Line 33 already validate version fields exist, this branch can still pass false positives for incompatible cases.

Suggested assertion tightening
-      if (column4 === '✅') {
-        // Compatible - versions should match (considering semver resolution)
-        expect(packageEntry!.remoteVersion).toBeDefined()
-      }
-      else {
-        // Incompatible - just verify the entry exists
-        expect(packageEntry!.localVersion).toBeDefined()
-      }
+      const compatibilityFlag = packageEntry!.compatible ?? packageEntry!.isCompatible
+      if (column4 === '✅') {
+        if (compatibilityFlag !== undefined)
+          expect(compatibilityFlag).toBe(true)
+        else
+          expect(packageEntry!.localVersion).toEqual(packageEntry!.remoteVersion)
+      }
+      else {
+        if (compatibilityFlag !== undefined)
+          expect(compatibilityFlag).toBe(false)
+        else
+          expect(packageEntry!.localVersion).not.toEqual(packageEntry!.remoteVersion)
+      }
#!/bin/bash
set -euo pipefail

# Verify the canonical compatibility field exposed by the SDK payload
rg -nP --type=ts -C4 '\bcheckBundleCompatibility\s*\('
rg -nP --type=ts -C4 '\b(compatible|isCompatible|status)\b'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli-meta.test.ts` around lines 37 - 44, The failing branch labeled '❌'
only checks existence; tighten it to assert actual incompatibility by verifying
mismatch between packageEntry versions or an explicit incompatible flag: for
example, in the else branch where column4 !== '✅', assert that
packageEntry!.localVersion !== packageEntry!.remoteVersion (or if the payload
exposes a compatibility boolean, assert packageEntry!.compatible === false)
using the existing variables column4 and packageEntry (and its
localVersion/remoteVersion fields).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/cli-meta.test.ts`:
- Around line 37-44: The failing branch labeled '❌' only checks existence;
tighten it to assert actual incompatibility by verifying mismatch between
packageEntry versions or an explicit incompatible flag: for example, in the else
branch where column4 !== '✅', assert that packageEntry!.localVersion !==
packageEntry!.remoteVersion (or if the payload exposes a compatibility boolean,
assert packageEntry!.compatible === false) using the existing variables column4
and packageEntry (and its localVersion/remoteVersion fields).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7678af39-ae4f-4bfd-b120-08036644c854

📥 Commits

Reviewing files that changed from the base of the PR and between 34c8133 and ac736fb.

📒 Files selected for processing (2)
  • supabase/tests/26_test_rls_policies.sql
  • tests/cli-meta.test.ts

@riderx riderx changed the title fix(db): add retention metrics service-role RLS policies fix(db): deny retention metrics user access Apr 27, 2026
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit 9b86ad3 into main Apr 27, 2026
16 checks passed
@riderx riderx deleted the codex/fix-retention-metrics-rls branch April 27, 2026 13:08
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.

1 participant