PST-059: Generated downloads (calendar, report CSV and PDF, contact export) are sent without Cache-Control: no-store #788
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#788
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-059. 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/applications/views.py(1220-1253);src/postulo/jobs/views.py(1021-1046)Who can do it, and what they need. Whoever next uses the same browser profile or disk cache (shared or family computer, library machine), or an operator-installed caching proxy that applies heuristic caching. Needs no account; the victim must have downloaded one of these files while signed in.
How it works.
private, max-age=86400, so a picture that an administrator fetched for any member stays cached for a day.Impact. Interview dates, company names, contacts' names and the application evidence list can remain in a cache that SECURITY.md says files never enter ("Files are delivered ... with Cache-Control: no-store"). Narrow precondition, so low. Sibling downloads (reminder feed, calendar_download, vCards, kind files, candidate file, uploads, rendered documents) do set no-store, which shows the omission is an oversight rather than a decision. Contact data-subject export lacking it is already on the list; these are different endpoints.
Evidence.
Verification (verdict: confirmed; severity after verification: Low). Keep the five routes (the two interview .ics endpoints, report CSV, report PDF GET, and the three API calendar feeds), and drop the avatar and logo sentence because it is deliberate and test-pinned. Line corrections: ReportCSVView.get is about views.py:1351-1359 and ReportPDFView.get about 1402-1416. The attacker needs the victim's browser profile or disk cache, which the threat model excludes, and the identical data is cached just as freely in HTML pages. Treat it as a consistency fix against SECURITY.md (one shared helper that sets
private, max-age=0, no-storeon generated downloads, plus a test that walks the download routes) rather than a vulnerability.Suggested remediation. Set
Cache-Control: private, max-age=0, no-storeon every authenticated response that carries a download (a small middleware keyed on Content-Disposition: attachment, ornever_cacheon these views and the API calendar feeds), serve avatars and logos withno-cacheor a short private lifetime, and add a test that iterates every download route and asserts the header.Also covered by this issue.
src/postulo/jobs/views.py(1021-1046); Informational). Narrow: a copy of one contact's personal-data document may persist in a cache after the session. SECURITY.md promises files are delivered 'with Cache-Control: no-store'.