Recorded while implementing #10231. Not fixed there — that card is fenced to plugin-sharing, and collapsing the two would be a cross-package dependency decision rather than a mechanical repair.
What was measured
After #10231 lands, the manager-chain column sys_user.manager_id has two organization screens, implemented separately:
| package |
entry point |
screen |
plugin-approvals |
ApprovalService.lookupManager |
managerIsProvablyOutsideOrg (private method) |
plugin-sharing |
TeamGraphService.managerOf, BusinessUnitGraphService.managerOf |
managerIsProvablyOutsideOrg (module function) |
Both landed deliberately and both are correct today — the plugin-sharing one was written to mirror the plugin-approvals one (13f533a43a, #10153) precisely because a screen that differed between them would route an approval one way and share a record the other.
The observation is that nothing enforces that mirroring. They are byte-similar, share no code, and have no cross-referencing test. The two share:
- the same fail-open posture on an absent tenancy fact (no
sys_member rows, or the membership read threw);
- the same "provably outside" shape, rather than "must prove membership";
- the same reason for reading
sys_member at all — sys_user is the global better-auth identity table and carries no organization_id, so a membership row is the only tenancy fact that exists for a user.
Each of those three is a posture decision that a future edit could change on one side alone, silently.
Why this is a finding and not a patch
The obvious repair — one shared helper — is not obviously right:
A shared conformance fixture is probably the cheapest option that catches divergence without deciding #7497, but that is a judgement call, not a mechanical one.
Related
Filed unassigned, no pm:queue.
Generated by Claude Code
Recorded while implementing #10231. Not fixed there — that card is fenced to
plugin-sharing, and collapsing the two would be a cross-package dependency decision rather than a mechanical repair.What was measured
After #10231 lands, the manager-chain column
sys_user.manager_idhas two organization screens, implemented separately:plugin-approvalsApprovalService.lookupManagermanagerIsProvablyOutsideOrg(private method)plugin-sharingTeamGraphService.managerOf,BusinessUnitGraphService.managerOfmanagerIsProvablyOutsideOrg(module function)Both landed deliberately and both are correct today — the
plugin-sharingone was written to mirror theplugin-approvalsone (13f533a43a, #10153) precisely because a screen that differed between them would route an approval one way and share a record the other.The observation is that nothing enforces that mirroring. They are byte-similar, share no code, and have no cross-referencing test. The two share:
sys_memberrows, or the membership read threw);sys_memberat all —sys_useris the global better-auth identity table and carries noorganization_id, so a membership row is the only tenancy fact that exists for a user.Each of those three is a posture decision that a future edit could change on one side alone, silently.
Why this is a finding and not a patch
The obvious repair — one shared helper — is not obviously right:
plugin-approvalsalready depends onplugin-sharing, so the direction exists; but hoisting a security screen into another plugin's public API surface is a contract decision, and [finding] plugin-sharing:TeamGraphService.managerOfdiscards itsorganizationIdargument — the manager-chain screen cited as this repo's governed side is not in this repo #10231 deliberately did not re-export its copy from the package index.packages/specas a declared obligation, or a conformance test that runs one fixture set against both) are different bets with different costs.A shared conformance fixture is probably the cheapest option that catches divergence without deciding #7497, but that is a judgement call, not a mechanical one.
Related
TeamGraphService.managerOfdiscards itsorganizationIdargument — the manager-chain screen cited as this repo's governed side is not in this repo #10231 — theplugin-sharingscreenmanagerapprover resolvessys_user.manager_idwith no organization screen, while every sibling approver expansion is org-scoped #10153 — theplugin-approvalsscreen this one mirrorsFiled unassigned, no
pm:queue.Generated by Claude Code