Repository navigation
fix(server): keep the word list private from other accounts - #24
Merged
Merged
Conversation
The database, its -wal and -shm files, the model list cache and a new data folder were created with the process umask, and the systemd unit set no UMask, so on a shared computer other accounts could read the word list (SCR-450). The API token and backups were already private. Now every file the server keeps in the data folder is readable only by its own account. The BEAM can't give a mode when it creates a file, so files are created empty, made 0600, and only then filled: the database before SQLite first opens it (SQLite gives the -wal, -shm and -journal files the database's mode; a test checks this), each backup and the copy from the old name before VACUUM INTO, and the token and model cache through a private temporary file. A data folder the server makes is 0700. For existing installs, every start checks the folder before the database opens: Kotiko's own files that others can open are made 0600 again (with one warning naming them), and the folder 0700 when it holds nothing but Kotiko's files. A folder with other files in it, or a link, may be shared on purpose, so it is left as it is with a warning that gives the chmod to run. Nothing here stops the start. run.sh now sets umask 077 and the unit from install-service.sh UMask=0077, so files are private from the moment they exist. Docs: the configuration reference, security requirements and assurance case (the umask gap leaves the residual risks), the site's server page, the changelog and slice 29. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Ayla Croft <dev@scriptkittyos.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What (Linear SCR-450, input to the slice 54 security review)
The server's word list could be read by other accounts on a shared computer:
kotiko.db, its-wal/-shm, migration backups,models-cache.json, and a new data folder all followed the process umask. Now they're private, at the root and in depth:-wal/-shm/-journalthe database's mode when it creates them. So the database is created empty at 0600 before SQLite opens it, and the others follow.VACUUM INTOinto an existing empty 0600 file keeps 0600.Kotiko.Private. The BEAM can't create a file with a mode, so the code creates empty 0600 files, writes through a private temp file and renames, and makes new folders 0700. The token, the model cache, the db, the partial copy and the backups all use it.DataDir.make_private/1):backups/to 0700, with one warning listing what changed.KOTIKO_DATA_DIR=~), so it's left alone and a warning gives thechmod. A disk that ignores modes (WSL/mnt/c) gets one warning.run.shsetsumask 077, and the systemd unit getsUMask=0077. Re-runninginstall-service.shpicks this up, and the CHANGELOG says so.Tests (each fails without the fix; verified by breaking it on purpose)
private_test.exscovers the helpers, and the SQLite behaviour this relies on.data_dir_test.exshas 6make_privatecases, including the exact warning text.run.shgives mix umask 0077 even from a 022 shell;UMask=0077.700 600 600 600 600(folder, db,-wal,-shm, token) while running.Docs: the assurance case (gap closed, new residual risk: a shared folder stays shared), the security requirements,
configuration.md("Files in the data folder"), the site's server page, the CHANGELOG and slice 29.Checked
mix format,compile --warnings-as-errorsandcredo --strictpass.mix test --warnings-as-errors --cover: 1106 tests, 23 properties, 0 failures, 91.68 %.mix sobelow: exit 0.check-old-name,bestpractices-links --check,reuse lint, and sitenpm test/npm run checkall pass.Known and left for later (recorded for slice 54)
mix kotiko.export --outputwrites the export with the shell's umask.run.shor the unit, a new, still-empty file has the umask mode for a moment before itschmod.🤖 Generated with Claude Code