Skip to content

[codex] Fix statistics app existence oracle#2026

Open
riderx wants to merge 2 commits intomainfrom
codex/fix-statistics-app-oracle
Open

[codex] Fix statistics app existence oracle#2026
riderx wants to merge 2 commits intomainfrom
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

  • Bug Fixes

    • Tightened API-key access checks to prevent use of keys outside their allowed app/org scope.
    • Improved error responses to return explicit "no access to app" for API-key requests when appropriate.
  • Tests

    • Added tests validating API-key scope restrictions across sibling and non-existent app scenarios.

@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: c56f3f0a-f8ea-45f5-8936-f6916f63815f

📥 Commits

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

📒 Files selected for processing (1)
  • tests/statistics.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/statistics.test.ts

📝 Walkthrough

Walkthrough

The PR enforces API-key allow-lists for app/org scope checks and makes the statistics endpoint return 401 (no_access_to_app) for apikey-authenticated requests that target apps outside the key's scope instead of 404. Tests were added to verify app-limited subkey behavior.

Changes

API Key Scope Enforcement

Layer / File(s) Summary
RBAC Scope Data & Guard
supabase/functions/_backend/utils/rbac.ts
Added deniesExplicitApiKeyScope(c, scope) which inspects auth.authType === 'apikey' and enforces limited_to_apps / limited_to_orgs allow-lists; logs denials and returns true to block when out-of-scope.
Permission Check Integration
supabase/functions/_backend/utils/rbac.ts
checkPermission now calls the new guard (if (deniesExplicitApiKeyScope(c, scope)) return false) immediately after verifying auth?.userId.
Endpoint Behavior Change
supabase/functions/_backend/public/statistics/index.ts
getStatsAppOwnerOrgOrThrow now throws quickError(401, 'no_access_to_app', ...) when resolveAppOwnerOrg returns notFound for an auth.authType === 'apikey' request, instead of falling through to a 404.
Tests / Fixtures
tests/statistics.test.ts
Added helpers to create/delete a sibling app and a new concurrent test that creates an app-limited read-only subkey, calls /statistics/app/:appId for a real sibling and a fake app, asserts both return 401/no_access_to_app, and cleans up created resources.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant StatsFn as Statistics Function
participant RBAC as RBAC Utils
participant DB as Database

Client->>StatsFn: GET /statistics/app/:appId (apikey headers)
StatsFn->>RBAC: resolveAppOwnerOrg(appId)
RBAC->>DB: query app owner/org
DB-->>RBAC: app not found
RBAC-->>StatsFn: notFound
StatsFn->>RBAC: deniesExplicitApiKeyScope(scope)
RBAC-->>StatsFn: denies (limited_to_apps/orgs mismatch)
StatsFn-->>Client: 401 no_access_to_app
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I dug a tunnel, soft and neat,
Found keys that wandered off the beat.
Now gates are set, the tunnels tight,
No sibling peeks in darkest night.
Hooray — access rules hop right! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 directly matches the main objective: fixing the statistics app existence oracle vulnerability in API key scope handling.
Description check ✅ Passed The PR description provides summary, motivation, business impact, and detailed test plan; all major template sections are adequately covered.
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

Review rate limit: 0/5 reviews remaining, refill in 59 minutes and 31 seconds.

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 (7ee852b) with main (c1f615d)

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 4, 2026

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