Summary
Repo permissions are keyed by plain username in every sink backend (the users.canPush / users.canAuthorise arrays in mongo/fs, the repo_users.username column in postgres), with no link to the user record itself. deleteUser does not touch these grants, and updateUser renaming a username does not carry them over.
Consequences:
- Grant inheritance: delete user
alice, create a new, unrelated alice later — the new account silently inherits every canPush / canAuthorise grant the old one held.
- Orphaned grants on rename: renaming a username leaves the old name's grants behind, and the renamed user loses their permissions.
Since those arrays gate pushing and approving, grant inheritance is effectively a privilege-escalation-by-username-reuse hole.
Where
src/db/file and src/db/mongo: usernames embedded in each repo document's users arrays; deleteUser only removes the user record.
src/db/postgres: repo_users.username is TEXT with no foreign key to users; deleteUser deletes only from users.
Suggested direction
Fix it uniformly across all three backends so behaviour stays at parity (see .agents/skills/sink-parity/SKILL.md):
deleteUser removes the username from every repo's grants (all backends).
- Username rename (where supported) migrates grants, or is rejected.
- Optionally, postgres adds a real foreign key from
repo_users.username once usernames are stable, making the invariant self-enforcing there.
Context
Raised during review of #1687 (#1687 (comment)). The postgres backend merely normalised the same pre-existing hole the document backends carry, so the fix belongs in a cross-backend change rather than that PR.
Summary
Repo permissions are keyed by plain username in every sink backend (the
users.canPush/users.canAuthorisearrays in mongo/fs, therepo_users.usernamecolumn in postgres), with no link to the user record itself.deleteUserdoes not touch these grants, andupdateUserrenaming a username does not carry them over.Consequences:
alice, create a new, unrelatedalicelater — the new account silently inherits everycanPush/canAuthorisegrant the old one held.Since those arrays gate pushing and approving, grant inheritance is effectively a privilege-escalation-by-username-reuse hole.
Where
src/db/fileandsrc/db/mongo: usernames embedded in each repo document'susersarrays;deleteUseronly removes the user record.src/db/postgres:repo_users.usernameisTEXTwith no foreign key tousers;deleteUserdeletes only fromusers.Suggested direction
Fix it uniformly across all three backends so behaviour stays at parity (see
.agents/skills/sink-parity/SKILL.md):deleteUserremoves the username from every repo's grants (all backends).repo_users.usernameonce usernames are stable, making the invariant self-enforcing there.Context
Raised during review of #1687 (#1687 (comment)). The postgres backend merely normalised the same pre-existing hole the document backends carry, so the fix belongs in a cross-backend change rather than that PR.