Gate the approval half of hr-summary on FINALIZE_PAY_PERIODS

FixSecurityService
Shipped
August 12, 2026 at 5:52 PM UTC
Author
Kamo
Commit
8eb9f78

Pointing the composer at close-readiness widened privilege. That relay's own mapping guards with FINALIZE_PAY_PERIODS, and resolveRole does not admit MANAGE_TIMECARDS to approval authority at all — not as PAYROLL, not as MANAGER. So a MANAGE_TIMECARDS-only holder, refused both the approve action and the direct read, would have learned the org-wide awaiting-approval count and every member's timecardState through a composite. Raising the whole endpoint to require both rights would be the wrong fix: it would break the deliberate parity between this gate and the /hr/attendance tab gate in hrTabs.ts and 403 a member the UI offered the tab to. Instead the approval half carries a second, narrower gate, in the same shape DocsService uses for a block its caller cannot reach — null, never 403. The controller derives the right from the session and passes a plain boolean; the composer still never sees the request. Without it the relay is NOT ISSUED — computing it and discarding the result would still have read data this caller has no right to. awaitingApproval comes back null rather than 0, no member carries a timecardState, and attention[] holds no APPROVAL rows. null is "not available to you"; 0 is "nothing is waiting", and showing the first as the second would tell an HR admin a queue they cannot see is clear. Everything MANAGE_TIMECARDS is entitled to — hours, on-the-clock, exceptions, tracked headcount, current period — is untouched, and asserted so. Tests assert the NON-INVOCATION of the relay, not merely the output shape.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing