diff --git a/api/files.go b/api/files.go index bd9296cc1..69e87b2bd 100644 --- a/api/files.go +++ b/api/files.go @@ -8,6 +8,7 @@ import ( "strings" "sync" + "github.com/aptly-dev/aptly/utils" "github.com/gin-gonic/gin" "github.com/saracen/walker" ) @@ -72,7 +73,7 @@ func apiFilesUpload(c *gin.Context) { return } - path := filepath.Join(context.UploadPath(), c.Params.ByName("dir")) + path := filepath.Join(context.UploadPath(), utils.PathEscape(c.Params.ByName("dir"))) err := os.MkdirAll(path, 0777) if err != nil { @@ -128,7 +129,7 @@ func apiFilesListFiles(c *gin.Context) { list := []string{} listLock := &sync.Mutex{} - root := filepath.Join(context.UploadPath(), c.Params.ByName("dir")) + root := filepath.Join(context.UploadPath(), utils.PathEscape(c.Params.ByName("dir"))) err := filepath.Walk(root, func(path string, _ os.FileInfo, err error) error { if err != nil { @@ -164,7 +165,7 @@ func apiFilesDeleteDir(c *gin.Context) { return } - err := os.RemoveAll(filepath.Join(context.UploadPath(), c.Params.ByName("dir"))) + err := os.RemoveAll(filepath.Join(context.UploadPath(), utils.PathEscape(c.Params.ByName("dir")))) if err != nil { AbortWithJSONError(c, 500, err) return @@ -179,12 +180,14 @@ func apiFilesDeleteFile(c *gin.Context) { return } - if !verifyPath(c.Params.ByName("name")) { + dir := utils.PathEscape(c.Params.ByName("dir")) + name := utils.PathEscape(c.Params.ByName("name")) + if !verifyPath(name) { AbortWithJSONError(c, 400, fmt.Errorf("wrong file")) return } - err := os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("name"))) + err := os.Remove(filepath.Join(context.UploadPath(), dir, name)) if err != nil { if err1, ok := err.(*os.PathError); !ok || !os.IsNotExist(err1.Err) { AbortWithJSONError(c, 500, err) diff --git a/api/publish.go b/api/publish.go index b05b43f56..6c5e8846c 100644 --- a/api/publish.go +++ b/api/publish.go @@ -9,6 +9,7 @@ import ( "github.com/aptly-dev/aptly/deb" "github.com/aptly-dev/aptly/pgp" "github.com/aptly-dev/aptly/task" + "github.com/aptly-dev/aptly/utils" "github.com/gin-gonic/gin" ) @@ -43,11 +44,10 @@ func getSigner(options *SigningOptions) (pgp.Signer, error) { return signer, nil } -// Replace '_' with '/' and double '__' with single '_', remove leading '/', remove '..' -func parseEscapedPath(path string) string { +// Replace '_' with '/' and double '__' with single '_', pathEscape +func slashEscape(path string) string { result := strings.Replace(strings.Replace(path, "_", "/", -1), "//", "_", -1) - result = strings.Replace(result, "..", "", -1) - result = strings.TrimPrefix(result, "/") + result = utils.PathEscape(result) if result == "" { result = "." } @@ -88,7 +88,7 @@ func apiPublishList(c *gin.Context) { // POST /publish/:prefix func apiPublishRepoOrSnapshot(c *gin.Context) { - param := parseEscapedPath(c.Params.ByName("prefix")) + param := slashEscape(c.Params.ByName("prefix")) storage, prefix := deb.ParsePrefix(param) var b struct { @@ -115,6 +115,8 @@ func apiPublishRepoOrSnapshot(c *gin.Context) { return } + b.Distribution = utils.PathEscape(b.Distribution) + signer, err := getSigner(&b.Signing) if err != nil { AbortWithJSONError(c, 500, fmt.Errorf("unable to initialize GPG signer: %s", err)) @@ -250,7 +252,7 @@ func apiPublishRepoOrSnapshot(c *gin.Context) { // PUT /publish/:prefix/:distribution func apiPublishUpdateSwitch(c *gin.Context) { - param := parseEscapedPath(c.Params.ByName("prefix")) + param := slashEscape(c.Params.ByName("prefix")) storage, prefix := deb.ParsePrefix(param) distribution := c.Params.ByName("distribution") @@ -375,7 +377,7 @@ func apiPublishDrop(c *gin.Context) { force := c.Request.URL.Query().Get("force") == "1" skipCleanup := c.Request.URL.Query().Get("SkipCleanup") == "1" - param := parseEscapedPath(c.Params.ByName("prefix")) + param := slashEscape(c.Params.ByName("prefix")) storage, prefix := deb.ParsePrefix(param) distribution := c.Params.ByName("distribution") diff --git a/api/repos.go b/api/repos.go index 81425f999..396c7b734 100644 --- a/api/repos.go +++ b/api/repos.go @@ -343,8 +343,8 @@ func apiReposPackageFromDir(c *gin.Context) { return } - dirParam := c.Params.ByName("dir") - fileParam := c.Params.ByName("file") + dirParam := utils.PathEscape(c.Params.ByName("dir")) + fileParam := utils.PathEscape(c.Params.ByName("file")) if fileParam != "" && !verifyPath(fileParam) { AbortWithJSONError(c, 400, fmt.Errorf("wrong file")) return @@ -620,8 +620,8 @@ func apiReposIncludePackageFromDir(c *gin.Context) { var sources []string var taskName string - dirParam := c.Params.ByName("dir") - fileParam := c.Params.ByName("file") + dirParam := utils.PathEscape(c.Params.ByName("dir")) + fileParam := utils.PathEscape(c.Params.ByName("file")) if fileParam != "" && !verifyPath(fileParam) { AbortWithJSONError(c, 400, fmt.Errorf("wrong file")) return diff --git a/utils/utils.go b/utils/utils.go index 4d4734fc7..1116ec744 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,6 +4,7 @@ package utils import ( "fmt" "os" + "strings" "golang.org/x/sys/unix" ) @@ -22,3 +23,10 @@ func DirIsAccessible(filename string) error { } return nil } + +// Remove leading '/', remove '..' +func PathEscape(path string) (result string) { + result = strings.Replace(path, "..", "", -1) + result = strings.TrimPrefix(result, "/") + return +}