Update path validation error handling

This commit is contained in:
2024-11-28 21:18:30 +01:00
parent fbb8fa3a60
commit 91489ca633
4 changed files with 70 additions and 7 deletions

View File

@@ -4,8 +4,10 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"os"
"novamd/internal/context" "novamd/internal/context"
"novamd/internal/storage"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
) )
@@ -63,10 +65,21 @@ func (h *Handler) GetFileContent() http.HandlerFunc {
filePath := chi.URLParam(r, "*") filePath := chi.URLParam(r, "*")
content, err := h.Storage.GetFileContent(ctx.UserID, ctx.Workspace.ID, filePath) content, err := h.Storage.GetFileContent(ctx.UserID, ctx.Workspace.ID, filePath)
if err != nil { if err != nil {
if storage.IsPathValidationError(err) {
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
if os.IsNotExist(err) {
http.Error(w, "Failed to read file", http.StatusNotFound) http.Error(w, "Failed to read file", http.StatusNotFound)
return return
} }
http.Error(w, "Failed to read file", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
_, err = w.Write(content) _, err = w.Write(content)
if err != nil { if err != nil {
@@ -93,6 +106,11 @@ func (h *Handler) SaveFile() http.HandlerFunc {
err = h.Storage.SaveFile(ctx.UserID, ctx.Workspace.ID, filePath, content) err = h.Storage.SaveFile(ctx.UserID, ctx.Workspace.ID, filePath, content)
if err != nil { if err != nil {
if storage.IsPathValidationError(err) {
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
http.Error(w, "Failed to save file", http.StatusInternalServerError) http.Error(w, "Failed to save file", http.StatusInternalServerError)
return return
} }
@@ -112,6 +130,16 @@ func (h *Handler) DeleteFile() http.HandlerFunc {
filePath := chi.URLParam(r, "*") filePath := chi.URLParam(r, "*")
err := h.Storage.DeleteFile(ctx.UserID, ctx.Workspace.ID, filePath) err := h.Storage.DeleteFile(ctx.UserID, ctx.Workspace.ID, filePath)
if err != nil { if err != nil {
if storage.IsPathValidationError(err) {
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
if os.IsNotExist(err) {
http.Error(w, "File not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to delete file", http.StatusInternalServerError) http.Error(w, "Failed to delete file", http.StatusInternalServerError)
return return
} }
@@ -165,12 +193,23 @@ func (h *Handler) UpdateLastOpenedFile() http.HandlerFunc {
return return
} }
// Validate the file path exists in the workspace // Validate the file path in the workspace
if requestBody.FilePath != "" { if requestBody.FilePath != "" {
if _, err := h.Storage.ValidatePath(ctx.UserID, ctx.Workspace.ID, requestBody.FilePath); err != nil { _, err := h.Storage.GetFileContent(ctx.UserID, ctx.Workspace.ID, requestBody.FilePath)
if err != nil {
if storage.IsPathValidationError(err) {
http.Error(w, "Invalid file path", http.StatusBadRequest) http.Error(w, "Invalid file path", http.StatusBadRequest)
return return
} }
if os.IsNotExist(err) {
http.Error(w, "File not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to update file", http.StatusInternalServerError)
return
}
} }
if err := h.DB.UpdateLastOpenedFile(ctx.Workspace.ID, requestBody.FilePath); err != nil { if err := h.DB.UpdateLastOpenedFile(ctx.Workspace.ID, requestBody.FilePath); err != nil {

View File

@@ -0,0 +1,24 @@
// storage/errors.go
package storage
import (
"errors"
"fmt"
)
// PathValidationError represents a path validation error (e.g., path traversal attempt)
type PathValidationError struct {
Path string
Message string
}
func (e *PathValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Message, e.Path)
}
// IsPathValidationError checks if the error is a PathValidationError
func IsPathValidationError(err error) bool {
var pathErr *PathValidationError
return err != nil && errors.As(err, &pathErr)
}

View File

@@ -27,7 +27,7 @@ func (s *Service) ValidatePath(userID, workspaceID int, path string) (string, er
// First check if the path is absolute // First check if the path is absolute
if filepath.IsAbs(path) { if filepath.IsAbs(path) {
return "", fmt.Errorf("invalid path: absolute paths not allowed") return "", &PathValidationError{Path: path, Message: "absolute paths not allowed"}
} }
// Join and clean the path // Join and clean the path
@@ -36,7 +36,7 @@ func (s *Service) ValidatePath(userID, workspaceID int, path string) (string, er
// Verify the path is still within the workspace // Verify the path is still within the workspace
if !strings.HasPrefix(cleanPath, workspacePath) { if !strings.HasPrefix(cleanPath, workspacePath) {
return "", fmt.Errorf("invalid path: outside of workspace") return "", &PathValidationError{Path: path, Message: "path traversal attempt"}
} }
return cleanPath, nil return cleanPath, nil

View File

@@ -48,7 +48,7 @@ func TestValidatePath(t *testing.T) {
path: "../../../etc/passwd", path: "../../../etc/passwd",
want: "", want: "",
wantErr: true, wantErr: true,
errContains: "outside of workspace", errContains: "path traversal attempt",
}, },
{ {
name: "absolute path attempt", name: "absolute path attempt",