SEC2-003: An administrator can join any group unnoticed: members cannot see who is in their group, and the log does not name the change #945
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#945
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-10-16 in a read-only security review of
postuloat56fc9d531, checked by a second, independent pass.Severity: Low · Category: Authentication/Authorization · Reference: CWE-778 (Insufficient Logging); CWE-306 (Missing Authentication for Critical Function); OWASP A09:2021 (Security Logging and Monitoring Failures)
Location:
src/postulo/core/views_access.py:84-103(GroupsView.post, actionsave)src/postulo/accounts/views.py:670-693(YourGroupsView)src/postulo/templates/accounts/groups.html:22-25(the member count)Issue
Group membership is the authorisation primitive of #896: every member of a group may see, change, share and delete what the group owns and everything shared with the group (
core/sharing.py:59-71). Server settings → Groups lets any staff account rewrite any group's member list, themselves included:Three things make this quieter than the threat model's promise that a grant is "explicitly, for a stated scope, revocably, and visibly to them at any time":
members = group.user_set.count(), drawn as "N members"), so the people whose records are reached have no page on which the new member appears;PersonAdminView), although it confers read/write/manage over records.The threat model does say an administrator is trusted with the instance, and Server settings → People already shows every account; this is about the trace rather than the capability.
Impact
A staff administrator who is not the operator, or whoever holds such a session, can reach every group-owned company, contact and listing (and what is shared with the group) without the group's members being able to notice from their own pages, and with no log entry that says so afterwards. Notes, applications and documents stay out of reach, as promised.
Fix
before = set(group.user_set.values_list("pk", flat=True))and write theadded/removedusernames into the warning line (usernames, never addresses).User's docstring), so a new member is visible to every other one.GroupsView.postwithreauthentication_required, for consistency withPersonAdminView.notifications.service.notify) when someone joins.Done when a test posts a membership change and asserts the log line names the added and removed usernames, and a unit or browser test asserts
accounts:groupslists the members of the viewer's group.Related
#896 (closed) - introduced the page and promises visibility of grants; member visibility and the audit line are not discussed. #740 (closed) - re-authentication of administrative actions; groups did not exist then. #95 (closed) - "an administrator can decide a plugin for a person — and must not be able to decide it quietly" is the same principle applied to plugin policy, which is shown on the person's own settings page; group membership has no such mirror. #268 (open) - background: its Part 3 says "An administrator who could grant themselves access is the whole promise gone", which #896's design and the updated threat model superseded for companies, contacts and listings.