Server settings → Data protection: staff see every account's contacts and every connection's configuration #369
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#369
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-09-29 in a read-only review of postulo at
95348908d, checking which staff pages read user-owned rows without narrowing them.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atcore/server_views.py:600-638(DataProtectionView.postandRecordOfProcessingView);core/gdpr.py, the two templates and the two tests are where they were.The threat model promises that "administrators see accounts, never anyone's applications or documents" (
docs/THREAT-MODEL.md, the row for another account on the same instance). Two staff-only pages added by #297 break that. The retention dry run lists, by name and company, the contacts of every account on the instance, and the record of processing prints each connection's label and configuration, which for the mail and calendar plugins is the other person's server and login name. Who a person is talking to at which company is the job search itself.core/gdpr.py:348,retention_dry_run:Contact.objects.filter(created_at__lt=older_than)with no owner narrowing, rendered name by name intemplates/server/data_protection.html:55-56.core/gdpr.py:395-404,record_of_processing:Connection.objects.all()with"label"and"config": connection.configfor each, rendered as<code>{{ recipient.config }}</code>intemplates/server/record_of_processing.html:56-57.DataProtectionView.postandRecordOfProcessingViewincore/server_views.py:599-637.postulo-imapkeeps both inconfig; only the password is in the encrypted secrets) and whatever label the member gave it.PersonAdminViewpromotes any member to staff from the People page.jobs/views.py:800) runs on a contact's own page, which is scoped withfor_user()and answers 404 to anyone but the owner.tests/test_gdpr.py:323and:343assert the current behaviour (an admin sees another user's contact name and connection label), so the tests enshrine the leak.for_user(), on a page where they can act on it (the contact list or Settings).Done when
tests/test_gdpr.pylogs in as staff with another user's contact past the retention line and a connection whose config holds a username, and asserts that neither the contact's name, its company, the connection's label nor the username appears on the Data protection dry run or the record of processing; it fails today.