check / check (pull_request) Successful in 2m41s
The local index lists every backed-up path and chunk hash, but its file mode was left to the SQLite driver and the umask, so under a typical 022 umask a fresh index (and its -wal/-shm side files) landed world-readable. The snapshot export copied the index to snapshot.db with a permissive create as well. provideDatabase now calls ensureIndexFileMode before opening the driver: it creates the index 0600 if missing and chmods an existing one to 0600. Doing this before the driver opens the file matters because SQLite gives its -wal and -shm files the mode of the main database file. The export copy is now created 0600 instead of via the umask-dependent default. Tests under umask 022 cover a fresh index, an existing 0644 index in a 0755 directory, and the export copy, asserting each ends up 0600. Model: opus-4-8
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package database
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"go.uber.org/fx/fxtest"
|
|
"sneak.berlin/go/vaultik/internal/config"
|
|
)
|
|
|
|
// TestProvideDatabaseFreshIndexMode verifies that provideDatabase creates a
|
|
// missing index file owner-only (0600), even under a lenient 022 umask that
|
|
// would otherwise leave a freshly created file world-readable.
|
|
//
|
|
//nolint:paralleltest // syscall.Umask is process-global; parallel tests would clash
|
|
func TestProvideDatabaseFreshIndexMode(t *testing.T) {
|
|
restore := syscall.Umask(0o022)
|
|
defer syscall.Umask(restore)
|
|
|
|
indexPath := filepath.Join(t.TempDir(), "index.sqlite")
|
|
|
|
openIndex(t, indexPath)
|
|
assertPerm(t, indexPath, 0o600)
|
|
}
|
|
|
|
// TestProvideDatabaseExistingIndexMode verifies that provideDatabase tightens
|
|
// an existing world-readable index (0644) in a group/other-readable directory
|
|
// down to owner-only (0600).
|
|
//
|
|
//nolint:paralleltest // syscall.Umask is process-global; parallel tests would clash
|
|
func TestProvideDatabaseExistingIndexMode(t *testing.T) {
|
|
restore := syscall.Umask(0o022)
|
|
defer syscall.Umask(restore)
|
|
|
|
dir := filepath.Join(t.TempDir(), "data")
|
|
|
|
//nolint:gosec // G301: the test intentionally uses a 0755 directory
|
|
err := os.MkdirAll(dir, 0o755)
|
|
if err != nil {
|
|
t.Fatalf("creating index directory: %v", err)
|
|
}
|
|
|
|
//nolint:gosec // G302: the test intentionally uses a 0755 directory
|
|
err = os.Chmod(dir, 0o755)
|
|
if err != nil {
|
|
t.Fatalf("relaxing index directory permissions: %v", err)
|
|
}
|
|
|
|
indexPath := filepath.Join(dir, "index.sqlite")
|
|
|
|
//nolint:gosec // G306: the test intentionally starts from a 0644 index
|
|
err = os.WriteFile(indexPath, nil, 0o644)
|
|
if err != nil {
|
|
t.Fatalf("creating pre-existing index: %v", err)
|
|
}
|
|
|
|
//nolint:gosec // G302: the test intentionally starts from a 0644 index
|
|
err = os.Chmod(indexPath, 0o644)
|
|
if err != nil {
|
|
t.Fatalf("relaxing pre-existing index permissions: %v", err)
|
|
}
|
|
|
|
openIndex(t, indexPath)
|
|
assertPerm(t, indexPath, 0o600)
|
|
}
|
|
|
|
// openIndex runs provideDatabase against indexPath and closes the resulting
|
|
// database before returning.
|
|
func openIndex(t *testing.T, indexPath string) {
|
|
t.Helper()
|
|
|
|
cfg := &config.Config{IndexPath: indexPath}
|
|
|
|
db, err := provideDatabase(fxtest.NewLifecycle(t), cfg)
|
|
if err != nil {
|
|
t.Fatalf("provideDatabase: %v", err)
|
|
}
|
|
|
|
err = db.Close()
|
|
if err != nil {
|
|
t.Fatalf("closing database: %v", err)
|
|
}
|
|
}
|
|
|
|
// assertPerm fails the test unless path has exactly the given permission bits.
|
|
func assertPerm(t *testing.T, path string, want os.FileMode) {
|
|
t.Helper()
|
|
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
t.Fatalf("stat %s: %v", path, err)
|
|
}
|
|
|
|
got := info.Mode().Perm()
|
|
if got != want {
|
|
t.Fatalf("permissions of %s = %#o, want %#o", path, got, want)
|
|
}
|
|
}
|