SQLite creates its file with the process umask, which on a stock system means every account on the machine can read it. What is in there is not a working credential — keys are stored hashed — but it is the whole tenancy tree and every request anyone made, and that should not depend on an operator remembering to chmod it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1839832b8b
commit
cd3233ffe8
2 files changed
+43
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -44,6 +45,17 @@ func OpenSQLite(ctx context.Context, path string) (*SQLite, error) {
|
||||
_ = db.Close()
|
||||
return nil, err
|
||||
}
|
||||
// SQLite creates the file with the process umask, which on a stock system
|
||||
// means world-readable. What is in here is not a working credential — keys
|
||||
// are stored hashed — but it is the whole tenancy tree and every request
|
||||
// anyone made, and that should not be readable by every account on the
|
||||
// machine because nobody remembered to chmod it.
|
||||
for _, f := range []string{path, path + "-wal", path + "-shm"} {
|
||||
if err := os.Chmod(f, 0o600); err != nil && !os.IsNotExist(err) {
|
||||
_ = db.Close()
|
||||
return nil, fmt.Errorf("securing %s: %w", f, err)
|
||||
}
|
||||
}
|
||||
return &SQLite{db: db}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package store
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -403,3 +404,33 @@ func must(t *testing.T, err error) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheStoreIsNotWorldReadable(t *testing.T) {
|
||||
// SQLite creates the file with the process umask, which on a stock system
|
||||
// leaves it readable by every account on the machine. What is in here is
|
||||
// not a working credential, but it is the whole tenancy tree and every
|
||||
// request anyone made.
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "llmgw.db")
|
||||
s, err := OpenSQLite(context.Background(), path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
// Force the WAL files into existence.
|
||||
tree(t, s)
|
||||
|
||||
for _, name := range []string{"llmgw.db", "llmgw.db-wal", "llmgw.db-shm"} {
|
||||
info, err := os.Stat(filepath.Join(dir, name))
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mode := info.Mode().Perm(); mode&0o077 != 0 {
|
||||
t.Errorf("%s is mode %o", name, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user