Skip to content

fix: secure get_total_metrics rpc#1671

Merged
riderx merged 13 commits intomainfrom
riderx/fix-metrics-rpc
Feb 25, 2026
Merged

fix: secure get_total_metrics rpc#1671
riderx merged 13 commits intomainfrom
riderx/fix-metrics-rpc

Conversation

@riderx
Copy link
Member

@riderx riderx commented Feb 24, 2026

Summary (AI generated)

  • Hardened public.get_total_metrics to block unauthenticated/public access.
  • Added membership checks requiring caller org membership via public.org_users for user-based requests.
  • Preserved service-role/internal behavior by allowing service_role and postgres role calls without a JWT user context.
  • Kept existing metric aggregation behavior intact and removed existing anon/public grants on both overloads.

Motivation (AI generated)

The RPC was callable with only the publishable key and exposed usage/financial signals for valid organizations, creating an org-existence oracle and operational metrics leak.

Business Impact (AI generated)

  • Removes sensitive org usage telemetry exposure from public endpoints.
  • Stops unauthorized org fingerprinting via different response shapes.
  • Keeps existing authenticated billing/admin flows available while closing a privilege-escalation risk.

Test Plan (AI generated)

  • Ran bun lint.
  • Verify POST /rest/v1/rpc/get_total_metrics with anon/sb_publishable key now returns no rows.
  • Verify authenticated org-member users can still fetch metrics.
  • Verify authenticated non-members cannot read another org's metrics.
  • Verify service-role path remains functional for internal/batch jobs.

Summary by CodeRabbit

  • Bug Fixes
    • Strengthened authentication and authorization for metrics access.
    • Restricted metrics visibility to organization members only.
    • Improved caching and seeding logic for faster, more reliable metric retrieval.
    • Added a context-aware metrics endpoint so authenticated users can fetch their org's totals without supplying an org ID.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

Recreates and restricts three overloads of public.get_total_metrics, adding SECURITY DEFINER, org membership and auth checks, cache seeding/validation, dynamic date resolution (via Stripe anchor_day), ownership changes to postgres, and updated grants/revocations.

Changes

Cohort / File(s) Summary
Database Migration
supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql
Drops existing public.get_total_metrics overloads and recreates three: (org_id, start_date, end_date) (SECURITY DEFINER, explicit date range, cache validation/seed, org/user checks), (org_id) (derives dates from Stripe anchor_day and delegates), and () (resolves caller org via identity/org membership and delegates). Alters owners to postgres and adjusts REVOKE/GRANT for anon, public, authenticated, and service_role.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Caller as Caller Context
    participant Auth as Auth System
    participant OrgDB as Org Database
    participant Cache as Metrics Cache
    participant Metrics as Metrics Seeder/Calc

    Client->>Caller: invoke get_total_metrics(org_id, start_date, end_date) or overload
    Caller->>Auth: resolve auth.uid() / service_role check
    Auth->>OrgDB: verify org exists and user is org member (if not service_role)
    OrgDB-->>Auth: org/user validated
    Auth->>Cache: check cached metrics for org/date range
    alt cache missing or stale
        Cache->>Metrics: seed_org_metrics_cache(org_id, start_date, end_date)
        Metrics-->>Cache: updated metrics
        Cache-->>Client: return fresh metrics
    else cache valid
        Cache-->>Client: return cached metrics
    end
Loading
sequenceDiagram
    participant Client
    participant Auth as Auth System
    participant Stripe as Billing Data
    participant DateCalc as Date Logic
    participant Main as get_total_metrics(org_id, start_date, end_date)

    Client->>Auth: invoke get_total_metrics(org_id)
    Auth->>Stripe: fetch anchor_day for org
    Stripe-->>DateCalc: anchor_day
    DateCalc->>Auth: compute v_start_date, v_end_date
    Auth->>Main: call get_total_metrics(org_id, v_start_date, v_end_date)
    Main-->>Client: return metrics
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

💰 Rewarded

Poem

🐰 I hopped through SQL, with a careful twitch,
Seeding caches and guarding the metrics stitch.
I checked each org, each user in line,
Now only trusted paws see usage and time —
A tiny rabbit, securing your sign. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is comprehensive with a detailed summary, motivation, business impact, and test plan. However, the description_template requires a formal checklist section with specific items (code style, documentation, testing coverage) which is not properly completed. Complete the checklist section by marking applicable items with [x] and provide explicit confirmation of manual testing steps, documentation updates if needed, and E2E test coverage status.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: secure get_total_metrics rpc' clearly and concisely summarizes the main change: securing the RPC function by removing unauthorized access.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch riderx/fix-metrics-rpc

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.0.4)
supabase/migrations/20260224093000_fix_get_total_metrics_auth.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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e55d3f4769

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

v_request_user := (SELECT auth.uid());

IF v_request_user IS NULL THEN
IF COALESCE(current_setting('role', true), '') NOT IN ('service_role', 'postgres') THEN

Choose a reason for hiding this comment

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

P1 Badge Detect privileged callers via session_user

Using current_setting('role', true) here does not reliably allow internal postgres/service-role sessions: in regular SQL sessions (including pg
tasks and psql without SET ROLE), that setting is typically 'none', so when auth.uid() is NULL this branch returns early and get_total_metrics yields no rows for callers you intended to preserve. In a SECURITY DEFINER function, use session_user (or JWT role claims) for this bypass check instead.

Useful? React with 👍 / 👎.

Copy link
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: 2

🧹 Nitpick comments (2)
supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql (2)

56-69: pg_stat_xact_user_tables.relname is not schema-qualified.

The relname column doesn't carry schema information. If identically named tables exist in other schemas, this check could trigger false cache refreshes. Consider also filtering on schemaname = 'public' to tighten the match.

Suggested diff
     FROM pg_catalog.pg_stat_xact_user_tables
     WHERE relname IN (
       'apps',
       'deleted_apps',
       'daily_mau',
       'daily_bandwidth',
       'daily_build_time',
       'daily_version',
       'app_versions',
       'app_versions_meta'
     )
+    AND schemaname = 'public'
     AND (n_tup_ins > 0 OR n_tup_upd > 0 OR n_tup_del > 0)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql` around
lines 56 - 69, The WHERE clause currently compares
pg_stat_xact_user_tables.relname against a list of table names (e.g.,
'apps','deleted_apps','daily_mau', etc.) without schema qualification, which can
match same-named tables in other schemas; update the condition on
pg_stat_xact_user_tables in this migration so it also filters on schemaname =
'public' (or the intended schema) to restrict matches to the correct schema and
avoid false cache refreshes, keeping the existing relname IN (...) predicate
intact.

144-157: Duplicate auth check — org_users is queried twice per 1-arg call.

The 1-arg overload performs its own auth/membership check (lines 144–157), then delegates to the 3-arg overload (line 187) which runs the same check again. This is fine as defense-in-depth, but if the double lookup on org_users is a concern, the inner call could be refactored to a shared internal helper that skips the auth gate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql` around
lines 144 - 157, The 1-arg get_total_metrics performs an auth membership check
against public.org_users and then calls the 3-arg overload which repeats the
same check; refactor by introducing a shared internal helper (e.g.
get_total_metrics_internal or add a boolean parameter skip_auth) that implements
the core aggregation logic without re-checking membership, have the 1-arg
overload call that helper after its auth check, and update the 3-arg overload to
call the helper only when it should perform auth (or respect skip_auth) so the
duplicate org_users lookup is avoided while preserving the guarded path.
🤖 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/migrations/20260224093000_fix_get_total_metrics_auth.sql`:
- Around line 159-164: The WHERE clause in the SELECT that sets v_anchor_day
uses an unqualified org_id which may resolve to a column instead of the function
parameter; update the filter to reference the function parameter explicitly
(e.g., use get_total_metrics.org_id) so the condition reads against the function
parameter; ensure this same qualification approach is applied to any other bare
org_id usages in this function and that v_anchor_day and
subscription_anchor_start remain referenced as shown.
- Around line 35-38: The WHERE clause in function get_total_metrics uses an
unqualified parameter org_id (WHERE o.id = org_id); qualify it as
get_total_metrics.org_id to match the existing pattern in this function (see
other references at lines 42, 51, 87) and comply with the coding standard
requiring fully qualified names.

---

Nitpick comments:
In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql`:
- Around line 56-69: The WHERE clause currently compares
pg_stat_xact_user_tables.relname against a list of table names (e.g.,
'apps','deleted_apps','daily_mau', etc.) without schema qualification, which can
match same-named tables in other schemas; update the condition on
pg_stat_xact_user_tables in this migration so it also filters on schemaname =
'public' (or the intended schema) to restrict matches to the correct schema and
avoid false cache refreshes, keeping the existing relname IN (...) predicate
intact.
- Around line 144-157: The 1-arg get_total_metrics performs an auth membership
check against public.org_users and then calls the 3-arg overload which repeats
the same check; refactor by introducing a shared internal helper (e.g.
get_total_metrics_internal or add a boolean parameter skip_auth) that implements
the core aggregation logic without re-checking membership, have the 1-arg
overload call that helper after its auth check, and update the 3-arg overload to
call the helper only when it should perform auth (or respect skip_auth) so the
duplicate org_users lookup is avoided while preserving the guarded path.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38ce641 and e55d3f4.

📒 Files selected for processing (1)
  • supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql

Copy link
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/migrations/20260224093000_fix_get_total_metrics_auth.sql`:
- Line 155: Replace the unqualified parameter org_id in the call to
public.get_total_metrics with the fully qualified parameter name used elsewhere
in this function (get_total_metrics.org_id); update the FROM clause that
currently calls public.get_total_metrics(org_id, v_start_date, v_end_date) to
pass get_total_metrics.org_id instead so it matches other references (e.g.,
get_total_metrics.org_id) and complies with the qualification guideline.
- Line 56: The call to public.seed_org_metrics_cache and subsequent comparisons
use unqualified start_date and end_date while org_id is qualified; update all
references to start_date and end_date in the get_total_metrics function to be
fully qualified as get_total_metrics.start_date and get_total_metrics.end_date
(including the arguments in the call that sets cache_entry and the comparisons
around lines where start_date/end_date are used) so that cache_entry :=
public.seed_org_metrics_cache(get_total_metrics.org_id,
get_total_metrics.start_date, get_total_metrics.end_date) and any comparisons
reference get_total_metrics.start_date/get_total_metrics.end_date consistently.
- Around line 179-195: The v_request_org_id uuid variable is being assigned
current_setting(...) (text) directly and compared to '' which causes invalid
uuid casts; instead introduce a temporary text variable (e.g.,
v_request_org_id_text), SELECT current_setting('request.jwt.claim.org_id', true)
INTO that text variable, test IF v_request_org_id_text IS NULL OR
v_request_org_id_text = '' THEN handle empty (return or fallback to org_users
lookup) ELSE CAST v_request_org_id_text::uuid INTO v_request_org_id; update
references to use v_request_org_id after the safe cast and remove the direct
text-to-uuid assignment/comparison that was causing the type errors.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e55d3f4 and e3b71d2.

📒 Files selected for processing (1)
  • supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql

)
AND (n_tup_ins > 0 OR n_tup_upd > 0 OR n_tup_del > 0)
) THEN
cache_entry := public.seed_org_metrics_cache(get_total_metrics.org_id, start_date, end_date);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Qualify start_date and end_date parameters for consistency.

Parameters start_date and end_date are passed/compared unqualified at lines 56, 75, and 76, while org_id is consistently qualified as get_total_metrics.org_id throughout this same function. Per coding guidelines, all references should be fully qualified.

♻️ Proposed fix
-    cache_entry := public.seed_org_metrics_cache(get_total_metrics.org_id, start_date, end_date);
+    cache_entry := public.seed_org_metrics_cache(get_total_metrics.org_id, get_total_metrics.start_date, get_total_metrics.end_date);
-    AND cache_entry.start_date = start_date
-    AND cache_entry.end_date = end_date
+    AND cache_entry.start_date = get_total_metrics.start_date
+    AND cache_entry.end_date = get_total_metrics.end_date

Also applies to: 75-76

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql` at line
56, The call to public.seed_org_metrics_cache and subsequent comparisons use
unqualified start_date and end_date while org_id is qualified; update all
references to start_date and end_date in the get_total_metrics function to be
fully qualified as get_total_metrics.start_date and get_total_metrics.end_date
(including the arguments in the call that sets cache_entry and the comparisons
around lines where start_date/end_date are used) so that cache_entry :=
public.seed_org_metrics_cache(get_total_metrics.org_id,
get_total_metrics.start_date, get_total_metrics.end_date) and any comparisons
reference get_total_metrics.start_date/get_total_metrics.end_date consistently.

metrics.fail,
metrics.install,
metrics.uninstall
FROM public.get_total_metrics(org_id, v_start_date, v_end_date) AS metrics;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Qualify org_id function argument for consistency.

org_id at line 155 is the function parameter but is referenced unqualified in the delegate call, inconsistent with get_total_metrics.org_id used throughout the rest of the file (including line 132 in this same function). As per coding guidelines, fully qualified names should be used for all references.

♻️ Proposed fix
-  FROM public.get_total_metrics(org_id, v_start_date, v_end_date) AS metrics;
+  FROM public.get_total_metrics(get_total_metrics.org_id, v_start_date, v_end_date) AS metrics;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM public.get_total_metrics(org_id, v_start_date, v_end_date) AS metrics;
FROM public.get_total_metrics(get_total_metrics.org_id, v_start_date, v_end_date) AS metrics;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql` at line
155, Replace the unqualified parameter org_id in the call to
public.get_total_metrics with the fully qualified parameter name used elsewhere
in this function (get_total_metrics.org_id); update the FROM clause that
currently calls public.get_total_metrics(org_id, v_start_date, v_end_date) to
pass get_total_metrics.org_id instead so it matches other references (e.g.,
get_total_metrics.org_id) and complies with the qualification guideline.

Comment on lines +179 to +195
v_request_org_id uuid;
BEGIN
SELECT public.get_identity() INTO v_request_user;

IF v_request_user IS NULL THEN
RETURN;
END IF;

SELECT current_setting('request.jwt.claim.org_id', true) INTO v_request_org_id;

IF v_request_org_id IS NULL OR v_request_org_id = '' THEN
SELECT org_users.org_id
INTO v_request_org_id
FROM public.org_users
WHERE org_users.user_id = v_request_user
ORDER BY org_users.org_id
LIMIT 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

uuid variable receives raw text GUC — throws on empty string; comparison = '' is also a type error.

Two related bugs:

  1. Line 187current_setting(...) returns text. Assigning it directly INTO v_request_org_id uuid performs an implicit text→uuid cast. If PostgREST sets request.jwt.claim.org_id to '' (an empty JWT claim field), PostgreSQL raises invalid input syntax for type uuid: "" — the function 500s instead of returning empty.

  2. Line 189v_request_org_id = '' compares a uuid column to a text empty-string literal. PostgreSQL will attempt to cast '' to uuid, which also fails. In practice this line is never reached with '' because line 187 already threw, but it would need fixing regardless.

The fix is to capture the raw setting into a text variable and only cast to uuid after confirming it is non-empty:

🐛 Proposed fix
 DECLARE
   v_request_user uuid;
   v_request_org_id uuid;
+  v_raw_org_id text;
 BEGIN
   SELECT public.get_identity() INTO v_request_user;

   IF v_request_user IS NULL THEN
     RETURN;
   END IF;

-  SELECT current_setting('request.jwt.claim.org_id', true) INTO v_request_org_id;
-
-  IF v_request_org_id IS NULL OR v_request_org_id = '' THEN
+  v_raw_org_id := current_setting('request.jwt.claim.org_id', true);
+
+  IF v_raw_org_id IS NOT NULL AND v_raw_org_id <> '' THEN
+    BEGIN
+      v_request_org_id := v_raw_org_id::uuid;
+    EXCEPTION WHEN invalid_text_representation THEN
+      v_request_org_id := NULL;
+    END;
+  END IF;
+
+  IF v_request_org_id IS NULL THEN
     SELECT org_users.org_id
     INTO v_request_org_id
     FROM public.org_users
     WHERE org_users.user_id = v_request_user
     ORDER BY org_users.org_id
     LIMIT 1;
   END IF;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260224093000_fix_get_total_metrics_auth.sql` around
lines 179 - 195, The v_request_org_id uuid variable is being assigned
current_setting(...) (text) directly and compared to '' which causes invalid
uuid casts; instead introduce a temporary text variable (e.g.,
v_request_org_id_text), SELECT current_setting('request.jwt.claim.org_id', true)
INTO that text variable, test IF v_request_org_id_text IS NULL OR
v_request_org_id_text = '' THEN handle empty (return or fallback to org_users
lookup) ELSE CAST v_request_org_id_text::uuid INTO v_request_org_id; update
references to use v_request_org_id after the safe cast and remove the direct
text-to-uuid assignment/comparison that was causing the type errors.

@riderx riderx merged commit d29ebf3 into main Feb 25, 2026
11 of 12 checks passed
@riderx riderx deleted the riderx/fix-metrics-rpc branch February 25, 2026 04:03
@sonarqubecloud
Copy link

riderx added a commit that referenced this pull request Feb 25, 2026
* fix(security): revoke anon access to exist_app_v2 rpc

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix(security): revoke PUBLIC execute on exist_app_v2 rpc

* fix(security): revoke anon execute on exist_app_v2 rpc

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(security): revoke anon access to exist_app_v2 rpc

* fix(security): revoke PUBLIC execute on exist_app_v2 rpc

* fix(security): revoke anon execute on exist_app_v2 rpc

* fix(frontend): require confirmation before URL login session (#1688)

* fix(frontend): require confirmation for URL session login

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix(frontend): retain tokens until query login succeeds

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(frontend): require confirmation for URL session login

* fix(frontend): retain tokens until query login succeeds

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* chore(release): 12.116.4

* Riderx/fix email otp rpc reopen (#1693)

* fix(security): restrict email otp verification rpc path

* fix(security): also revoke otp rpc execute from public

* fix(security): record email otp verification via service-side rpc

* fix(security): harden email otp verification RPC usage

* fix(db): drop legacy record_email_otp_verified overload

* fix(frontend): delete replaced profile images from storage (#1683)

* fix(frontend): delete replaced profile images from storage

* fix(backend): clean stale unlinked user avatars

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix: address sonar regex exec suggestions

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(frontend): delete replaced profile images from storage

* fix(backend): clean stale unlinked user avatars

* fix: address sonar regex exec suggestions

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: restrict find_apikey_by_value RPC to service role (#1672)

* fix(security): restrict find_apikey_by_value to service role

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(security): restrict find_apikey_by_value to service role

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: secure get_total_metrics rpc (#1671)

* fix(db): harden get_total_metrics rpc auth

* fix(db): qualify org_id and harden rpc role checks

* fix(db): align get_total_metrics auth overloads

* fix(db): harden get_total_metrics rpc auth

* fix(db): qualify org_id and harden rpc role checks

* fix(db): align get_total_metrics auth overloads

* fix(db): harden get_total_metrics rpc auth

* fix(db): qualify org_id and harden rpc role checks

* fix(db): align get_total_metrics auth overloads

* fix(backend): validate stripe redirect URLs (#1681)

* fix(backend): validate stripe redirect URLs

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* test(backend): add stripe redirect validation tests

* test(backend): fix stripe redirect unit test env setup

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(backend): validate stripe redirect URLs

* test(backend): add stripe redirect validation tests

* test(backend): fix stripe redirect unit test env setup

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* feat(api): auto cleanup EXIF image metadata (#1673)

* feat(api): auto cleanup image metadata on updates

* fix: preserve content type when stripping image metadata

* fix(security): restrict get_orgs_v6(userid uuid) access (#1677)

* fix(security): restrict get_orgs_v6(uuid) execution to private roles

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(security): restrict get_orgs_v6(uuid) execution to private roles

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(security): revoke anon access to apikey oracle RPCs (#1670)

* fix(security): restrict apikey oracle rpc execution

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix: remove anon-backed get_user_id calls in private apikey flows

* fix(database): enforce org-scoped webhook rls (#1676)

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(security): restrict apikey oracle rpc execution

* fix: remove anon-backed get_user_id calls in private apikey flows

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(security): require capgkey auth in exist_app_v2

* fix(api): block scoped apikey key creation (#1685)

* fix(api): block scoped apikeys from creating keys

* fix(build): restore TUS HEAD upload routing (#1664)

* fix(build): handle tus HEAD upload route

* fix(build): chain HEAD middleware correctly

* chore(release): 12.116.2

* fix(security): protect replication endpoint (#1686)

* fix(security): protect replication endpoint

* fix(api): require replication endpoint internal api secret

* fix(api): allow admin JWT access to replication endpoint

* fix(frontend): use admin session only when no replication secret

* fix(database): enforce org-scoped webhook rls (#1676)

* test: fix apikey test lint violations

* fix: use exact app_id match for preview lookup (#1674)

* fix(backend): use exact app_id match for preview lookup

* fix(backend): preserve case-insensitive preview app lookup

* chore(release): 12.116.3

* fix(api): block scoped apikeys from creating keys

* test: fix apikey test lint violations

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: restrict webhook secret access to admin-only (#1692)

* fix(security): restrict webhook secret read access

* fix(rls): restrict webhook reads to admins

* fix(security): keep only apikey-based exist_app_v2 check

* fix(security): require capgkey auth in exist_app_v2

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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