PST-021: Account deletion leaves the person's email address, notes and held import data behind #750
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#750
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 in the security audit of 2026-10-10 (
SECURITY-AUDIT-2026-10-10.md), run againstmainatc30851bfrom 8 to 10 October 2026, as PST-021. Severity: Low.The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.
src/postulo/accounts/deletion.py(deletion.py:91-139)Who can do it, and what they need. No attacker is needed: the data subject who asked for erasure, a regulator, or anybody who later reads the database, an administrator's Invitations page, or a backup. The precondition is simply that the person was invited, or left a spreadsheet/vCard/candidate file waiting for review on a second device, before the account was deleted (by themselves, by an administrator via People > Delete, or by manage.py delete_account).
How it works.
Impact. The 'Your account and everything in it have been deleted' promise is false for the erased person's email address and free-text note (kept in invitation rows visible to every administrator), for anything they had waiting in a session (their own CV/spreadsheet and third parties' contact details from a vCard), and for copies already pushed to external stores (Paperless, WebDAV) because Postulo never calls a store's delete. All of it also rides along in every backup. …
Evidence.
Verification (verdict: confirmed; severity after verification: Low). Keep it low. Cite deletion.py:108-112 and models.py:736/748. The user-visible overclaim is views.py:545 ('everything in it') and server_views.py:331-340, not the delete page's invitation list, which is accurate. The main leftovers are Invite.email and Invite.note for accepted invitations and for invitations created by a deleted administrator. The session leftover is secondary. Self-service deletion does flush the current browser's session, and the stale rows are inert as credentials because _auth_user_id points at a deleted user. They still hold the stashed spreadsheet or vCard, and nothing ever prunes expired session rows (no clearsessions). That is a related retention weakness even without account deletion. Drop the external-store point or mark it informational, since those copies are in services the person controls. The Invitations page is visible only to administrators who typed the address themselves. Remediation: null or delete Invite.email and Invite.note when accepted_by or created_by is erased, or delete accepted invitations bound to the deleted account's address. …
Suggested remediation. In delete_account: delete (or blank email and note on) every Invite whose accepted_by is the user or whose email matches an address the user held, not only pending ones the user created; delete the person's django_session rows (record the user pk in a column or iterate sessions and match _auth_user_id) and clear the discoverable session keys; schedule the cache/session purge; and either call the store plugin's delete for sent DocumentCopy rows or state on the deletion page that external copies stay.
Fixed in
fb5072037on main.