Skip to content

fix(security): write the token cache owner-only (GHSA-prmg-4fr3-mm6x) - #167

Merged
nictuku merged 1 commit into
mainfrom
fix/ghsa-prmg-token-cache-perms
Sep 23, 2026
Merged

nictuku merged 1 commit into
mainfrom
fix/ghsa-prmg-token-cache-perms

Conversation

@nictuku

@nictuku nictuku commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

Fixes GHSA-prmg-4fr3-mm6x, reported by @Gal3m and @mohammad228.

The bug

The cache holding the long-lived Meta access token (~60 days) was created by a bare open(path, "w"), inside a directory made by mkdir() with no mode. Reproduced on this machine with the old code path:

old behaviour: dir 0o755  file 0o644
group/other can read file: True

On a shared host — CI runner, bastion, multi-UID container — any other local user could read the operator's credential and act as them against the Graph API.

The report also names pipeboard_token_cache.json in pipeboard_auth.py. That module was deleted in 1.0.121, so there is nothing left to fix there; token_cache.json was the only remaining credential file written by the package (grep for other open(..., "w") writes finds none).

The fix

  • The file is created with os.open(path, O_WRONLY|O_CREAT|O_TRUNC, 0o600) and the directory with mkdir(mode=0o700).
  • Both are then narrowed explicitly, because O_CREAT's mode applies only when the file is created and mkdir's mode is masked by the umask and is a no-op for a directory that already exists. This is also what repairs a cache written by an earlier version.
  • The same narrowing runs on load, so an existing install stops being world-readable as soon as the server starts — no re-authentication needed.
  • _restrict_permissions is best-effort and never raises into the caller, and it is skipped on Windows, where chmod cannot express "owner only" and access is governed by ACLs.

The fd handling is written so the descriptor is closed exactly once: if os.fdopen fails the raw fd is closed, otherwise the file object owns it.

Tests

tests/test_token_cache_permissions.py — 7 cases: file and directory modes, that the modes hold under umask(0o000) (the umask is what produced 0644, so it is what the fix has to survive), that a pre-existing 0644 cache is narrowed on both save and load, that the owner can still read what was written, and that a vanished path does not raise.

Full suite: 604 passed, 9 skipped. Bumped to 1.0.127 with a SECURITY.md entry that tells operators on shared hosts to rotate the token.

The cache holding the long-lived Meta access token was created by a bare open(path, 'w') inside a directory made by mkdir() with no mode, so under the usual 0022 umask it landed at 0644 inside 0755 — readable by every other local user on a shared host.

The file is now created with os.open(..., O_CREAT, 0o600) and the directory with mode 0o700. O_CREAT's mode only applies at creation and mkdir's is masked by the umask, so both are narrowed explicitly afterwards; the same narrowing runs on load, which repairs a cache written by an earlier version without requiring re-authentication. The chmod step is skipped on Windows, where access is governed by ACLs.

Bumps to 1.0.127 and documents the advisory in SECURITY.md.
@nictuku
nictuku merged commit d5eacee into main Sep 23, 2026
4 checks passed
PM-Labs pushed a commit to pmlabs-org/mcp-meta-ads that referenced this pull request Oct 11, 2026
…pipeboard-co#167)

The cache holding the long-lived Meta access token was created by a bare open(path, 'w') inside a directory made by mkdir() with no mode, so under the usual 0022 umask it landed at 0644 inside 0755 — readable by every other local user on a shared host.

The file is now created with os.open(..., O_CREAT, 0o600) and the directory with mode 0o700. O_CREAT's mode only applies at creation and mkdir's is masked by the umask, so both are narrowed explicitly afterwards; the same narrowing runs on load, which repairs a cache written by an earlier version without requiring re-authentication. The chmod step is skipped on Windows, where access is governed by ACLs.

Bumps to 1.0.127 and documents the advisory in SECURITY.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant