on main help sign in
security(sqlite): enable `secure_delete` pragma (#966)

Co-authored-by: Nuno Cruces <ncruces@users.noreply.github.com>

commit78540b012f6ace2a377a00daadf05176d0335ce8 authorbbrodriges <bender.rodriges@gmail.com> dateTue 9 Sep 2025, 16:47 (1 year ago) parentc94c67d
3 files changed+37−23
internal/app/app.go+11−4 internal/db/connect.go+19−17 internal/llm/agent/mcp-tools.go+7−2
internal/app/app.go+11−4
@@ -50,7 +50,7 @@ type App struct {
5050
5151 // global context and cleanup functions
5252 globalCtx context.Context
53− cleanupFuncs []func()
53+ cleanupFuncs []func() error
5454}
5555
5656// New initializes a new applcation instance.
@@ -88,6 +88,9 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
8888 // Initialize LSP clients in the background.
8989 app.initLSPClients(ctx)
9090
91+ // cleanup database upon app shutdown
92+ app.cleanupFuncs = append(app.cleanupFuncs, conn.Close)
93+
9194 // TODO: remove the concept of agent config, most likely.
9295 if cfg.IsConfigured() {
9396 if err := app.InitCoderAgent(); err != nil {
@@ -221,9 +224,10 @@ func (app *App) setupEvents() {
221224 setupSubscriber(ctx, app.serviceEventsWG, "history", app.History.Subscribe, app.events)
222225 setupSubscriber(ctx, app.serviceEventsWG, "mcp", agent.SubscribeMCPEvents, app.events)
223226 setupSubscriber(ctx, app.serviceEventsWG, "lsp", SubscribeLSPEvents, app.events)
224− cleanupFunc := func() {
227+ cleanupFunc := func() error {
225228 cancel()
226229 app.serviceEventsWG.Wait()
230+ return nil
227231 }
228232 app.cleanupFuncs = append(app.cleanupFuncs, cleanupFunc)
229233}
@@ -297,10 +301,11 @@ func (app *App) Subscribe(program *tea.Program) {
297301
298302 app.tuiWG.Add(1)
299303 tuiCtx, tuiCancel := context.WithCancel(app.globalCtx)
300− app.cleanupFuncs = append(app.cleanupFuncs, func() {
304+ app.cleanupFuncs = append(app.cleanupFuncs, func() error {
301305 slog.Debug("Cancelling TUI message handler")
302306 tuiCancel()
303307 app.tuiWG.Wait()
308+ return nil
304309 })
305310 defer app.tuiWG.Done()
306311
@@ -350,7 +355,9 @@ func (app *App) Shutdown() {
350355 // Call call cleanup functions.
351356 for _, cleanup := range app.cleanupFuncs {
352357 if cleanup != nil {
353− cleanup()
358+ if err := cleanup(); err != nil {
359+ slog.Error("Failed to cleanup app properly on shutdown", "error", err)
360+ }
354361 }
355362 }
356363}
internal/db/connect.go+19−17
@@ -7,7 +7,8 @@ import (
77 "log/slog"
88 "path/filepath"
99
10− _ "github.com/ncruces/go-sqlite3/driver"
10+ "github.com/ncruces/go-sqlite3"
11+ "github.com/ncruces/go-sqlite3/driver"
1112 _ "github.com/ncruces/go-sqlite3/embed"
1213
1314 "github.com/pressly/goose/v3"
@@ -18,17 +19,6 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
1819 return nil, fmt.Errorf("data.dir is not set")
1920 }
2021 dbPath := filepath.Join(dataDir, "crush.db")
21− // Open the SQLite database
22− db, err := sql.Open("sqlite3", dbPath)
23− if err != nil {
24− return nil, fmt.Errorf("failed to open database: %w", err)
25− }
26−
27− // Verify connection
28− if err = db.PingContext(ctx); err != nil {
29− db.Close()
30− return nil, fmt.Errorf("failed to connect to database: %w", err)
31− }
3222
3323 // Set pragmas for better performance
3424 pragmas := []string{
@@ -37,14 +27,25 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
3727 "PRAGMA page_size = 4096;",
3828 "PRAGMA cache_size = -8000;",
3929 "PRAGMA synchronous = NORMAL;",
30+ "PRAGMA secure_delete = ON;",
4031 }
4132
42− for _, pragma := range pragmas {
43− if _, err = db.ExecContext(ctx, pragma); err != nil {
44− slog.Error("Failed to set pragma", pragma, err)
45− } else {
46− slog.Debug("Set pragma", "pragma", pragma)
33+ db, err := driver.Open(dbPath, func(c *sqlite3.Conn) error {
34+ for _, pragma := range pragmas {
35+ if err := c.Exec(pragma); err != nil {
36+ return fmt.Errorf("failed to set pragma `%s`: %w", pragma, err)
37+ }
4738 }
39+ return nil
40+ })
41+ if err != nil {
42+ return nil, fmt.Errorf("failed to open database: %w", err)
43+ }
44+
45+ // Verify connection
46+ if err = db.PingContext(ctx); err != nil {
47+ db.Close()
48+ return nil, fmt.Errorf("failed to connect to database: %w", err)
4849 }
4950
5051 goose.SetBaseFS(FS)
@@ -58,5 +59,6 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
5859 slog.Error("Failed to apply migrations", "error", err)
5960 return nil, fmt.Errorf("failed to apply migrations: %w", err)
6061 }
62+
6163 return db, nil
6264}
internal/llm/agent/mcp-tools.go+7−2
@@ -4,6 +4,7 @@ import (
44 "cmp"
55 "context"
66 "encoding/json"
7+ "errors"
78 "fmt"
89 "log/slog"
910 "maps"
@@ -253,11 +254,15 @@ func updateMCPState(name string, state MCPState, err error, client *client.Clien
253254}
254255
255256// CloseMCPClients closes all MCP clients. This should be called during application shutdown.
256−func CloseMCPClients() {
257+func CloseMCPClients() error {
258+ var errs []error
257259 for c := range mcpClients.Seq() {
258− _ = c.Close()
260+ if err := c.Close(); err != nil {
261+ errs = append(errs, err)
262+ }
259263 }
260264 mcpBroker.Shutdown()
265+ return errors.Join(errs...)
261266}
262267
263268var mcpInitRequest = mcp.InitializeRequest{