Skip to content

[codex] Fix statistics app existence oracle#2026

Merged
riderx merged 4 commits into
mainfrom
codex/fix-statistics-app-oracle
May 5, 2026
Merged

[codex] Fix statistics app existence oracle#2026
riderx merged 4 commits into
mainfrom
codex/fix-statistics-app-oracle

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented May 4, 2026

Summary (AI generated)

  • Added explicit limited API key app/org scope denial before shared RBAC permission checks.
  • Normalized API-key missing-app statistics responses to 401 no_access_to_app.
  • Added a regression test covering real sibling apps and fake app IDs with the same app-limited subkey.

Motivation (AI generated)

GHSA-73p9-mprg-7r75 reported that app-limited API keys could distinguish real sibling app IDs from nonexistent app IDs through different /statistics/app/:app_id responses.

Business Impact (AI generated)

This preserves tenant isolation for app-scoped keys and removes an app existence oracle that could support enumeration or follow-on targeting.

Test Plan (AI generated)

  • bun run lint:backend
  • bunx eslint tests/statistics.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/statistics.test.ts tests/statistics-retries.unit.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/rbac-permissions.test.ts
  • Commit hook typecheck path: bun run cli:build && vue-tsc --noEmit

Generated with AI

Summary by CodeRabbit

  • Security
    • Enhanced API key validation to enforce scope restrictions on limited keys accessing apps and organizations
    • Improved authorization error responses to correctly distinguish between unauthorized access and missing resources

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 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: 062bc10b-4d41-461d-930a-9a5fac1c9275

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee852b and 5639095.

📒 Files selected for processing (1)
  • supabase/functions/_backend/public/statistics/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • supabase/functions/_backend/public/statistics/index.ts

📝 Walkthrough

Walkthrough

These changes implement API key scope restrictions to prevent limited API keys from accessing apps or organizations outside their allow-lists. A new RBAC helper enforces scope constraints, the statistics endpoint explicitly denies out-of-scope requests with 401 errors, and tests verify the enforcement for both real and non-existent sibling apps.

Changes

API Key Scope Restriction Implementation

Layer / File(s) Summary
RBAC Scope Validation
supabase/functions/_backend/utils/rbac.ts
New internal helper deniesExplicitApiKeyScope checks if an explicit API key request falls outside limited_to_apps and limited_to_orgs allow-lists; checkPermission calls this early and denies matching requests.
Statistics Endpoint Integration
supabase/functions/_backend/public/statistics/index.ts
getStatsAppOwnerOrgOrThrow returns 401 (no_access_to_app) instead of 404 when an API key authenticated request cannot find the requested app.
Test Coverage & Verification
tests/statistics.test.ts
New helpers create and delete sibling apps; concurrent test verifies that a limited subkey is denied with 401 when accessing both real sibling apps and non-existent apps with the same insufficient scope.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Cap-go/capgo#1997: Both PRs implement stronger app/org ownership checks and API-key scope restrictions (returning 401 on org/app mismatches).
  • Cap-go/capgo#2014: Both PRs add early-deny for app-limited API keys in statistics endpoints, returning 401 to prevent scoped keys from leaking sibling/nonexistent app existence.
  • Cap-go/capgo#1951: Both PRs address the same API-key scoping bypass via explicit JS-level deny and 401 path for apikey-scoped access.

Poem

🐰 A rabbit hops through scopes so tight,
No key shall stray beyond its right,
Sibling apps are hidden well,
Four-oh-one the truth shall tell! 🔐

🚥 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 clearly summarizes the main change: fixing a security issue where app-limited API keys could distinguish real sibling app IDs from nonexistent ones via the statistics endpoint.
Description check ✅ Passed The description includes a comprehensive summary of changes, motivation tied to a specific security issue (GHSA-73p9-mprg-7r75), business impact, and detailed test plan with all verification steps.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-statistics-app-oracle

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

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 4, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing codex/fix-statistics-app-oracle (5639095) with main (272b4f5)

Open in CodSpeed

@riderx riderx marked this pull request as ready for review May 4, 2026 17:08
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

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.

🧹 Nitpick comments (1)
tests/statistics.test.ts (1)

169-201: ⚡ Quick win

Make this regression test self-contained (remove cross-test state dependency)

At Line 170, this test relies on subkeyId being created by a previous test, which makes it order-dependent and not parallel-safe. Create a dedicated limited subkey inside this test and clean it up in finally (then it can be it.concurrent).

Proposed refactor sketch
-  it('should not reveal sibling app existence outside an app-limited subkey', async () => {
-    expect(subkeyId).not.toBe(0)
+  it.concurrent('should not reveal sibling app existence outside an app-limited subkey', async () => {
+    const createSubkey = await fetch(`${BASE_URL}/apikey`, {
+      method: 'POST',
+      headers: headersStats,
+      body: JSON.stringify({
+        name: 'Limited Stats Subkey - oracle test',
+        mode: 'read',
+        limited_to_apps: [APPNAME],
+      }),
+    })
+    expect(createSubkey.status).toBe(200)
+    const localSubkey = await createSubkey.json() as { id: number }
+
     const siblingApp = `com.stats.oracle.${randomUUID().replaceAll('-', '')}`
     const fakeApp = `com.stats.fake.${randomUUID().replaceAll('-', '')}`
     await createStatsSiblingApp(siblingApp)

     try {
-      const subkeyHeaders = { 'x-limited-key-id': String(subkeyId) }
+      const subkeyHeaders = { 'x-limited-key-id': String(localSubkey.id) }
       ...
     }
     finally {
       await deleteAppByAppId(siblingApp)
+      await deleteApikeyById(localSubkey.id)
     }
   })

As per coding guidelines tests/**/*.test.ts: “Design all tests for parallel execution across files; use it.concurrent() instead of it() to maximize parallelism within test files; create dedicated seed data for tests that modify resources or when resource state matters for assertions.”

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

In `@tests/statistics.test.ts` around lines 169 - 201, The test depends on
external shared state (subkeyId) so make it self-contained: create a dedicated
limited subkey at the start of this test (e.g., call the same helper used
elsewhere to create subkeys and capture its id into a local variable rather than
using the global subkeyId), use that local id to build subkeyHeaders and the
merged headers (replace use of subkeyId and headersStats with the local
variables), mark the test it.concurrent(...) and ensure you delete the created
subkey in the finally block along with the existing deleteAppByAppId(siblingApp)
cleanup; keep the rest of the flow (createStatsSiblingApp, fetch to
BASE_URL/statistics/app/..., assertions) unchanged so the test is independent
and parallel-safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/statistics.test.ts`:
- Around line 169-201: The test depends on external shared state (subkeyId) so
make it self-contained: create a dedicated limited subkey at the start of this
test (e.g., call the same helper used elsewhere to create subkeys and capture
its id into a local variable rather than using the global subkeyId), use that
local id to build subkeyHeaders and the merged headers (replace use of subkeyId
and headersStats with the local variables), mark the test it.concurrent(...) and
ensure you delete the created subkey in the finally block along with the
existing deleteAppByAppId(siblingApp) cleanup; keep the rest of the flow
(createStatsSiblingApp, fetch to BASE_URL/statistics/app/..., assertions)
unchanged so the test is independent and parallel-safe.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ef14254-981c-4803-86b3-e0001f6f0c96

📥 Commits

Reviewing files that changed from the base of the PR and between 21c4c38 and 0752cd7.

📒 Files selected for processing (3)
  • supabase/functions/_backend/public/statistics/index.ts
  • supabase/functions/_backend/utils/rbac.ts
  • tests/statistics.test.ts

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

@riderx riderx merged commit c226217 into main May 5, 2026
38 checks passed
@riderx riderx deleted the codex/fix-statistics-app-oracle branch May 5, 2026 16:32
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