mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
Update path validation error handling
This commit is contained in:
@@ -4,8 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"novamd/internal/context"
|
||||
"novamd/internal/storage"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
@@ -63,10 +65,21 @@ func (h *Handler) GetFileContent() http.HandlerFunc {
|
||||
filePath := chi.URLParam(r, "*")
|
||||
content, err := h.Storage.GetFileContent(ctx.UserID, ctx.Workspace.ID, filePath)
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to read file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
_, err = w.Write(content)
|
||||
if err != nil {
|
||||
@@ -93,6 +106,11 @@ func (h *Handler) SaveFile() http.HandlerFunc {
|
||||
|
||||
err = h.Storage.SaveFile(ctx.UserID, ctx.Workspace.ID, filePath, content)
|
||||
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)
|
||||
return
|
||||
}
|
||||
@@ -112,6 +130,16 @@ func (h *Handler) DeleteFile() http.HandlerFunc {
|
||||
filePath := chi.URLParam(r, "*")
|
||||
err := h.Storage.DeleteFile(ctx.UserID, ctx.Workspace.ID, filePath)
|
||||
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)
|
||||
return
|
||||
}
|
||||
@@ -165,12 +193,23 @@ func (h *Handler) UpdateLastOpenedFile() http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the file path exists in the workspace
|
||||
// Validate the file path in the workspace
|
||||
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)
|
||||
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 {
|
||||
|
||||
24
server/internal/storage/errors.go
Normal file
24
server/internal/storage/errors.go
Normal 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)
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func (s *Service) ValidatePath(userID, workspaceID int, path string) (string, er
|
||||
|
||||
// First check if the path is absolute
|
||||
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
|
||||
@@ -36,7 +36,7 @@ func (s *Service) ValidatePath(userID, workspaceID int, path string) (string, er
|
||||
|
||||
// Verify the path is still within the workspace
|
||||
if !strings.HasPrefix(cleanPath, workspacePath) {
|
||||
return "", fmt.Errorf("invalid path: outside of workspace")
|
||||
return "", &PathValidationError{Path: path, Message: "path traversal attempt"}
|
||||
}
|
||||
|
||||
return cleanPath, nil
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestValidatePath(t *testing.T) {
|
||||
path: "../../../etc/passwd",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
errContains: "outside of workspace",
|
||||
errContains: "path traversal attempt",
|
||||
},
|
||||
{
|
||||
name: "absolute path attempt",
|
||||
|
||||
Reference in New Issue
Block a user