PST-089: Board-recipe selection reads a different host from the one fetched when the URL carries userinfo #818
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#818
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-089. 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.
src/postulo/plugins/builtin/boards/__init__.py(61-72)Who can do it, and what they need. Someone who gets a member to capture a crafted link, or a holder of a captures token. No other precondition: page_address() accepts any http(s) URL including userinfo.
How it works.
https://www.linkedin.com:x@evil.example/jobs/view/1. page_address() accepts it (only the scheme and control characters are checked).urlparse(url).netloc.split(':')[0]and getswww.linkedin.com, so the LinkedIn recipe claims the page, while the fetcher and remembered places use hostnameevil.example(verified in a scratch run).urlparse(url).netloc=www.linkedin.com:x@evil.example, and the stored URL is rendered as a link.Impact. Cosmetic spoofing of which board a capture came from (a hostile page is parsed by LinkedIn's selectors and labelled as LinkedIn). No code execution or data access. core/link_services already refuses userinfo for exactly this reason; the capture path does not.
Verification (verdict: confirmed; severity after verification: Informational). Precondition: a member submits, or is talked into submitting, a URL with userinfo. Effect: the LinkedIn or Greenhouse recipe claims a page from another host, and the stored
sourceand the capture URL link read as the board with@evil-hostappended. There is no code execution, data access or SSRF. The suggested fix is to useurlsplit(url).hostnameinrecipe_forandsource, or to refuse userinfo inpage_addressas link_services does. Confidence in the behaviour is high; its security significance is low.Suggested remediation. Use urlsplit(url).hostname in recipe_for and for
source, and reject or strip userinfo in page_address() for captured URLs.Fixed in
ddafcbe0eon main.