PST-074: captures/known lets a captures-only token test whether the owner holds a listing with a given title at a company #803
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#803
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-074. 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/api/api.py(776-816)Who can do it, and what they need. Someone holding a leaked browser-extension token.
How it works.
POST /captures/knownwith up to 100{url, title, company}triples per call at 600 calls/h.similaris non-empty when the owner holds a listing with that exact title at that company, returning its id, title, date andlisting_url.Impact. The threat model allows the extension to 'ask whether a posting it names is already held' and limits the answer to title/date/link, which the code honours; the oracle over company names is the inherent cost of that feature, bounded by the API rate. No deviation found; recorded for completeness.
Verification (verdict: confirmed; severity after verification: Informational). Line numbers: the route is api.py:777-824 (decorator at 777,
return answersat 824), not 776-816; the similarity query is known.py:64-73. Attack path step 3 overstates it. A title is required as well as a company (title.strip() and company.strip()), so the attacker must guess an exact (title, company) pair, for example a generic title swept across a list of employers. They cannot enumerate employers alone. The match is exact with case and whitespace folding only, and theiexactlookup escapes '%' and '_'. The per-token throttle is charged per call, so the practical ceiling is about 60,000 probes per hour per token. …Suggested remediation. None required; if desired, only answer
similarwhen theurl's host matches a listing the owner already holds, or dropsimilarfor tokens withoutread.Fixed in
24e8e98e8on main.