PST-074: captures/known lets a captures-only token test whether the owner holds a listing with a given title at a company #803

Closed
opened 2026-10-10 10:56:44 +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-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.


  • Severity: Informational
  • Category: information-disclosure (by design, worth recording)
  • Location: src/postulo/api/api.py (776-816)
  • Found by: REST API

Who can do it, and what they need. Someone holding a leaked browser-extension token.

How it works.

  1. POST /captures/known with up to 100 {url, title, company} triples per call at 600 calls/h.
  2. For each, similar is non-empty when the owner holds a listing with that exact title at that company, returning its id, title, date and listing_url.
  3. Iterating over employer names yields which companies the person has listings at.

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 answers at 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 the iexact lookup 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 similar when the url's host matches a listing the owner already holds, or drop similar for tokens without read.

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-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. --- - **Severity:** Informational - **Category:** information-disclosure (by design, worth recording) - **Location:** `src/postulo/api/api.py` (776-816) - **Found by:** REST API **Who can do it, and what they need.** Someone holding a leaked browser-extension token. **How it works.** 1. `POST /captures/known` with up to 100 `{url, title, company}` triples per call at 600 calls/h. 2. For each, `similar` is non-empty when the owner holds a listing with that exact title at that company, returning its id, title, date and `listing_url`. 3. Iterating over employer names yields which companies the person has listings at. **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 answers` at 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 the `iexact` lookup 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 `similar` when the `url`'s host matches a listing the owner already holds, or drop `similar` for tokens without `read`.
tiagoagueda added this to the 1.0.0 milestone 2026-10-10 10:58:08 +00:00
Author
Owner

Fixed in 24e8e98e8 on main.

Fixed in 24e8e98e8 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#803
No description provided.