From 5c74eda48bd5b342f2c02871eaa445216852feaf Mon Sep 17 00:00:00 2001 From: Hadi Date: Fri, 29 May 2026 10:29:18 +0200 Subject: [PATCH] fix: gosec vulns Signed-off-by: Hadi --- internal/config/config.go | 2 +- internal/db/db.go | 21 ++++++++++++++++----- internal/db/entries.go | 2 +- internal/plugins/lua.go | 2 +- internal/plugins/pluginsfile.go | 2 +- internal/ui/app/update.go | 2 +- internal/ui/history/update.go | 17 +++++++++++++---- internal/ui/home/update.go | 19 ++++++++++++------- internal/ui/replay/update.go | 15 +++++++++++---- internal/util/editor.go | 8 +++++--- spilltea.go | 4 ++-- 11 files changed, 64 insertions(+), 30 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 7066ada..d5da9d1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -86,7 +86,7 @@ func Load(path string) error { } func WriteDefaultConfig(path string) error { - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil { return fmt.Errorf("create config dir: %w", err) } if err := os.WriteFile(path, defaultConfig, 0o600); err != nil { diff --git a/internal/db/db.go b/internal/db/db.go index e0a364e..984517c 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -2,6 +2,7 @@ package db import ( "database/sql" + "log" "sync" _ "modernc.org/sqlite" @@ -25,17 +26,25 @@ func Open(path string) (*DB, error) { conn.SetMaxOpenConns(1) d := &DB{conn: conn, path: path} if err := d.migrate(); err != nil { - conn.Close() + if cerr := conn.Close(); cerr != nil { + log.Printf("db: close conn: %v", cerr) + } return nil, err } roConn, err := sql.Open("sqlite", path) if err != nil { - conn.Close() + if cerr := conn.Close(); cerr != nil { + log.Printf("db: close conn: %v", cerr) + } return nil, err } if _, err := roConn.Exec("PRAGMA query_only=ON"); err != nil { - conn.Close() - roConn.Close() + if cerr := conn.Close(); cerr != nil { + log.Printf("db: close conn: %v", cerr) + } + if cerr := roConn.Close(); cerr != nil { + log.Printf("db: close roConn: %v", cerr) + } return nil, err } d.roConn = roConn @@ -114,6 +123,8 @@ func CountEntriesAt(path string) int { } defer conn.Close() var n int - conn.QueryRow(`SELECT COUNT(*) FROM entries`).Scan(&n) + if err := conn.QueryRow(`SELECT COUNT(*) FROM entries`).Scan(&n); err != nil { + log.Printf("db: count entries: %v", err) + } return n } diff --git a/internal/db/entries.go b/internal/db/entries.go index a1c718a..30f6677 100644 --- a/internal/db/entries.go +++ b/internal/db/entries.go @@ -121,7 +121,7 @@ func (d *DB) SearchEntries(term string) ([]Entry, error) { // Uses the persistent read-only connection (PRAGMA query_only=ON) so that any // DML or DDL in the user-supplied expression is rejected by SQLite before it executes. func (d *DB) QueryEntries(where string) ([]Entry, error) { - q := "SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw, flagged FROM entries WHERE " + strings.TrimSpace(where) + q := "SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw, flagged FROM entries WHERE " + strings.TrimSpace(where) // #nosec G202 -- intentional: user-supplied WHERE clause on a PRAGMA query_only=ON connection rows, err := d.roConn.Query(q) if err != nil { return nil, err diff --git a/internal/plugins/lua.go b/internal/plugins/lua.go index df86363..807d38f 100644 --- a/internal/plugins/lua.go +++ b/internal/plugins/lua.go @@ -281,7 +281,7 @@ func registerUtilities(L *lua.LState, mgr *Manager, p *Plugin) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - c := exec.CommandContext(ctx, "sh", "-c", cmd) + c := exec.CommandContext(ctx, "sh", "-c", cmd) // #nosec G204 -- intentional: shell_pipe is a Lua plugin API for user-written scripts c.Stdin = strings.NewReader(input) var stdout, stderr bytes.Buffer diff --git a/internal/plugins/pluginsfile.go b/internal/plugins/pluginsfile.go index 40d2e3a..ab08218 100644 --- a/internal/plugins/pluginsfile.go +++ b/internal/plugins/pluginsfile.go @@ -27,7 +27,7 @@ func OpenPluginsFile(dbPath string) (*PluginsFile, error) { path: path, data: pluginsFileData{Plugins: make(map[string]pluginFileEntry)}, } - raw, err := os.ReadFile(path) + raw, err := os.ReadFile(path) // #nosec G304 -- path is filepath.Join(dbDir, "plugins.yaml"), hardcoded filename if os.IsNotExist(err) { return pf, nil } diff --git a/internal/ui/app/update.go b/internal/ui/app/update.go index 3317c33..a7dedbc 100644 --- a/internal/ui/app/update.go +++ b/internal/ui/app/update.go @@ -178,7 +178,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { editor = "vi" } logPath := filepath.Join(filepath.Dir(m.projectPath), "logs.log") - return m, tea.ExecProcess(exec.Command(editor, logPath), nil) + return m, tea.ExecProcess(exec.Command(editor, logPath), nil) // #nosec G204 G702 -- editor from trusted $EDITOR env var, logPath is a fixed path } if !m.activeIsEditing() { diff --git a/internal/ui/history/update.go b/internal/ui/history/update.go index 964919f..2696f1e 100644 --- a/internal/ui/history/update.go +++ b/internal/ui/history/update.go @@ -2,6 +2,7 @@ package history import ( "fmt" + "log" "net/http" "charm.land/bubbles/v2/key" @@ -234,7 +235,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, h.Flag): if len(m.entries) > 0 && m.database != nil { - m.database.ToggleFlag(m.entries[m.cursor].ID) + if err := m.database.ToggleFlag(m.entries[m.cursor].ID); err != nil { + log.Printf("history: toggle flag: %v", err) + } return m, m.RefreshCmd() } @@ -242,7 +245,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if len(m.entries) > 0 { id := m.entries[m.cursor].ID if m.database != nil { - m.database.DeleteEntry(id) + if err := m.database.DeleteEntry(id); err != nil { + log.Printf("history: delete entry: %v", err) + } } return m, LoadEntriesCmd(m.database) } @@ -261,10 +266,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if hasUnflagged && e.Flagged { continue } - m.database.DeleteEntry(e.ID) + if err := m.database.DeleteEntry(e.ID); err != nil { + log.Printf("history: delete entry: %v", err) + } } } else { - m.database.DeleteAllExceptFlagged() + if err := m.database.DeleteAllExceptFlagged(); err != nil { + log.Printf("history: delete all unflagged: %v", err) + } } } return m, m.clearSearch() diff --git a/internal/ui/home/update.go b/internal/ui/home/update.go index a6bd846..76f0c8f 100644 --- a/internal/ui/home/update.go +++ b/internal/ui/home/update.go @@ -3,6 +3,7 @@ package home import ( crypto "crypto/rand" "fmt" + "log" "os" "path/filepath" "strings" @@ -72,7 +73,7 @@ func (m Model) handleSelection() (tea.Model, tea.Cmd) { return m, m.nameInput.Focus() case kindTemp: dir := tempDir() - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o750); err != nil { return m, nil } initProjectFiles(dir) @@ -90,7 +91,9 @@ func (m Model) deleteSelected() (tea.Model, tea.Cmd) { return m, nil } dir := filepath.Dir(item.path) // parent dir of data.db - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + log.Printf("home: remove project dir: %v", err) + } idx := m.list.GlobalIndex() m.list.RemoveItem(idx) if idx > 0 && idx >= len(m.list.Items()) { @@ -113,7 +116,7 @@ func (m Model) updateNaming(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { m.mode = modeSelect m.nameInput.Blur() dir := filepath.Join(m.projectDir, name) - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o750); err != nil { return m, nil } initProjectFiles(dir) @@ -147,14 +150,14 @@ func IsValidProjectName(s string) bool { func OpenProject(projectDir, name string) (*Project, error) { if name == "tmp" || name == "temp" || name == "temporary" { dir := tempDir() - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o750); err != nil { return nil, err } initProjectFiles(dir) return &Project{Name: "temporary", Path: filepath.Join(dir, "data.db")}, nil } dir := filepath.Join(projectDir, name) - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o750); err != nil { return nil, err } initProjectFiles(dir) @@ -171,9 +174,11 @@ func initProjectFiles(dir string) { for _, name := range []string{"data.db", "logs.log"} { p := filepath.Join(dir, name) if _, err := os.Stat(p); os.IsNotExist(err) { - f, err := os.Create(p) + f, err := os.Create(p) // #nosec G304 -- p is filepath.Join(dir, hardcoded_name), no traversal possible if err == nil { - f.Close() + if cerr := f.Close(); cerr != nil { + log.Printf("home: close project file: %v", cerr) + } } } } diff --git a/internal/ui/replay/update.go b/internal/ui/replay/update.go index 03cc8d6..2467368 100644 --- a/internal/ui/replay/update.go +++ b/internal/ui/replay/update.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "fmt" "io" + "log" "net/http" "strings" "time" @@ -79,7 +80,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { e.ResponseRaw = "Error: " + msg.err.Error() } if m.database != nil && e.DBID != 0 { - m.database.UpdateReplayEntry(entryToDB(*e)) + if err := m.database.UpdateReplayEntry(entryToDB(*e)); err != nil { + log.Printf("replay: update entry: %v", err) + } } } m.refreshListViewport() @@ -216,7 +219,9 @@ func (m Model) updateNormalMode(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { if len(m.entries) > 0 { e := m.entries[m.cursor] if m.database != nil && e.DBID != 0 { - m.database.DeleteReplayEntry(e.DBID) + if err := m.database.DeleteReplayEntry(e.DBID); err != nil { + log.Printf("replay: delete entry: %v", err) + } } m.entries = append(m.entries[:m.cursor], m.entries[m.cursor+1:]...) if m.cursor >= len(m.entries) && m.cursor > 0 { @@ -229,7 +234,9 @@ func (m Model) updateNormalMode(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { case key.Matches(msg, r.DeleteAll): if m.database != nil { - m.database.DeleteAllReplayEntries() + if err := m.database.DeleteAllReplayEntries(); err != nil { + log.Printf("replay: delete all entries: %v", err) + } } m.entries = nil m.cursor = 0 @@ -399,7 +406,7 @@ func doSend(entry Entry) (responseRaw string, statusCode int, err error) { client := &http.Client{ Timeout: 30 * time.Second, Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // #nosec G402 -- intentional for replay feature }, CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse diff --git a/internal/util/editor.go b/internal/util/editor.go index eee7c0d..e7b9835 100644 --- a/internal/util/editor.go +++ b/internal/util/editor.go @@ -35,8 +35,10 @@ func openWithEditor(content string, callback func(string, error) tea.Msg) tea.Cm if _, err := f.WriteString(content); err != nil { log.Printf("editor: writing temp file: %v", err) } - f.Close() - return tea.ExecProcess(exec.Command(resolveEditor(), tmpPath), func(err error) tea.Msg { + if err := f.Close(); err != nil { + log.Printf("editor: closing temp file: %v", err) + } + return tea.ExecProcess(exec.Command(resolveEditor(), tmpPath), func(err error) tea.Msg { // #nosec G204 -- editor from trusted config/$EDITOR, tmpPath from os.CreateTemp defer os.Remove(tmpPath) return callback(tmpPath, err) }) @@ -47,7 +49,7 @@ func OpenExternalEditor(content string) tea.Cmd { if err != nil { return EditorFinishedMsg{Err: err} } - data, readErr := os.ReadFile(tmpPath) + data, readErr := os.ReadFile(tmpPath) // #nosec G304 -- tmpPath is from os.CreateTemp, controlled by this process if readErr != nil { return EditorFinishedMsg{Err: readErr} } diff --git a/spilltea.go b/spilltea.go index f6f278c..d51ce34 100644 --- a/spilltea.go +++ b/spilltea.go @@ -17,7 +17,7 @@ var PluginsFS embed.FS // InstallDefaultPlugins copies embedded default plugins into dir, skipping // files that already exist. Returns the number of files written. func InstallDefaultPlugins(dir string) (int, error) { - if err := os.MkdirAll(dir, 0o755); err != nil { + if err := os.MkdirAll(dir, 0o750); err != nil { return 0, fmt.Errorf("create plugins dir: %w", err) } @@ -39,7 +39,7 @@ func InstallDefaultPlugins(dir string) (int, error) { if err != nil { return written, fmt.Errorf("read embedded %s: %w", e.Name(), err) } - if err := os.WriteFile(dst, data, 0o644); err != nil { + if err := os.WriteFile(dst, data, 0o600); err != nil { return written, fmt.Errorf("write %s: %w", dst, err) } written++