Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

test(auth): systemic scan for bespoke isCedarStaff/resolveCallerIsStaff checks

merged#2751CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/staff-check-authority-audit

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 10, 2026, 6:27 AM
  2. Merged
    Sep 10, 2026, 7:21 AM
  3. Live on prod
    Sep 10, 2026, 7:21 AM
  4. Observed 0 hours
    Sep 10, 2026, 7:21 AM
  5. Unobserved

    Live on prod, no production signal yet

    Sep 10, 2026, 7:21 AM
  6. Pipelines steady after this deploy
    Sep 10, 2026, 7:21 AM

Libra has no production signal for this change on prod yet. Libra checks hourly for 3 days after each deploy.

Summary

Follow-up to #2749 (fix(debug): org admin/owner can view a teammate's debug data in their own org).

That PR fixed debugTool.ts's execution-detail/tool-call-detail/recent-executions/health-report actions, which used a bespoke resolveCallerIsStaff() check (self-or-Cedar-staff-only) instead of the codebase's real permission authority, authorize() , missing the org-admin-in-same-org tier DEFAULT_POLICY already declares (subagent_execution:user: 'SELF_OR_ORG_ADMIN').

A fresh grep confirmed analyticsTool.ts is the only other current call site, and it's legitimately, deliberately staff-only by design (cross-account draft-attribution data). But nothing stops a future tool or route from making the identical mistake , reaching for isCedarStaff/resolveCallerIsStaff directly instead of routing through authorize() (via assertActor/assertActorAllowed in tRPC, or resolveTargetUserContext in a Mastra tool).

This PR adds a systemic test modeled closely on the existing role-reads-confined-to-authority.test.ts precedent: it scans all of apps/server/src for resolveCallerIsStaff(/isCedarStaff( calls outside services/auth/, and requires every hit to sit in an explicit ALLOWED_STAFF_CHECK_CALLS allowlist with a kind and a written, verified reason. An undocumented new call site fails the test.

Category taxonomy

Tailored to this check rather than reusing the role-read categories verbatim:

  • STAFF_ONLY_CAPABILITY , a genuine, deliberately staff-only capability outside DEFAULT_POLICY's shape (no organization can hold or grant it) , e.g. analyticsTool.ts, trpc.ts's cedarAdminProcedure, debugTool.ts's remaining 4 calls (all documented as staff-only-by-design in that file's own header comment), staff-only-settings.ts's agentExecutionEnabled gate.
  • CONTEXT , resolves/carries the staff flag for a decision made elsewhere, not itself the gate , e.g. mastra/mcp/external/resolve-session.ts.
  • ADVISORY , explicitly non-authoritative by the file's own comment (UI hygiene, real gate elsewhere) , folded into http/app.ts's combined entry.
  • DECISION , a real access decision made outside authorize(), self-or-Cedar-staff (or same-org-or-staff) shaped with no org-admin tier , the exact shape PR #2749 fixed. 11 files, pinned via EXPECTED_DECISION_ENTRIES: admin-crm-sync.ts, admin.ts, agent-executions.ts, aop.ts (one outlier route), connections.ts, documents.ts, files.ts, integrations.ts, mail.ts, wiki.ts, http/app.ts. None are security holes , self-or-staff is, if anything, too restrictive (same as debugTool's pre-fix behavior) , but they're the same class of gap, on parallel code paths #2749 didn't touch, now tracked instead of silently missed.

What happened to #2749's narrow test

Kept as-is. debugTool.test.ts's rewrite in #2749 is genuine behavioral regression coverage (asserts authorize() is called with the right resource/scope/targetUserId and the verdict is branched on correctly for self/plain-member/org-admin/cross-org/staff scenarios) , something this static allowlist scan cannot provide. No duplication: the new test only proves every staff-check call site is accounted for and reasoned about; the old test proves the fixed call sites actually behave correctly.

Verification

  • pnpm --filter @zero/server exec vitest run src/services/auth/__tests__/staff-check-confined-to-authority.test.ts , 5/5 pass
  • Sibling suites unaffected: role-reads-confined-to-authority.test.ts + debugTool.test.ts , 66/66 pass
  • pnpm --filter @zero/server run types , clean
  • eslint + prettier --check , clean

Test plan

  • New test passes and matches a from-scratch manual grep sweep exactly (18 files, 11 DECISION)
  • No regression in sibling auth tests
  • Scoped typecheck clean
  • Lint/format clean

šŸ¤– Generated with Claude Code

https://claude.ai/code/session_01YZB5RgfxH

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/services/auth/__tests__/staff-check-confined-to-authority.test.tsno production surface mapped