Skip to content

fix(api): enforce org security settings validation#1845

Open
riderx wants to merge 9 commits intomainfrom
fix/ghsa-964v-org-security-validation
Open

fix(api): enforce org security settings validation#1845
riderx wants to merge 9 commits intomainfrom
fix/ghsa-964v-org-security-validation

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Mar 23, 2026

Summary (AI generated)

  • route dashboard organization security setting updates through PUT /organization
  • extend PUT /organization to validate encrypted bundle key updates
  • enforce max_apikey_expiration_days at the database layer to block direct REST/RLS bypasses
  • add regression coverage for the direct org update path and encrypted bundle organization updates

Motivation (AI generated)

The security advisory GHSA-964v-j58v-2q3h showed that an org admin could bypass the validated /organization write path and persist an invalid max_apikey_expiration_days value through direct public.orgs updates. The fix needs to block the bypass on the server side and keep the dashboard aligned with the validated endpoint.

Business Impact (AI generated)

This closes an in-scope security configuration bypass, reduces advisory handling time, and keeps organization security controls consistent across API and dashboard write paths.

Test Plan (AI generated)

  • bun run lint
  • bunx eslint tests/apikeys-expiration.test.ts
  • bunx sqlfluff lint supabase/migrations/20260323210855_enforce_org_apikey_expiration_days_check.sql --dialect postgres
  • bun run supabase:with-env -- bunx vitest run tests/apikeys-expiration.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/organization-api.test.ts
  • bun run lint:backend (currently fails on unrelated supabase/functions/_backend/files/uploadHandler.ts unused import)

Generated with AI

Summary by CodeRabbit

  • New Features

    • Encrypted bundles enforcement with configurable encryption-key requirement.
  • Improvements

    • Validation for encryption key length with clear error responses.
    • API key expiration duration now constrained to 1–365 days.
    • Organization settings updates are more reliable and refresh after changes.
  • Bug Fixes

    • Rejects invalid direct updates to API key expiration days to prevent invalid values.

@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.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@riderx riderx added the codex label Mar 23, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 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: 65623461-5494-424d-96fe-2cb180776e9b

📥 Commits

Reviewing files that changed from the base of the PR and between f8106e2 and e309474.

📒 Files selected for processing (2)
  • supabase/functions/_backend/utils/stats.ts
  • tests/apikeys-expiration.test.ts
✅ Files skipped from review due to trivial changes (1)
  • supabase/functions/_backend/utils/stats.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/apikeys-expiration.test.ts

📝 Walkthrough

Walkthrough

Replaced direct DB updates in the org security settings UI with a shared updateOrganizationSettings helper calling the existing PUT /organization function; added backend handling and validation for encrypted-bundles fields; added DB CHECK for API key expiration days; extended tests for both features.

Changes

Cohort / File(s) Summary
Frontend settings
src/pages/settings/organization/Security.vue
Added updateOrganizationSettings(body) helper, replaced direct supabase.from('orgs').update(...) calls with helper, and added organizationStore.fetchOrganizations() refresh after some updates.
Backend organization API
supabase/functions/_backend/public/organization/put.ts
Added enforce_encrypted_bundles and required_encryption_key request fields; normalize (trim/empty→null) and validate required_encryption_key (must be 21 chars) before persisting; extend update field builder.
Database migration
supabase/migrations/20260323210855_enforce_org_apikey_expiration_days_check.sql
Add/drop-and-recreate CHECK constraint enforcing max_apikey_expiration_days is NULL or between 1 and 365.
Tests
tests/apikeys-expiration.test.ts, tests/organization-api.test.ts
Added REST PATCH test ensuring invalid max_apikey_expiration_days is rejected and unchanged; added PUT /organization tests for encrypted bundles success and invalid key length rejection; minor whitespace normalization in a test.
Minor import cleanup
supabase/functions/_backend/utils/stats.ts
Removed unused supabaseAdmin import.

Sequence Diagram(s)

sequenceDiagram
  participant User as Client (browser)
  participant UI as Frontend UI
  participant Fn as Edge Function /organization (PUT)
  participant DB as Database (orgs)

  User->>UI: Toggle setting (e.g., enforce_encrypted_bundles)
  UI->>UI: build body, call updateOrganizationSettings(body)
  UI->>Fn: PUT /organization { orgId, ...body }
  Fn->>Fn: normalize/validate required_encryption_key
  Fn->>DB: UPDATE orgs SET ... WHERE id=orgId
  DB-->>Fn: success / validation error
  Fn-->>UI: 200 OK or 400 error
  UI->>UI: on success call organizationStore.fetchOrganizations()
  UI-->>User: show result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hopped in to help update the land,

Helpers now tidy with a gentle hand,
Keys trimmed and checked to twenty-one,
Constraints kept tight so rules are done,
Tests hop along — the changes stand.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(api): enforce org security settings validation' directly and clearly summarizes the main change: enforcing security validation for organization settings at the API level.
Description check ✅ Passed The PR description includes a summary, test plan, and key implementation details, but is missing explicit screenshots/videos section and incomplete checklist (one lint command noted as failing).

✏️ 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 fix/ghsa-964v-org-security-validation

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

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/apikeys-expiration.test.ts (1)

429-467: Consider using it.concurrent() for parallel test execution.

Per coding guidelines, all tests should use it.concurrent() instead of it() to run tests in parallel within the same file. However, since this test modifies shared state (updateOrgId) that other sequential tests in this describe block also depend on, and the comment on line 469-470 explicitly orders tests, the sequential execution may be intentional here.

The test logic itself is correct and properly validates the DB CHECK constraint by:

  1. Capturing the value before the direct PATCH attempt
  2. Verifying the REST response fails
  3. Confirming the database value remains unchanged
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/apikeys-expiration.test.ts` around lines 429 - 467, The test "rejects
invalid max expiration days via direct org update RLS path" should be converted
to use it.concurrent() to follow the parallel-test guideline unless this test
must run sequentially due to shared state (updateOrgId) and the explicit
ordering in the describe block; either change the invocation from it(...) to
it.concurrent(...) for this test, or if sequential execution is intentional, add
a brief inline comment above this test referencing updateOrgId and the
describe-level ordering to document why it() is required.
🤖 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/apikeys-expiration.test.ts`:
- Around line 429-467: The test "rejects invalid max expiration days via direct
org update RLS path" should be converted to use it.concurrent() to follow the
parallel-test guideline unless this test must run sequentially due to shared
state (updateOrgId) and the explicit ordering in the describe block; either
change the invocation from it(...) to it.concurrent(...) for this test, or if
sequential execution is intentional, add a brief inline comment above this test
referencing updateOrgId and the describe-level ordering to document why it() is
required.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5093541a-04ba-4c9f-a8cb-b4693ee0bbc7

📥 Commits

Reviewing files that changed from the base of the PR and between 68c6957 and f8106e2.

📒 Files selected for processing (5)
  • src/pages/settings/organization/Security.vue
  • supabase/functions/_backend/public/organization/put.ts
  • supabase/migrations/20260323210855_enforce_org_apikey_expiration_days_check.sql
  • tests/apikeys-expiration.test.ts
  • tests/organization-api.test.ts

@riderx
Copy link
Copy Markdown
Member Author

riderx commented Mar 26, 2026

Pushed a small CI-unblocker commit (e309474):

  • Remove unused supabaseAdmin import from supabase/functions/_backend/utils/stats.ts (was only referenced in commented code).
  • Add a short note above the rejects invalid max expiration days... test explaining why it is intentionally not it.concurrent().

Local verification in the PR worktree:
bun run lint:backend, bun run typecheck, bun run test:all (all passed).

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq bot commented Mar 28, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing fix/ghsa-964v-org-security-validation (b86dc5a) with main (a843519)

Open in CodSpeed

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant