on main help sign in
security(sqlite): enable `secure_delete` pragma (#966)
78540b0bbrodriges
internal/app/app.go+11−4
@@ -50,7 +50,7 @@ type App struct {
50
50
51 // global context and cleanup functions
51 // global context and cleanup functions
52 globalCtx context.Context
52 globalCtx context.Context
53− cleanupFuncs []func()
53+ cleanupFuncs []func() error
54}
54}
55
55
56// New initializes a new applcation instance.
56// New initializes a new applcation instance.
@@ -88,6 +88,9 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
88 // Initialize LSP clients in the background.
88 // Initialize LSP clients in the background.
89 app.initLSPClients(ctx)
89 app.initLSPClients(ctx)
90
90
91+ // cleanup database upon app shutdown
92+ app.cleanupFuncs = append(app.cleanupFuncs, conn.Close)
93+
91 // TODO: remove the concept of agent config, most likely.
94 // TODO: remove the concept of agent config, most likely.
92 if cfg.IsConfigured() {
95 if cfg.IsConfigured() {
93 if err := app.InitCoderAgent(); err != nil {
96 if err := app.InitCoderAgent(); err != nil {
@@ -221,9 +224,10 @@ func (app *App) setupEvents() {
221 setupSubscriber(ctx, app.serviceEventsWG, "history", app.History.Subscribe, app.events)
224 setupSubscriber(ctx, app.serviceEventsWG, "history", app.History.Subscribe, app.events)
222 setupSubscriber(ctx, app.serviceEventsWG, "mcp", agent.SubscribeMCPEvents, app.events)
225 setupSubscriber(ctx, app.serviceEventsWG, "mcp", agent.SubscribeMCPEvents, app.events)
223 setupSubscriber(ctx, app.serviceEventsWG, "lsp", SubscribeLSPEvents, app.events)
226 setupSubscriber(ctx, app.serviceEventsWG, "lsp", SubscribeLSPEvents, app.events)
224− cleanupFunc := func() {
227+ cleanupFunc := func() error {
225 cancel()
228 cancel()
226 app.serviceEventsWG.Wait()
229 app.serviceEventsWG.Wait()
230+ return nil
227 }
231 }
228 app.cleanupFuncs = append(app.cleanupFuncs, cleanupFunc)
232 app.cleanupFuncs = append(app.cleanupFuncs, cleanupFunc)
229}
233}
@@ -297,10 +301,11 @@ func (app *App) Subscribe(program *tea.Program) {
297
301
298 app.tuiWG.Add(1)
302 app.tuiWG.Add(1)
299 tuiCtx, tuiCancel := context.WithCancel(app.globalCtx)
303 tuiCtx, tuiCancel := context.WithCancel(app.globalCtx)
300− app.cleanupFuncs = append(app.cleanupFuncs, func() {
304+ app.cleanupFuncs = append(app.cleanupFuncs, func() error {
301 slog.Debug("Cancelling TUI message handler")
305 slog.Debug("Cancelling TUI message handler")
302 tuiCancel()
306 tuiCancel()
303 app.tuiWG.Wait()
307 app.tuiWG.Wait()
308+ return nil
304 })
309 })
305 defer app.tuiWG.Done()
310 defer app.tuiWG.Done()
306
311
@@ -350,7 +355,9 @@ func (app *App) Shutdown() {
350 // Call call cleanup functions.
355 // Call call cleanup functions.
351 for _, cleanup := range app.cleanupFuncs {
356 for _, cleanup := range app.cleanupFuncs {
352 if cleanup != nil {
357 if cleanup != nil {
353− cleanup()
358+ if err := cleanup(); err != nil {
359+ slog.Error("Failed to cleanup app properly on shutdown", "error", err)
360+ }
354 }
361 }
355 }
362 }
356}
363}
internal/db/connect.go+19−17
@@ -7,7 +7,8 @@ import (
7 "log/slog"
7 "log/slog"
8 "path/filepath"
8 "path/filepath"
9
9
10− _ "github.com/ncruces/go-sqlite3/driver"
10+ "github.com/ncruces/go-sqlite3"
11+ "github.com/ncruces/go-sqlite3/driver"
11 _ "github.com/ncruces/go-sqlite3/embed"
12 _ "github.com/ncruces/go-sqlite3/embed"
12
13
13 "github.com/pressly/goose/v3"
14 "github.com/pressly/goose/v3"
@@ -18,17 +19,6 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
18 return nil, fmt.Errorf("data.dir is not set")
19 return nil, fmt.Errorf("data.dir is not set")
19 }
20 }
20 dbPath := filepath.Join(dataDir, "crush.db")
21 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− }
32
22
33 // Set pragmas for better performance
23 // Set pragmas for better performance
34 pragmas := []string{
24 pragmas := []string{
@@ -37,14 +27,25 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
37 "PRAGMA page_size = 4096;",
27 "PRAGMA page_size = 4096;",
38 "PRAGMA cache_size = -8000;",
28 "PRAGMA cache_size = -8000;",
39 "PRAGMA synchronous = NORMAL;",
29 "PRAGMA synchronous = NORMAL;",
30+ "PRAGMA secure_delete = ON;",
40 }
31 }
41
32
33+ db, err := driver.Open(dbPath, func(c *sqlite3.Conn) error {
42− for _, pragma := range pragmas {
34+ for _, pragma := range pragmas {
43− if _, err = db.ExecContext(ctx, pragma); err != nil {
35+ if err := c.Exec(pragma); err != nil {
44− slog.Error("Failed to set pragma", pragma, err)
36+ return fmt.Errorf("failed to set pragma `%s`: %w", pragma, err)
45− } else {
37+ }
46− slog.Debug("Set pragma", "pragma", pragma)
47 }
38 }
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)
48 }
49 }
49
50
50 goose.SetBaseFS(FS)
51 goose.SetBaseFS(FS)
@@ -58,5 +59,6 @@ func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
58 slog.Error("Failed to apply migrations", "error", err)
59 slog.Error("Failed to apply migrations", "error", err)
59 return nil, fmt.Errorf("failed to apply migrations: %w", err)
60 return nil, fmt.Errorf("failed to apply migrations: %w", err)
60 }
61 }
62+
61 return db, nil
63 return db, nil
62}
64}
internal/llm/agent/mcp-tools.go+7−2
@@ -4,6 +4,7 @@ import (
4 "cmp"
4 "cmp"
5 "context"
5 "context"
6 "encoding/json"
6 "encoding/json"
7+ "errors"
7 "fmt"
8 "fmt"
8 "log/slog"
9 "log/slog"
9 "maps"
10 "maps"
@@ -253,11 +254,15 @@ func updateMCPState(name string, state MCPState, err error, client *client.Clien
253}
254}
254
255
255// CloseMCPClients closes all MCP clients. This should be called during application shutdown.
256// CloseMCPClients closes all MCP clients. This should be called during application shutdown.
256−func CloseMCPClients() {
257+func CloseMCPClients() error {
258+ var errs []error
257 for c := range mcpClients.Seq() {
259 for c := range mcpClients.Seq() {
258− _ = c.Close()
260+ if err := c.Close(); err != nil {
261+ errs = append(errs, err)
262+ }
259 }
263 }
260 mcpBroker.Shutdown()
264 mcpBroker.Shutdown()
265+ return errors.Join(errs...)
261}
266}
262
267
263var mcpInitRequest = mcp.InitializeRequest{
268var mcpInitRequest = mcp.InitializeRequest{