Skip to content

Commit

Permalink
Test that PRAGMA query_only=1 prevents writes through all connections.
Browse files Browse the repository at this point in the history
Also avoid setting this pragma in the URI query parameters passed to
Open, because there's no guarantee that this works for file paths.
  • Loading branch information
LTLA committed Jan 24, 2025
1 parent d425a4c commit 1844349
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 2 deletions.
10 changes: 8 additions & 2 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,16 @@ CREATE INDEX index_links ON links(tid, fid);
}

func initializeReadOnlyDatabase(path string) (*sql.DB, error) {
ro_db, err := sql.Open("sqlite", path + "?_pragma=query_only(1)")
ro_db, err := sql.Open("sqlite", path)
if err != nil {
return nil, fmt.Errorf("failed to open read-only SQLite handle; %w", err)
return nil, fmt.Errorf("failed to open SQLite handle; %w", err)
}

_, err = ro_db.Exec("PRAGMA query_only=1")
if err != nil {
return nil, fmt.Errorf("failed to set SQLite handle as read-only; %w", err)
}

return ro_db, nil
}

Expand Down
57 changes: 57 additions & 0 deletions database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2029,3 +2029,60 @@ func TestFetchRegisteredDirectoryNames(t *testing.T) {
t.Fatalf("unexpected names for a non-registered directory")
}
}

func TestInitializeReadOnlyDatabase(t *testing.T) {
tmp, err := os.MkdirTemp("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)

dbpath := filepath.Join(tmp, "db.sqlite3")
dbconn, err := initializeDatabase(dbpath)
if err != nil {
t.Fatal(err)
}
defer dbconn.Close()

tokr, err := newUnicodeTokenizer(false)
if err != nil {
t.Fatal(err)
}

// Mocking up some contents.
to_add := filepath.Join(tmp, "to_add")
err = mockDirectory(to_add)
if err != nil {
t.Fatal(err)
}

// Checking that we can't write through a read-only connection.
ro_dbconn, err := initializeReadOnlyDatabase(dbpath)
if err != nil {
t.Fatal(err)
}
defer ro_dbconn.Close()

_, err = addNewDirectory(ro_dbconn, to_add, []string{ "metadata.json", "other.json" }, "myself", tokr)
if err == nil || strings.Index(err.Error(), "read-only") >= 0 {
t.Error("expected a failure to modify the database through read-only connection")
}

// Adding it as a negative control.
comments, err := addNewDirectory(dbconn, to_add, []string{ "metadata.json", "other.json" }, "myself", tokr)
if err != nil {
t.Fatal(err)
}
if len(comments) > 0 {
t.Fatalf("unexpected comments from the directory addition %v", comments)
}

// Checking that we can still read from this.
found, err := isDirectoryRegistered(dbconn, to_add)
if err != nil {
t.Fatal(err)
}
if !found {
t.Error("failed to find the newly added directory through a read-only connection")
}
}

0 comments on commit 1844349

Please sign in to comment.