Skip to content

Create auth secret and token files with owner-only permissions - #741

Open
lukiod wants to merge 2 commits into
HKUDS:mainfrom
lukiod:fix/secret-file-permissions
Open

Create auth secret and token files with owner-only permissions#741
lukiod wants to merge 2 commits into
HKUDS:mainfrom
lukiod:fix/secret-file-permissions

Conversation

@lukiod

@lukiod lukiod commented Jul 31, 2026

Copy link
Copy Markdown

Secret files were written and then chmod'd:

SECRET_FILE.write_text(secret, encoding="utf-8")
try:
    SECRET_FILE.chmod(0o600)
except OSError:
    pass

The file exists with the default umask (usually 0644) between those two calls, so the secret is briefly readable by any local user. And the chmod is swallowed, so if it fails the file stays 0644 and nothing says so.

Adds deeptutor/utils/secret_files.py with write_secret_text, which opens with O_CREAT | O_EXCL and mode 0600 so the file is never wider than intended, and uses it in services/skill/credentials.py and multi_user/identity.py.

It also unlinks a leftover first, since O_CREAT does not narrow the mode of an existing file — an interrupted earlier run would otherwise keep its old permissions.

Tests in tests/utils/test_secret_files.py; the permission assertions are POSIX-only and skip on Windows, where I ran them (3 passed, 3 skipped). The rest of the suite doesn't collect in my environment — 124 import errors from missing optional deps (pydantic_settings, openai, aiohttp), all pre-existing and unrelated.

lukiod added 2 commits July 27, 2026 22:08
Three sites wrote secrets with Path.write_text and narrowed them with chmod
afterwards. write_text creates using 0o666 & ~umask, so the contents were
readable by other local users until the chmod landed, and permanently whenever
it raised, since the error was discarded.

Affected: the auth secret in multi_user/identity.py, minted with
secrets.token_hex(32) and written world readable at creation, the same file on
the legacy migration path, and the skill hub tokens in
services/skill/credentials.py.

write_secret_text passes the mode to os.open so it applies at creation. The
parent directory is created 0700, and a leftover from an interrupted run is
replaced rather than truncated.

Measured on Linux under the default umask: 0644 holding the secret before being
narrowed to 0600.
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