fix(storage): transactional migrations, purge legacy plaintext secrets, private DB files, stricter ids and hosts

- Each migration runs in a transaction. Several rebuild the profiles
  table (create, copy, DROP, rename); a crash between the DROP and the
  rename left a database that failed to open on every later launch.
- Migration 009 removes the Go-era proxyPassword/totpSecret keys still
  sitting in old ssh_config JSON, then VACUUMs and checkpoints the WAL so
  the old pages leave the file. secure_delete is on from now on.
- nexus.db (and -wal/-shm) are created and kept 0600.
- Profile ids are limited to [A-Za-z0-9._-]{1,64}: ids name keychain and
  vault entries (profile:<id>:<kind>), so a crafted id containing ':'
  could shadow or purge another profile's credentials on import/sync.
- Hosts reject control characters (NUL got through) and >253 bytes; jump
  hosts and forward destination/bind addresses get the same host check.
This commit is contained in:
Andy Dixon
2026-10-13 06:55:49 +01:00
parent eb8f08a182
commit 13ca82ab2d
3 changed files with 121 additions and 6 deletions
@@ -0,0 +1,10 @@
-- Profiles saved by the Go app kept proxyPassword and totpSecret inside ssh_config. They live in
-- the keychain or vault now and are dropped on read, but the old JSON stayed in the column until
-- the profile was next saved. Remove them (the store also VACUUMs after this migration so the
-- old pages don't linger in the file).
UPDATE profiles
SET ssh_config = json_remove(ssh_config, '$.proxyPassword', '$.totpSecret')
WHERE json_valid(ssh_config)
AND (json_extract(ssh_config, '$.proxyPassword') IS NOT NULL OR json_extract(ssh_config, '$.totpSecret') IS NOT NULL);
INSERT INTO schema_version (version, applied_at) VALUES (9, datetime('now'));