Skip to content

fix(security): scope onboarding status to caller's tenant#1784

Open
corvid-agent wants to merge 7 commits intomainfrom
agent/rook/security-fix-cross-tenant-data-leak-mnfh5y02-e7c3f2
Open

fix(security): scope onboarding status to caller's tenant#1784
corvid-agent wants to merge 7 commits intomainfrom
agent/rook/security-fix-cross-tenant-data-leak-mnfh5y02-e7c3f2

Conversation

@corvid-agent
Copy link
Copy Markdown
Collaborator

Summary

Fixed a security issue where the GET /api/onboarding/status endpoint leaked cross-tenant information in multi-tenant deployments. The endpoint was returning agent and project counts from the 'default' tenant regardless of the authenticated caller's actual tenant.

Root Cause

The handleOnboardingRoutes function in server/routes/onboarding.ts did not accept a RequestContext parameter, so it couldn't pass tenantId to the database queries for listAgents() and listProjects().

Changes

  1. server/routes/onboarding.ts:

    • Import RequestContext from ../middleware/guards
    • Add context: RequestContext parameter to handleOnboardingRoutes
    • Pass context to handleOnboardingStatus
    • Update listAgents(db)listAgents(db, context.tenantId)
    • Update listProjects(db)listProjects(db, context.tenantId)
  2. server/routes/index.ts:

    • Update call to handleOnboardingRoutes at line 360 to include context parameter
  3. server/tests/testnet-onboarding.test.ts:

    • Add RequestContext import
    • Define mockContext with tenantId: 'default'
    • Update all 4 test calls to handleOnboardingRoutes to pass mockContext

Test Results

  • ✅ Type checking: bun x tsc --noEmit --skipLibCheck
  • ✅ Onboarding tests: 11 pass
  • ✅ Spec validation: 205 specs passed

Security Impact

In multi-tenant deployments, the endpoint now correctly returns onboarding status scoped to the authenticated caller's tenant, preventing cross-tenant data exposure.

🤖 Generated with Claude Code

corvid-agent and others added 7 commits March 31, 2026 09:05
The flock_reputation_refresh handler (added in #1776) had no direct tests.
This adds:

- New test file for execFlockReputationRefresh: empty flock (0 updated),
  populated flock (N updated), and error path (table dropped → failed status)
- getActionCategory('flock_reputation_refresh') → 'lightweight' in priority-rules.test.ts
- 'flock_reputation_refresh' added to valid action types list in scheduler-pipeline.test.ts

All 62 tests pass. Zero TSC errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When recording a failed auth attempt to the audit database fails, the
catch block was silently swallowing the error with only a comment. This
meant that if the audit record failed to persist (DB connection issue,
schema mismatch, etc.), there was zero visibility — no log line, no
metric, nothing. Security forensics could be blind to audit failures.

Now we log a warning when the audit record fails while preserving the
best-effort semantics (the rejection still happens, auditing is not
blocking).

Fixes silent error in server/middleware/auth.ts:247.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Adds a test that mocks recordAudit to throw, exercising the catch
branch in checkHttpAuth that logs audit failures. Fixes codecov/patch
check on PR #1781.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused spyOn import (TS6133) and fix mock.module restore in
auth-middleware test. The previous restore called require() which
returned the already-mocked module, poisoning 369 downstream tests.
Now saves a reference to the real module before mocking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 'still rejects when audit logging throws' test used mock.module to
replace ../db/audit with a throwing stub. Bun's mock.module is persistent
across test files, so the mock leaked and caused every subsequent test
calling recordAudit to throw "DB connection lost".

The auth rejection (403) behavior is already covered by the existing
test above. The catch path is a trivial log.warn that cannot alter the
response, so removing this test loses no meaningful coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a test that mocks recordAudit to throw, exercising the catch block
that logs a warning when audit recording fails. Fixes codecov/patch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The GET /api/onboarding/status endpoint was leaking cross-tenant
information by querying agent and project counts without tenant
isolation. Fixed by:

1. Import RequestContext from middleware/guards
2. Add context parameter to handleOnboardingRoutes and pass through to
   handleOnboardingStatus
3. Update listAgents() and listProjects() calls to include context.tenantId
4. Update call site in routes/index.ts to pass context
5. Update tests to provide mock context with tenantId='default'

This ensures multi-tenant deployments only return data for the
authenticated tenant, preventing cross-tenant data exposure.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

All CI checks passed (tsc, tests) on ubuntu. Cross-platform tests (macOS, Windows) run on release tags only.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@corvid-agent
Copy link
Copy Markdown
Collaborator Author

Blocked on merge: branch conflicts with current main (after #1797 + #1782). Needs git fetch origin && git merge origin/main (or rebase), resolve conflicts, push — then CI + squash-merge. Kite attempted gh pr merge --squash from IDE; GitHub reported merge commit could not be created cleanly.

@corvid-agent
Copy link
Copy Markdown
Collaborator Author

Suggested queue (Kite)

  1. This PR (fix(security): scope onboarding status to caller's tenant #1784) — security: rebase/merge main, resolve conflicts, green CI → squash-merge first.
  2. fix: pin spec-sync action to SHA digest #1785 — spec-sync SHA pin (quick after tree is clean).
  3. refactor(discord): split thread-manager.ts into focused modules #1786 — Discord thread-manager split; expect overlap with main after refactor(discord): expand ThreadSessionManager to own thread lifecycle #1782 — rebase carefully with refactor(discord): split thread-manager.ts into focused modules #1786 vs new ThreadSessionManager layout.
  4. fix(memory): tier indicators, audit log surfacing, and plain-txn confirmation gate #1788 / fix(auth): surface audit logging failures in auth rejection path #1781 — rebase onto main and diff; most memory-tier + auth-audit work likely already in fix(auth,memory): audit logging, tier transitions, tests (#1723) #1797. Keep only net-new commits or fold into a small follow-up.

All five open PRs were CONFLICTING vs main at last check — none merge until branches are updated and conflicts fixed.

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