PST-024: A Web Push subscription outlives sign-out, so reminders keep arriving on a shared browser #753
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#753
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-024. 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/static/js/app.js(~6730-6775)Who can do it, and what they need. Whoever uses the same browser profile after the person has signed out (shared computer, borrowed laptop). Precondition: the person set up the Browser notifier with delivery Pushed to this browser while using that browser, then signed out without removing the connection.
How it works.
/sw.jswith scope/and subscribes; the subscription JSON is saved on the connection.Notification.permissionstays granted for the origin.BrowserNotifier.send()pushes_payload(title, body, url, event)to the browser; sw.js shows it (showNotification(data.title, {body: data.body, data: {url}})) regardless of who, if anyone, is signed in.?next=). The in-tab path is correctly limited to signed-in pages (data-browser-noticesis only rendered for an authenticated person), so only the push path leaks.Impact. Names of companies applied to, reminder subjects and application URLs shown to a third party on a shared machine, indefinitely, until the person revokes the connection from another device. Narrow: needs a shared browser and the push delivery mode, and reveals notification text rather than records.
Evidence.
Verification (verdict: confirmed; severity after verification: Low). Keep it as a low-severity privacy and UX hardening note, not an exposure of records. Correct lines: app.js ~6782-6792 (register and subscribe), plugins/browser/init.py 155-188 (send), sw.js 21-28 (push handler). Corrections to the narrative: (a) sign-out leaving the push in place is intended, since the 'push' option is labelled 'even with Postulo closed' and sessions lapse after Django's default two weeks; (b) the person can stop it from the same browser by removing the connection or by revoking the site's notification permission, so 'only from another device' is wrong; (c) the shared-profile user gains notification text only, and role titles are already visible in browser history through the page <title>; (d) a Web Push subscription is per origin and per browser profile, so a second account that signs in on the same browser and presses Allow gets the same subscription back, and the two accounts' reminders then land in one browser. This is the same shared-profile decision and gives no cross-account read. …
Suggested remediation. On the sign-out form submit (app.js already delegates
submit), callnavigator.serviceWorker.getRegistration('/')→pushManager.getSubscription()→unsubscribe()andregistration.unregister()before the navigation, and have the server mark that connection unusable on the next410; or offer a per-browser forget this browser on sign-out choice. At minimum, add to the connection page's help the sentence that a push subscription belongs to the browser, not the session, and should only be made in a browser that is the person's own, and show the subscribed browsers under Settings with a revoke button (the connection list already allows deleting the connection).Fixed in
a47c0b837on main.