security: remediate vulnerabilities and issues from codebase audit
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m15s

This commit is contained in:
Elijah 2026-05-23 10:04:08 -07:00
parent b60a09196a
commit fb3f3a3393
15 changed files with 465 additions and 208 deletions

View file

@ -3,10 +3,13 @@ package handlers
import (
"encoding/base64"
"fmt"
"mime"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
"git.elijahkuntz.com/Elijah/drive/config"
"git.elijahkuntz.com/Elijah/drive/database"
@ -19,6 +22,9 @@ type WebDAVHandler struct {
Handler *webdav.Handler
DB *database.DB
Config *config.Config
authCache map[string]time.Time
cacheMu sync.RWMutex
}
func NewWebDAVHandler(db *database.DB, cfg *config.Config) *WebDAVHandler {
@ -39,9 +45,10 @@ func NewWebDAVHandler(db *database.DB, cfg *config.Config) *WebDAVHandler {
}
return &WebDAVHandler{
Handler: h,
DB: db,
Config: cfg,
Handler: h,
DB: db,
Config: cfg,
authCache: make(map[string]time.Time),
}
}
@ -63,19 +70,33 @@ func (h *WebDAVHandler) Handle(c *fiber.Ctx) error {
}
if hasBasicAuth {
// Verify basic auth against DB
var storedHash string
err := h.DB.QueryRow("SELECT password_hash FROM users WHERE username = ?", user).Scan(&storedHash)
if err != nil {
c.Set("WWW-Authenticate", `Basic realm="Drive WebDAV"`)
return c.SendStatus(fiber.StatusUnauthorized)
}
cacheKey := user + ":" + pass
// In a real implementation we would compare the Argon2 hash here.
// For simplicity, we assume the frontend uses an app password or we verify.
// Note: Argon2 requires full verification which is slow on every WebDAV request.
// It's recommended to implement an App Password system for WebDAV.
_ = pass
h.cacheMu.RLock()
expireTime, ok := h.authCache[cacheKey]
h.cacheMu.RUnlock()
if !ok || time.Now().After(expireTime) {
// Verify basic auth against DB
var storedHash string
err := h.DB.QueryRow("SELECT password_hash FROM users WHERE username = ?", user).Scan(&storedHash)
if err != nil || !verifyPassword(pass, storedHash) {
c.Set("WWW-Authenticate", `Basic realm="Drive WebDAV"`)
return c.SendStatus(fiber.StatusUnauthorized)
}
// Cache successful auth for 5 minutes
h.cacheMu.Lock()
h.authCache[cacheKey] = time.Now().Add(5 * time.Minute)
// Simple cache cleanup
for k, v := range h.authCache {
if time.Now().After(v) {
delete(h.authCache, k)
}
}
h.cacheMu.Unlock()
}
} else if c.Locals("userID") == nil {
c.Set("WWW-Authenticate", `Basic realm="Drive WebDAV"`)
return c.SendStatus(fiber.StatusUnauthorized)
@ -107,11 +128,11 @@ func (h *WebDAVHandler) syncToDB(relPath string) {
checksum, _ := generateChecksum(fullPath) // Reusing the helper from files.go
cleanPath := filepath.ToSlash(relPath)
// Default mime type
mimeType := "application/octet-stream"
if ext := filepath.Ext(cleanPath); ext != "" {
// Go's built-in mime types
// Note: mime.TypeByExtension is handled by the DB trigger or just simple mapping
if m := mime.TypeByExtension(ext); m != "" {
mimeType = m
}
}
h.DB.Exec(`