summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/dashboard/main.go5
-rw-r--r--internal/auth/middleware.go4
-rw-r--r--internal/store/sqlite.go22
3 files changed, 23 insertions, 8 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
index d7da061..6b895d1 100644
--- a/cmd/dashboard/main.go
+++ b/cmd/dashboard/main.go
@@ -90,8 +90,11 @@ func main() {
var googleCalendarClient api.GoogleCalendarAPI
if cfg.HasGoogleCalendar() {
+ // Use timeout context to prevent startup hangs if credentials file is unreachable
+ initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
var err error
- googleCalendarClient, err = api.NewGoogleCalendarClient(context.Background(), cfg.GoogleCredentialsFile, cfg.GoogleCalendarID)
+ googleCalendarClient, err = api.NewGoogleCalendarClient(initCtx, cfg.GoogleCredentialsFile, cfg.GoogleCalendarID)
+ cancel()
if err != nil {
log.Printf("Warning: failed to initialize Google Calendar client: %v", err)
} else {
diff --git a/internal/auth/middleware.go b/internal/auth/middleware.go
index b440032..ecdde82 100644
--- a/internal/auth/middleware.go
+++ b/internal/auth/middleware.go
@@ -3,6 +3,7 @@ package auth
import (
"context"
"crypto/rand"
+ "crypto/subtle"
"encoding/base64"
"net/http"
@@ -82,7 +83,8 @@ func (m *Middleware) CSRFProtect(next http.Handler) http.Handler {
requestToken = r.FormValue("csrf_token")
}
- if requestToken == "" || requestToken != token {
+ // Use constant-time comparison to prevent timing attacks
+ if requestToken == "" || subtle.ConstantTimeCompare([]byte(requestToken), []byte(token)) != 1 {
http.Error(w, "Forbidden - CSRF Token Mismatch", http.StatusForbidden)
return
}
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go
index a9a0597..12aa1ce 100644
--- a/internal/store/sqlite.go
+++ b/internal/store/sqlite.go
@@ -3,6 +3,7 @@ package store
import (
"database/sql"
"encoding/json"
+ "errors"
"fmt"
"log"
"os"
@@ -11,6 +12,7 @@ import (
"time"
_ "github.com/mattn/go-sqlite3"
+
"task-dashboard/internal/models"
)
@@ -169,8 +171,12 @@ func (s *Store) SaveTasks(tasks []models.Task) error {
defer func() { _ = stmt.Close() }()
for _, task := range tasks {
- labelsJSON, _ := json.Marshal(task.Labels)
- _, err := stmt.Exec(
+ labelsJSON, err := json.Marshal(task.Labels)
+ if err != nil {
+ log.Printf("Warning: failed to marshal labels for task %s: %v", task.ID, err)
+ labelsJSON = []byte("[]")
+ }
+ _, err = stmt.Exec(
task.ID,
task.Content,
task.Description,
@@ -213,8 +219,12 @@ func (s *Store) DeleteTask(id string) error {
// UpsertTask inserts or updates a single task
func (s *Store) UpsertTask(task models.Task) error {
- labelsJSON, _ := json.Marshal(task.Labels)
- _, err := s.db.Exec(`
+ labelsJSON, err := json.Marshal(task.Labels)
+ if err != nil {
+ log.Printf("Warning: failed to marshal labels for task %s: %v", task.ID, err)
+ labelsJSON = []byte("[]")
+ }
+ _, err = s.db.Exec(`
INSERT OR REPLACE INTO tasks
(id, content, description, project_id, project_name, due_date, priority, completed, labels, url, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
@@ -344,7 +354,7 @@ func (s *Store) GetCacheMetadata(key string) (*models.CacheMetadata, error) {
WHERE key = ?
`, key).Scan(&cm.Key, &cm.LastFetch, &cm.TTLMinutes)
- if err == sql.ErrNoRows {
+ if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
if err != nil {
@@ -552,7 +562,7 @@ func (s *Store) DeleteCard(id string) error {
func (s *Store) GetSyncToken(service string) (string, error) {
var token string
err := s.db.QueryRow(`SELECT token FROM sync_tokens WHERE service = ?`, service).Scan(&token)
- if err == sql.ErrNoRows {
+ if errors.Is(err, sql.ErrNoRows) {
return "", nil
}
if err != nil {