Skip to content

fix(auth): atomic 0600 token file, coverage gate, no bundled OAuth creds - #19

Merged
matagus merged 2 commits into
mainfrom
fix/coverage-gate-and-auth-file-perms
Sep 14, 2026
Merged

matagus merged 2 commits into
mainfrom
fix/coverage-gate-and-auth-file-perms

Conversation

@matagus

@matagus matagus commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

Three security/quality fixes to the auth path.

1. Coverage gate — fail_under = 90 in [tool.coverage.report], so coverage can't regress silently (Codecov runs with fail_ci_if_error: false, making the local gate the only enforcement).

2. Token file at rest — save_oauth_token used to write ~/.quickup/auth.json with default umask perms (e.g. 0644) and chmod to 0600 only afterwards, leaving the OAuth token briefly world-readable. It now writes through a 0600 temp file and publishes atomically with os.replace, cleaning up on failure.

3. No bundled OAuth credentials (breaking) — the ClickUp client ID/secret were hardcoded in quickup/cli/auth.py and readable by anyone with repo access. get_oauth_config() now requires QUICKUP_CLIENT_ID / QUICKUP_CLIENT_SECRET from the environment or a local .env, raising the new OAuthConfigError (exit 6, with a setup hint) otherwise; quickup login fails fast before opening a browser. README, docs and .env.example document creating your own app (redirect URI http://localhost:4242).

⚠️ The leaked credential pair still needs revoking/rotating in ClickUp — removing it from HEAD does not purge git history.

Verification: 235 tests pass (was 226), coverage 95.05% vs the 90% gate; new tests assert 0600 under umask 000, no leftover temp file, missing/partial/blank credentials, .env loading, env-wins-over-.env, and browser-not-opened fail-fast. ruff, black, isort, pyright clean via pre-commit; quickup login without credentials exits 6 with the hint on stderr.

- pyproject: fail_under = 90 in [tool.coverage.report] so coverage cannot
  regress silently (currently 95.26%; Codecov runs with fail_ci_if_error: false).
- save_oauth_token now creates ~/.quickup/auth.json through a 0600 temp file and
  publishes it with os.replace, removing the window where the OAuth token sat on
  disk with default-umask permissions (e.g. 0644) before the chmod.
- tests: assert 0600 holds under umask 000, and that no temp file is left behind.
…g them

The ClickUp OAuth client ID and secret were committed in plaintext at
quickup/cli/auth.py:13-14, exposing a shared app credential to anyone with read
access to the repo (and permanently to git history). They are now gone:
get_oauth_config() resolves QUICKUP_CLIENT_ID / QUICKUP_CLIENT_SECRET from the
environment or a local .env file and raises the new OAuthConfigError (exit code
6, with a setup hint) when either is missing or blank.

- quickup login fails fast on that error before opening a browser tab, and no
  longer flattens ClickupyError into OAuthError, so the hint survives.
- Docs updated: README, commands.rst, features.rst, Installation.rst, and
  .env.example now document creating your own app (redirect URI
  http://localhost:4242) and exporting the two variables.
- Tests: 235 pass (was 226) covering missing/partial/blank credentials, .env
  loading, real env winning over .env, and browser-not-opened fail-fast.
  Coverage 95.05%; ruff, black and isort clean.

BREAKING CHANGE: `quickup login` no longer works out of the box. Create a
ClickUp OAuth app and set QUICKUP_CLIENT_ID and QUICKUP_CLIENT_SECRET first.

NOTE: the leaked pair must still be revoked/rotated in ClickUp — deleting it
from HEAD does not remove it from git history.
@matagus matagus changed the title fix(auth): write token file atomically at 0600; add coverage gate fix(auth): atomic 0600 token file, coverage gate, no bundled OAuth creds Sep 14, 2026
@matagus
matagus merged commit 8a26aa0 into main Sep 14, 2026
8 checks passed
@matagus
matagus deleted the fix/coverage-gate-and-auth-file-perms branch September 14, 2026 01:46
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