PST-033: Any API token, including a captures-only one, learns the account's email address from GET /me #762

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


  • Severity: Low
  • Category: information-disclosure / scope-creep
  • Location: src/postulo/api/api.py (408-425; 409-424)
  • Found by: REST API, ownership review of api

Who can do it, and what they need. Someone holding a leaked browser-extension token (captures only), or any other low-scope token.

How it works.

  1. GET /api/v1/me with the token.
  2. The answer carries owner: <email> as well as the time zone. Verified: a captures-only token received {'owner': 'applicant@example.org', ...}; a token issued with no scopes at all also received it.

Impact. The email is the sign-in identifier for the account and is personal data; a leaked extension token now also yields a phishing/credential-stuffing target tied to a known job seeker, beyond the 'capture API only' reach the threat model assigns it.

Evidence.

api.py 416-423: `return {"name": token.name, "owner": token.owner.email, "scopes": token.scopes, "time_zone": ...}` under the API default `auth=TokenAuth()` (api.py 127) which checks no scope.

Verification (verdict: confirmed; severity after verification: Low). Correct the file reference to api.py 409-424 plus the TokenAuth class in auth.py 74-85 and TokenOut in schemas.py 1033-1040. It is intended and test-pinned ('any live token may ask /me'), so the threat model should either say it or the field should shrink. Options: return owner only to tokens holding read, or return a masked address. Confidence in the facts is high, and the severity is at most low.

Suggested remediation. Return the email only to a token holding read (or drop it: a client needs the scopes and the time zone, not the address). Consider refusing tokens that hold no scope at all in ApiToken.issue.

Also covered by this issue.

  • A captures-only token (browser extension) learns the account's email address and time zone from /me (src/postulo/api/api.py (409-424); Informational). Small: the person who installed the extension already knows their address; a thief learns the account identity, which helps target phishing or a sign-in attempt. No record data.
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-033**. 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. --- - **Severity:** Low - **Category:** information-disclosure / scope-creep - **Location:** `src/postulo/api/api.py` (408-425; 409-424) - **Found by:** REST API, ownership review of api **Who can do it, and what they need.** Someone holding a leaked browser-extension token (`captures` only), or any other low-scope token. **How it works.** 1. `GET /api/v1/me` with the token. 2. The answer carries `owner: <email>` as well as the time zone. Verified: a captures-only token received `{'owner': 'applicant@example.org', ...}`; a token issued with no scopes at all also received it. **Impact.** The email is the sign-in identifier for the account and is personal data; a leaked extension token now also yields a phishing/credential-stuffing target tied to a known job seeker, beyond the 'capture API only' reach the threat model assigns it. **Evidence.** ```text api.py 416-423: `return {"name": token.name, "owner": token.owner.email, "scopes": token.scopes, "time_zone": ...}` under the API default `auth=TokenAuth()` (api.py 127) which checks no scope. ``` **Verification** (verdict: confirmed; severity after verification: Low). Correct the file reference to api.py 409-424 plus the TokenAuth class in auth.py 74-85 and TokenOut in schemas.py 1033-1040. It is intended and test-pinned ('any live token may ask /me'), so the threat model should either say it or the field should shrink. Options: return `owner` only to tokens holding `read`, or return a masked address. Confidence in the facts is high, and the severity is at most low. **Suggested remediation.** Return the email only to a token holding `read` (or drop it: a client needs the scopes and the time zone, not the address). Consider refusing tokens that hold no scope at all in `ApiToken.issue`. **Also covered by this issue.** - **A captures-only token (browser extension) learns the account's email address and time zone from /me** (`src/postulo/api/api.py` (409-424); Informational). Small: the person who installed the extension already knows their address; a thief learns the account identity, which helps target phishing or a sign-in attempt. No record data.
tiagoagueda added this to the 0.6.0 milestone 2026-10-10 10:57:40 +00:00
Author
Owner

Fixed in 016158c9b on main.

Fixed in 016158c9b 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#762
No description provided.