PST-075: The messages partial decides a message is HTML by a substring test on extra_tags #804

Closed
opened 2026-10-10 10:56:45 +00:00 by tiagoagueda · 1 comment
Owner

Found in the security audit of 2026-10-10 (SECURITY-AUDIT-2026-10-10.md), run against main at c30851b from 8 to 10 October 2026, as PST-075. Severity: Informational.

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.


  • Severity: Informational
  • Category: template-safety / fragile-guard
  • Location: src/postulo/templates/partials/messages.html (12)
  • Found by: XSS and CSP

Who can do it, and what they need. No attacker today: the only producers of extra_tags="safe" (capture_views.py:458, 498, 611) build their text with format_html, and allauth's messages carry no extra tag.

How it works.

A future call messages.info(request, user_text, extra_tags="unsafe") or any tag containing the letters 'safe' (e.g. 'safe-to-retry') would be printed with |safe, since 'safe' in "unsafe" is true for the string extra_tags holds.

Impact. None now; a latent stored-XSS sink that depends on nobody ever choosing a tag that contains 'safe'.

Verification (verdict: confirmed; severity after verification: Informational). File and line are correct (src/postulo/templates/partials/messages.html:12). Reachability needs a future code change that introduces a message with a tag containing 'safe' and unescaped user text. There is no attacker-reachable path today. Suggested fix: compare tags as a list, for example 'safe' in message.extra_tags.split (or a message.tags filter), or drop the |safe branch and rely on SafeData, which Django already preserves in storage.

Suggested remediation. Test the tag as a word ({% if 'safe' in message.extra_tags.split %}) or, better, keep a dedicated helper (e.g. messages_tool.safe_message) that is the only producer of that tag, with a lint/test over messages.*(... extra_tags= calls.

Found in the security audit of 2026-10-10 (`SECURITY-AUDIT-2026-10-10.md`), run against `main` at `c30851b` from 8 to 10 October 2026, as **PST-075**. Severity: **Informational**. 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. --- - **Severity:** Informational - **Category:** template-safety / fragile-guard - **Location:** `src/postulo/templates/partials/messages.html` (12) - **Found by:** XSS and CSP **Who can do it, and what they need.** No attacker today: the only producers of extra_tags="safe" (capture_views.py:458, 498, 611) build their text with format_html, and allauth's messages carry no extra tag. **How it works.** A future call `messages.info(request, user_text, extra_tags="unsafe")` or any tag containing the letters 'safe' (e.g. 'safe-to-retry') would be printed with |safe, since `'safe' in "unsafe"` is true for the string extra_tags holds. **Impact.** None now; a latent stored-XSS sink that depends on nobody ever choosing a tag that contains 'safe'. **Verification** (verdict: confirmed; severity after verification: Informational). File and line are correct (src/postulo/templates/partials/messages.html:12). Reachability needs a future code change that introduces a message with a tag containing 'safe' and unescaped user text. There is no attacker-reachable path today. Suggested fix: compare tags as a list, for example `'safe' in message.extra_tags.split` (or a `message.tags` filter), or drop the `|safe` branch and rely on SafeData, which Django already preserves in storage. **Suggested remediation.** Test the tag as a word (`{% if 'safe' in message.extra_tags.split %}`) or, better, keep a dedicated helper (e.g. messages_tool.safe_message) that is the only producer of that tag, with a lint/test over `messages.*(... extra_tags=` calls.
tiagoagueda added this to the 1.0.0 milestone 2026-10-10 10:58:09 +00:00
Author
Owner

Fixed in 138e717fb on main.

Fixed in 138e717fb on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#804
No description provided.