security: remediate vulnerabilities and issues from codebase audit
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m15s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m15s
This commit is contained in:
parent
b60a09196a
commit
fb3f3a3393
15 changed files with 465 additions and 208 deletions
|
|
@ -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(`
|
||||
|
|
|
|||
Reference in a new issue