diff --git a/packages/envd/internal/api/upload.go b/packages/envd/internal/api/upload.go index 1d7c65301..c3dbca2da 100644 --- a/packages/envd/internal/api/upload.go +++ b/packages/envd/internal/api/upload.go @@ -16,6 +16,7 @@ import ( "github.com/e2b-dev/infra/packages/envd/internal/logs" "github.com/e2b-dev/infra/packages/envd/internal/permissions" + "github.com/e2b-dev/infra/packages/envd/internal/utils" ) func freeDiskSpace(path string) (free uint64, err error) { @@ -113,7 +114,12 @@ func resolvePath(part *multipart.Part, paths *UploadSuccess, u *user.User, param if params.Path != nil { pathToResolve = *params.Path } else { - pathToResolve = part.FileName() + var err error + customPart := utils.NewCustomPart(part) + pathToResolve, err = customPart.FileNameWithPath() + if err != nil { + return "", fmt.Errorf("error getting multipart custom part file name: %w", err) + } } filePath, err := permissions.ExpandAndResolve(pathToResolve, u) diff --git a/packages/envd/internal/utils/multipart.go b/packages/envd/internal/utils/multipart.go new file mode 100644 index 000000000..dc757f709 --- /dev/null +++ b/packages/envd/internal/utils/multipart.go @@ -0,0 +1,41 @@ +package utils + +import ( + "errors" + "mime" + "mime/multipart" +) + +// CustomPart is a wrapper around multipart.Part that overloads the FileName method +type CustomPart struct { + *multipart.Part +} + +// FileNameWithPath returns the filename parameter of the Part's Content-Disposition header. +// This method borrows from the original FileName method implementation but returns the full +// filename without using `filepath.Base`. +func (p *CustomPart) FileNameWithPath() (string, error) { + dispositionParams, err := p.parseContentDisposition() + if err != nil { + return "", err + } + filename, ok := dispositionParams["filename"] + if !ok { + return "", errors.New("filename not found in Content-Disposition header") + } + return filename, nil +} + +func (p *CustomPart) parseContentDisposition() (map[string]string, error) { + v := p.Header.Get("Content-Disposition") + _, dispositionParams, err := mime.ParseMediaType(v) + if err != nil { + return nil, err + } + return dispositionParams, nil +} + +// NewCustomPart creates a new CustomPart from a multipart.Part +func NewCustomPart(part *multipart.Part) *CustomPart { + return &CustomPart{Part: part} +}