Restore and deep verify opened the decrypted snapshot database read-write through the local-index constructor (applying migrations against whatever the file carried) and left it in the shared temp directory.
New database.OpenReadOnly: opens the file read-only (mode=ro) with query_only on and trusted_schema=OFF, never applies schema files, and refuses any file whose schema carries a trigger, view or virtual table, or lacks an expected table. Restore and deep verify both use it, so a forged file can no longer redefine what the restore queries return.
Each command now writes the decrypted database into its own private (0700) temp directory and removes the whole directory (RemoveAll) on every return path — success, error, and the open failure that previously leaked decrypted metadata. This also sweeps any SQLite side files.
Deep verify now checks the error from closing the temp file, matching restore.
pickNextDownload returns (FileID, bool), so a genuine file carrying the nil UUID is no longer read as "nothing left"; runRestoreLoop returns an error if any file is still pending once it can make no further progress.
Tests: a view named files is refused, a read-only open rejects writes, the temp dir is 0700, a forced open failure leaves no temp directory, and a nil-UUID file is picked rather than abandoned.
What a reader would trip over: the driver only honours mode=ro via a file: URI DSN, so snapshotReadOnlyDSN builds one. Two //nolint:gosec G304 suppressions cover file opens on a MkdirTemp path plus a constant filename.
Model: opus-4-8
Closes https://git.eeqj.de/sneak/vaultik/issues/162.
Restore and deep verify opened the decrypted snapshot database read-write through the local-index constructor (applying migrations against whatever the file carried) and left it in the shared temp directory.
- New `database.OpenReadOnly`: opens the file read-only (`mode=ro`) with `query_only` on and `trusted_schema=OFF`, never applies schema files, and refuses any file whose schema carries a trigger, view or virtual table, or lacks an expected table. Restore and deep verify both use it, so a forged file can no longer redefine what the restore queries return.
- Each command now writes the decrypted database into its own private (`0700`) temp directory and removes the whole directory (`RemoveAll`) on every return path — success, error, and the open failure that previously leaked decrypted metadata. This also sweeps any SQLite side files.
- Deep verify now checks the error from closing the temp file, matching restore.
- `pickNextDownload` returns `(FileID, bool)`, so a genuine file carrying the nil UUID is no longer read as "nothing left"; `runRestoreLoop` returns an error if any file is still pending once it can make no further progress.
Tests: a view named `files` is refused, a read-only open rejects writes, the temp dir is `0700`, a forced open failure leaves no temp directory, and a nil-UUID file is picked rather than abandoned.
What a reader would trip over: the driver only honours `mode=ro` via a `file:` URI DSN, so `snapshotReadOnlyDSN` builds one. Two `//nolint:gosec` G304 suppressions cover file opens on a MkdirTemp path plus a constant filename.
Model: opus-4-8
Restore and deep verify used to open the decrypted snapshot database
read-write through the local-index constructor, which applied migrations
against whatever the file carried, and left the decrypted file in the
shared temp directory. A forged file could redefine what the restore
queries return, and an interrupted open left decrypted metadata on disk.
Add database.OpenReadOnly: opens the file read-only (mode=ro) with
query_only and trusted_schema=OFF, never applies schema files, and
refuses any file whose schema carries a trigger, view or virtual table
or lacks an expected table. Restore and deep verify now both use it.
Each command materializes the database inside its own private (0700)
temp directory and removes the whole directory on every return path, so
the decrypted file and any SQLite side files are always cleaned up.
Deep verify now also checks the error from closing the temp file.
pickNextDownload returns (FileID, bool), so a genuine file carrying the
nil UUID is no longer mistaken for "nothing left"; runRestoreLoop fails
with an error if any file is still pending when it can make no progress.
Model: opus-4-8
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #162.
Restore and deep verify opened the decrypted snapshot database read-write through the local-index constructor (applying migrations against whatever the file carried) and left it in the shared temp directory.
database.OpenReadOnly: opens the file read-only (mode=ro) withquery_onlyon andtrusted_schema=OFF, never applies schema files, and refuses any file whose schema carries a trigger, view or virtual table, or lacks an expected table. Restore and deep verify both use it, so a forged file can no longer redefine what the restore queries return.0700) temp directory and removes the whole directory (RemoveAll) on every return path — success, error, and the open failure that previously leaked decrypted metadata. This also sweeps any SQLite side files.pickNextDownloadreturns(FileID, bool), so a genuine file carrying the nil UUID is no longer read as "nothing left";runRestoreLoopreturns an error if any file is still pending once it can make no further progress.Tests: a view named
filesis refused, a read-only open rejects writes, the temp dir is0700, a forced open failure leaves no temp directory, and a nil-UUID file is picked rather than abandoned.What a reader would trip over: the driver only honours
mode=rovia afile:URI DSN, sosnapshotReadOnlyDSNbuilds one. Two//nolint:gosecG304 suppressions cover file opens on a MkdirTemp path plus a constant filename.Model: opus-4-8
PASS
Model: opus-4-8
8e335008cbto46c295acf3