Skip to content

audit: cross-tenant tenancy audit — report, seed regression test, probe invariants - #93

Merged
Bonobo791 merged 4 commits into
mainfrom
audit-cross-tenant
Aug 5, 2026
Merged

Bonobo791 merged 4 commits into
mainfrom
audit-cross-tenant

Conversation

@Bonobo791

@Bonobo791 Bonobo791 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

User description

Summary

Cross-tenant audit triggered by the reported finding: "session renewal/repair UPDATEs without their WHERE (cross-tenant session rewrite)".

Seed verdict: NOT present on main. Both session UPDATEs (session.ts:124,128) carry .where(eq(sessions.id, token)). A two-tenant regression test now proves renewing/repairing user A's session never touches user B's row (passes on current code — documented as the verdict).

Full handler inventory: no breaches found. Every handler boundary scopes by ownedChannel (org match → 404) or membership/role checks; the id-only UPDATEs in queue/log actions are safe because a channel-scoped claim/pair-select precedes them in the same action. Existing tests already cover cross-tenant 404s everywhere. Details and edge notes in docs/tenancy-audit.md.

What ships

  • src/lib/server/session.test.ts — seed regression test for the reported finding.
  • docs/tenancy-audit.md — audit report: tenancy key model, per-surface verdicts, edge notes, runbook, handoff recommendations (FK on comments.channel_id → DB agent; CI wiring → maintainer).
  • scripts/verify-tenancy.mjs — 6 new read-only invariants: sessions never act in an org the user isn't a member of; zero orphaned channel_id rows in comments/moderation_actions/audit_log/rules; zero channels in memberless orgs. Each has a failing behavior test in verify-tenancy.test.mjs.

Verification

  • npm run test — 468/468 green (11 probe tests incl. new ones)
  • npm run check — 0 errors, 0 warnings
  • npm run build — clean
  • Live probe against the dev Turso DB: ALL 15 CHECKS PASSED

CodeAnt-AI Description

Add cross-tenant safety checks and verify session isolation

What Changed

  • Verifies that renewing or repairing one user's session never changes another user's session.
  • Adds read-only checks for sessions using unauthorized organizations, orphaned channel data, and channels in organizations without members.
  • Reports only a limited sample of violations, avoiding unnecessary detail scans when checks pass.
  • Documents the tenancy audit findings, covered access boundaries, known edge cases, and follow-up recommendations.
  • Adds passing and failing tests for the new tenancy checks.

Impact

✅ Prevents cross-tenant session changes
✅ Detects orphaned tenant data
✅ Flags inaccessible organization channels
✅ Faster healthy-database verification

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@cla-bot cla-bot Bot added the cla-signed label Aug 5, 2026
@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed c295a14 Aug 05, 2026 · 13:41 13:44
✅ Reviewed your PR 2e19964 Aug 05, 2026 · 13:27 13:30

@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for moderaty ready!

Name Link
🔨 Latest commit c295a14
🔍 Latest deploy log https://app.netlify.com/projects/moderaty/deploys/6a733d77cce5d600086e4dc0
😎 Deploy Preview https://deploy-preview-93--moderaty.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 89
Accessibility: 97
Best Practices: 100
SEO: 100
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Bonobo791, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 18c6bd70-93a8-401d-bfed-0a258c28a603

📥 Commits

Reviewing files that changed from the base of the PR and between 00ded64 and c295a14.

📒 Files selected for processing (4)
  • docs/tenancy-audit.md
  • scripts/verify-tenancy.mjs
  • scripts/verify-tenancy.test.mjs
  • src/lib/server/session.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 5, 2026
@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR adds a two tenant regression test proving session renewal and organization repair update only the requesting user's session, plus read only database probes that detect invalid tenant state and orphaned channel records.

sequenceDiagram
    participant Test Runner
    participant Session Handler
    participant Database
    participant Tenancy Probe

    Test Runner->>Database: Seed two tenant sessions
    Test Runner->>Session Handler: Renew and repair user A session
    Session Handler->>Database: Update session A by token
    Database-->>Session Handler: Return repaired session
    Test Runner->>Database: Verify session B is unchanged
    Tenancy Probe->>Database: Check session membership invariants
    Tenancy Probe->>Database: Check channel ownership and row references
    Database-->>Tenancy Probe: Return zero violations
Loading

Generated by CodeAnt AI

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: c295a144
Scan Time: 2026-08-05 13:49:02 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@amazon-q-developer amazon-q-developer Bot left a comment

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.

This PR implements a thorough cross-tenant security audit with comprehensive coverage across documentation, runtime verification, and regression tests. The changes successfully address the reported session management concern with a strong verdict (NOT PRESENT on main) backed by executable tests.

Key Strengths:

  • All 6 new tenancy invariants have corresponding behavior tests that verify both pass and fail scenarios
  • The verification script includes proper error containment (broken queries fail loudly rather than aborting)
  • Test coverage includes edge cases like legacy probe IDs, missing tables, and wrong constraint names
  • The new session regression test validates isolation by checking byte-identical state preservation

Verification Confirmed:

  • All 468 tests passing (per PR description)
  • Build and type checks clean
  • Live probe against dev database: ALL 15 CHECKS PASSED

The implementation is merge-ready.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 4 complexity · 0 duplication

Metric Results
Complexity 4 (≤ 100 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Repository owner deleted a comment from codeant-ai Bot Aug 5, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Audit cross-tenant tenancy: report, session regression test, probe invariants

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add two-tenant regression test proving session renewal/repair updates remain token-scoped.
• Extend verify-tenancy probe with cross-tenant invariants and behavior tests.
• Document tenancy key model, handler inventory verdicts, and follow-up recommendations.
Diagram

graph TD
  T1["session.test.ts"] --> S["getSessionUser"] --> DB[("Turso/SQLite DB")]
  S --> O["resolveActiveOrg"]
  T2["verify-tenancy.test.mjs"] --> P["verify-tenancy.mjs"] --> DB
  D["tenancy-audit.md"] -. "spec + runbook" .-> P
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Enforce invariants structurally with DB foreign keys/constraints
  • ➕ Prevents orphaned channel-scoped rows by construction (not just detection).
  • ➕ Moves correctness to the lowest layer; fewer app-level assumptions.
  • ➖ May require migrations and careful handling of existing data.
  • ➖ SQLite/Turso constraint changes can be operationally sensitive (lockstep deploy/migrate).
2. Run the probe continuously (CI + scheduled prod job)
  • ➕ Turns the audit into an ongoing guardrail; detects regressions quickly.
  • ➕ Creates a clear operational signal when invariants break.
  • ➖ Adds operational ownership (alerting, handling transient DB issues).
  • ➖ May require secrets management for prod probe access.
3. Add app-layer tenant scoping helpers for mutations (centralized patterns)
  • ➕ Reduces risk of future id-only updates by making correct scoping the default.
  • ➕ Improves reviewability by standardizing tenancy boundaries.
  • ➖ Requires refactoring many call sites; may be noisy without immediate functional payoff.
  • ➖ Does not prevent out-of-band data corruption (still benefits from probes/constraints).

Recommendation: Keep this PR’s approach (documented audit + executable regression test + read-only invariants probe) as the fastest way to close the reported risk and prevent recurrence. Follow up separately with DB-level foreign keys for channel-scoped tables and a decision on CI/scheduled execution of verify-tenancy, as already recommended in the audit doc.

Files changed (4) +295 / -0

Enhancement (1) +24 / -0
verify-tenancy.mjsAdd cross-tenant invariants to verify-tenancy probe +24/-0

Add cross-tenant invariants to verify-tenancy probe

• Extends the existing read-only tenancy probe with new zero-count checks: session active_org membership validity, orphaned channel-scoped rows across multiple tables, and channels in memberless orgs. The checks are designed to fail loudly with row details while keeping the probe non-mutating.

scripts/verify-tenancy.mjs

Tests (2) +143 / -0
verify-tenancy.test.mjsAdd behavior tests for new cross-tenant probe checks +105/-0

Add behavior tests for new cross-tenant probe checks

• Expands the probe’s behavior-test harness by adding minimal table DDL for sessions and channel-scoped tables. Adds passing and failing test cases that assert the probe exits non-zero and names the correct invariant when violations are seeded.

scripts/verify-tenancy.test.mjs

session.test.tsAdd two-tenant regression test for session renewal/repair scoping +38/-0

Add two-tenant regression test for session renewal/repair scoping

• Adds a targeted regression test that creates two users/sessions and forces both renewal and active-org repair on user A. Asserts user B’s session row remains byte-identical, guarding against any accidental unscoped session UPDATEs.

src/lib/server/session.test.ts

Documentation (1) +128 / -0
tenancy-audit.mdAdd cross-tenant tenancy audit report and runbook +128/-0

Add cross-tenant tenancy audit report and runbook

• Introduces a written audit covering the tenancy key model, handler-by-handler scoping verdicts, and edge-case notes. Documents the seed finding verdict and provides a runbook plus follow-up recommendations (e.g., adding DB FKs, CI wiring).

docs/tenancy-audit.md

@qodo-code-review

qodo-code-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. tenancy-audit.md indented with spaces ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new Markdown file uses leading spaces for indentation in list continuation lines, violating the
repository rule requiring tabs for indentation. This can cause inconsistent formatting and fails the
enforced style requirement.
Code

docs/tenancy-audit.md[R95-98]

+   `log/+page.server.ts:109-112,:136`): the WHERE is `comments.id` only, but
+   each is reachable solely after a channel-scoped claim or pair-verifying
+   select succeeded *in the same action invocation*. A second tenant's
+   comment id fails the claim first (its `channelId` doesn't match) and 404s.
Relevance

●●● Strong

Tab-only indentation is an enforced repo rule; similar tab-indentation fix was previously accepted.

PR-#64

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2401137 requires indentation using tabs only. In docs/tenancy-audit.md, list
continuation lines are indented using spaces (e.g., lines 95-98 begin with spaces), which violates
this requirement.

Rule 2401137: Use tabs for indentation, never spaces
docs/tenancy-audit.md[94-99]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docs/tenancy-audit.md` contains indented lines that start with spaces instead of tabs.

## Issue Context
Compliance rule requires tabs for indentation across changed files.

## Fix Focus Areas
- docs/tenancy-audit.md[94-99]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Probe runs redundant queries ✓ Resolved 🐞 Bug ➹ Performance
Description
verify-tenancy.mjs runs the “detail” SELECT for every expectZero() check even when the count is
zero (PASS), and this PR adds several new checks over potentially large tables (comments,
moderation_actions, audit_log, rules). This can significantly increase probe runtime and DB
read load on healthy databases, and can also emit huge JSON blobs when failures exist.
Code

scripts/verify-tenancy.mjs[R167-170]

+	for (const table of ['comments', 'moderation_actions', 'audit_log', 'rules']) {
+		await expectZero(
+			`zero ${table} with channel_id missing from channels`,
+			`SELECT count(*) AS n FROM ${table} t WHERE NOT EXISTS (SELECT 1 FROM channels c WHERE c.id = t.channel_id)`,
Relevance

●● Moderate

Perf concern seems valid, but no close precedent on skipping detail queries; script changes are
sometimes accepted, sometimes not.

PR-#62
PR-#83

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds new expectZero() calls over four potentially large tables, and the helper’s
implementation unconditionally runs rows(detailSql) regardless of whether the count is zero;
therefore these additions multiply redundant work for healthy databases and can magnify output size
on failures.

scripts/verify-tenancy.mjs[59-66]
scripts/verify-tenancy.mjs[156-178]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`expectZero()` always executes the detail query (`rows(detailSql)`) even when the invariant passes (n === 0). With the newly added cross-tenant checks over large tables, this causes unnecessary work on every probe run and can make the probe slow or noisy.

## Issue Context
The PR adds multiple `expectZero()` calls for large channel-scoped tables (`comments`, `moderation_actions`, `audit_log`, `rules`). Because `expectZero()` currently fetches detail rows unconditionally, a healthy DB run still performs the expensive detail scans.

## Fix Focus Areas
- scripts/verify-tenancy.mjs[59-67]
- scripts/verify-tenancy.mjs[156-178]

## Suggested fix
- Change `expectZero()` to only execute the detail query when `n !== 0` (or when `ok` is false), e.g.:
 - compute `n`
 - if `n === 0`, call `report(label, true, '[]')` (or omit detail)
 - else fetch detail rows (ideally with a `LIMIT`, e.g. 50) and include `n` + sample rows in the detail string.
- Optionally: for very large tables, consider using an existence query first (e.g., `SELECT 1 ... LIMIT 1`) and only count rows when you actually need the exact number.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 78 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread docs/tenancy-audit.md Outdated
Comment thread scripts/verify-tenancy.mjs
@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Aug 5, 2026
@Bonobo791

Copy link
Copy Markdown
Owner Author

Triage of all bot reviews on this PR:

  • @qodo-code-review[bot] — both findings valid, fixed in c295a14 (tab indentation in docs/tenancy-audit.md; expectZero() no longer scans detail rows on PASS, samples capped at 50 on FAIL). Replied inline on both threads.
  • @codacy-production — 0 issues, up to standards. Nothing to fix.
  • @CodeAnt-AI — quality gate PASSED (secrets/SAST/bugs/duplication all clean). Nothing to fix.
  • @sonarqubecloud — quality gate passed, 0 new issues. Nothing to fix.
  • @coderabbitai — rate-limited, no review produced; nothing to act on. Can be re-triggered with @coderabbitai review if wanted.

Post-fix verification: 468/468 tests green, probe behavior tests 11/11, live dev-DB run passes all 15 checks.

@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@Bonobo791 Confirmed. The two valid Qodo findings were fixed in c295a14. The remaining bot checks report no issues. The reported test suite, probe tests, and dev-database verification are green.

You are interacting with an AI system.

@qodo-code-review

Copy link
Copy Markdown

Triage of all bot reviews on this PR:

  • @qodo-code-review[bot] — both findings valid, fixed in c295a14 (tab indentation in docs/tenancy-audit.md; expectZero() no longer scans detail rows on PASS, samples capped at 50 on FAIL). Replied inline on both threads.
  • @codacy-production — 0 issues, up to standards. Nothing to fix.
  • @CodeAnt-AI — quality gate PASSED (secrets/SAST/bugs/duplication all clean). Nothing to fix.
  • @sonarqubecloud — quality gate passed, 0 new issues. Nothing to fix.
  • @coderabbitai — rate-limited, no review produced; nothing to act on. Can be re-triggered with @coderabbitai review if wanted.

Post-fix verification: 468/468 tests green, probe behavior tests 11/11, live dev-DB run passes all 15 checks.

Triage looks complete. Both Qodo findings are implemented: finding 1 (tab indentation) and finding 2 (expectZero() avoids detail scans on PASS and caps failure samples at 50). The other reviewers reported no actionable issues, and the reported verification results—468/468 tests, 11/11 probe behavior tests, and all 15 live checks passing—support merging from a review-quality perspective.

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Question: - @qodo-code-review[bot] — both findings valid, fixed in c295a14 (tab indentation in docs/tenancy-audit.md; expectzero() no longer scans detail rows on pass, samples capped at 50 on fail). replied inline on both threads.

  • @codacy-production — 0 issues, up to standards. nothing to fix.
  • @CodeAnt-AI — quality gate passed (secrets/sast/bugs/duplication all clean). nothing to fix.
  • @sonarqubecloud — quality gate passed, 0 new issues. nothing to fix.
  • @coderabbitai — rate-limited, no review produced; nothing to act on. can be re-triggered with @coderabbitai review if wanted.

post-fix verification: 468/468 tests green, probe behavior tests 11/11, live dev-db run passes all 15 checks.

Answer:
No actionable issues found in the added code.

  • The regression test exercises both session renewal and active-organization repair for user A, and verifies that user B’s session remains unchanged.
  • Both session updates remain correctly scoped with WHERE sessions.id = token.
  • expectZero() now avoids detail scans on passing checks and limits diagnostics to 50 rows on failure.
  • The new probe invariants are covered by passing and failing behavior tests, including orphaned channel-scoped rows, unauthorized session organizations, and memberless organizations.
  • The reported post-fix verification is consistent with the changes: tests, checks, build, and the live probe all pass.

One scope clarification: the new orphan checks verify that rows reference an existing channel, not that every row’s channel belongs to the acting tenant. That ownership guarantee is provided by the handler-level ownedChannel checks documented in the audit, so this is not a defect in this PR.

@Bonobo791
Bonobo791 merged commit f11e540 into main Aug 5, 2026
17 of 20 checks passed
@Bonobo791
Bonobo791 deleted the audit-cross-tenant branch August 5, 2026 13:48
Repository owner deleted a comment from codeant-ai Bot Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant