Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
-- Warn users on @capgo/cli versions older than 7.107.0 when their API key
-- would hit the appid-passthrough RBAC bug fixed by PR #2282.
--
-- The bug: is_allowed_action_org_action(orgid, actions) calls check_min_rights
-- with app_id = NULL. For an RBAC-managed key (mode IS NULL) with
-- limited_to_apps set, on an org with use_new_rbac = true,
-- rbac_check_permission_direct denies the call because the key is restricted
-- to apps but no app context was provided. The CLI surfaces this as the
-- misleading "Plan upgrade required for upload" error.
--
-- The fix shipped in @capgo/cli@7.107.0. For users still running older CLIs,
-- this warning fires fatally during checkRemoteCliMessages (which runs BEFORE
-- checkPlanValidUpload), replacing the misleading billing error with an
-- actionable one that explains the bug, the upgrade target, and the
-- workaround.
--
-- Scope: RBAC v2 keys only (mode IS NULL with role_bindings). Legacy keys
-- (mode IN ('read','write','upload','all')) with limited_to_apps would also
-- hit the bug on use_new_rbac orgs, but the user-requested scope is RBAC v2.

CREATE OR REPLACE FUNCTION public.get_organization_cli_warnings(
orgid uuid,
cli_version text
) RETURNS jsonb []
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
messages jsonb[] := ARRAY[]::jsonb[];
request_apikey text;
api_key public.apikeys%ROWTYPE;
fallback_app_id text;
has_org_read boolean;
org_uses_new_rbac boolean;
cli_version_match text[];
cli_version_parts int[];
-- Lowest @capgo/cli release that contains the PR #2282 fix.
fix_cli_version constant int[] := ARRAY[7, 107, 0];
BEGIN
PERFORM cli_version;

has_org_read := public.cli_check_permission(
permission_key := public.rbac_perm_org_read(),
org_id := orgid
);

SELECT public.get_apikey_header() INTO request_apikey;

IF request_apikey IS NOT NULL AND request_apikey <> '' THEN
SELECT * INTO api_key
FROM public.find_apikey_by_value(request_apikey)
LIMIT 1;
END IF;

IF NOT has_org_read THEN
IF api_key.id IS NOT NULL
AND COALESCE(array_length(api_key.limited_to_apps, 1), 0) > 0
THEN
SELECT public.apps.app_id INTO fallback_app_id
FROM public.apps
WHERE public.apps.owner_org = orgid
AND public.apps.app_id = ANY(api_key.limited_to_apps)
ORDER BY public.apps.app_id
LIMIT 1;

IF fallback_app_id IS NOT NULL THEN
has_org_read := public.cli_check_permission(
permission_key := public.rbac_perm_org_read(),
org_id := orgid,
app_id := fallback_app_id
);
END IF;
END IF;
END IF;

IF NOT has_org_read THEN
messages := array_append(messages, jsonb_build_object(
'message', 'API key does not have read access to this organization',

Check failure on line 79 in supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 3 times.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ5DmeOpLyiN3yWp5Lh0&open=AZ5DmeOpLyiN3yWp5Lh0&pullRequest=2302
'fatal', true

Check failure on line 80 in supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 3 times.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ5DmeOpLyiN3yWp5Lhz&open=AZ5DmeOpLyiN3yWp5Lhz&pullRequest=2302
));
RETURN messages;
END IF;

IF (
public.is_paying_and_good_plan_org_action(orgid, ARRAY['mau']::public.action_type[]) = true
AND public.is_paying_and_good_plan_org_action(orgid, ARRAY['bandwidth']::public.action_type[]) = true
AND public.is_paying_and_good_plan_org_action(orgid, ARRAY['storage']::public.action_type[]) = false
) THEN
messages := array_append(messages, jsonb_build_object(
'message', 'You have exceeded your storage limit.\nUpload will fail, but you can still download your data.\nMAU and bandwidth limits are not exceeded.\nIn order to upload your plan, please upgrade your plan here: https://console.capgo.app/settings/plans.',
'fatal', true
));
END IF;

-- PR #2282 warning. Triggers only when ALL of the following are true:
-- 1. Caller's API key is RBAC v2 (mode IS NULL).
-- 2. Key has limited_to_apps set (the gate that trips inside RBAC).
-- 3. Org has use_new_rbac = true (the gate routes through
-- rbac_check_permission_direct only when RBAC is enabled).
-- 4. CLI version parses cleanly and sits below 7.107.0.
IF api_key.id IS NOT NULL
AND api_key.mode IS NULL
AND COALESCE(array_length(api_key.limited_to_apps, 1), 0) > 0
THEN
SELECT COALESCE(o.use_new_rbac, false) INTO org_uses_new_rbac
FROM public.orgs o
WHERE o.id = orgid;

IF COALESCE(org_uses_new_rbac, false) THEN
-- Parse leading X.Y.Z. Unparseable versions (empty string, "dev",

Check warning on line 111 in supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql

View workflow job for this annotation

GitHub Actions / Run tests

"Unparseable" should be "Unparsable".

Check warning on line 111 in supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql

View workflow job for this annotation

GitHub Actions / Run tests

"Unparseable" should be "Unparsable".
-- "next") fall through without firing the warning - safer to be
-- silent than to nag on non-release builds.
Comment on lines +111 to +113
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Minor typo: "Unparseable" → "Unparsable".

Static analysis flagged the spelling. The standard form is "unparsable".

📝 Proposed fix
-            -- Parse leading X.Y.Z. Unparseable versions (empty string, "dev",
+            -- Parse leading X.Y.Z. Unparsable versions (empty string, "dev",
📝 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
-- Parse leading X.Y.Z. Unparseable versions (empty string, "dev",
-- "next") fall through without firing the warning - safer to be
-- silent than to nag on non-release builds.
-- Parse leading X.Y.Z. Unparsable versions (empty string, "dev",
-- "next") fall through without firing the warning - safer to be
-- silent than to nag on non-release builds.
🧰 Tools
🪛 GitHub Check: Run tests

[warning] 111-111:
"Unparseable" should be "Unparsable".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/migrations/20260520040202_cli_warning_rbac_appid_bug.sql` around
lines 111 - 113, Update the comment text in the migration SQL where the phrase
"Unparseable versions (empty string, "dev","next")" appears: change the word
"Unparseable" to the standard spelling "Unparsable" so the comment reads
"Unparsable versions (empty string, "dev", "next")". Locate and edit that
comment block (the multi-line comment containing "Parse leading X.Y.Z.") to
apply the correction.

cli_version_match := regexp_match(cli_version, '^([0-9]+)\.([0-9]+)\.([0-9]+)');
IF cli_version_match IS NOT NULL THEN
cli_version_parts := ARRAY[
cli_version_match[1]::int,
cli_version_match[2]::int,
cli_version_match[3]::int
];

IF cli_version_parts < fix_cli_version THEN
messages := array_append(messages, jsonb_build_object(
'message',
'Your CLI version (' || cli_version || ') has a known bug affecting RBAC-managed API keys restricted to specific apps.\n' ||
'Uploads with this key fail with "Plan upgrade required for upload" even when your plan is healthy.\n' ||
'Fix: upgrade to @capgo/cli@7.107.0 or newer:\n' ||
' npm i -g @capgo/cli@latest\n' ||
'Workaround if you cannot upgrade: remove the app restriction (limited_to_apps) on this API key, leaving only the org restriction. See https://github.com/Cap-go/capgo/pull/2282 for context.',
'fatal', true
));
END IF;
END IF;
END IF;
END IF;

RETURN messages;
END;
$$;

ALTER FUNCTION public.get_organization_cli_warnings(uuid, text)
OWNER TO postgres;

REVOKE ALL ON FUNCTION public.get_organization_cli_warnings(uuid, text)
FROM public;
GRANT EXECUTE ON FUNCTION public.get_organization_cli_warnings(uuid, text)
TO anon;
GRANT EXECUTE ON FUNCTION public.get_organization_cli_warnings(uuid, text)
TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_organization_cli_warnings(uuid, text)
TO service_role;
Loading
Loading