From 78c453095626c7d2dde661ecc163f91614a9aebf Mon Sep 17 00:00:00 2001 From: Dmitry Salakhov Date: Mon, 30 Aug 2021 17:14:44 +1200 Subject: [PATCH] fix(stacks): allow root based compose file paths (#5506) --- api/exec/compose_stack.go | 30 +++++++++++++++++++++++++++--- api/exec/compose_stack_test.go | 18 ++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/api/exec/compose_stack.go b/api/exec/compose_stack.go index 36283c6d9..465862047 100644 --- a/api/exec/compose_stack.go +++ b/api/exec/compose_stack.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path" + "path/filepath" "regexp" "strings" @@ -56,7 +57,7 @@ func (w *ComposeStackManager) Up(stack *portainer.Stack, endpoint *portainer.End return errors.Wrap(err, "failed to create env file") } - filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...) + filePaths := getStackFiles(stack) _, err = w.wrapper.Up(filePaths, stack.ProjectPath, url, stack.Name, envFilePath, w.configPath) return errors.Wrap(err, "failed to deploy a stack") } @@ -71,8 +72,7 @@ func (w *ComposeStackManager) Down(stack *portainer.Stack, endpoint *portainer.E defer proxy.Close() } - filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...) - + filePaths := getStackFiles(stack) _, err = w.wrapper.Down(filePaths, stack.ProjectPath, url, stack.Name) return err } @@ -115,3 +115,27 @@ func createEnvFile(stack *portainer.Stack) (string, error) { return "stack.env", nil } + +// getStackFiles returns list of stack's confile file paths. +// items in the list would be sanitized according to following criterias: +// 1. no empty paths +// 2. no "../xxx" paths that are trying to escape stack folder +// 3. no dir paths +// 4. root paths would be made relative +func getStackFiles(stack *portainer.Stack) []string { + paths := make([]string, 0, len(stack.AdditionalFiles)+1) + + for _, p := range append([]string{stack.EntryPoint}, stack.AdditionalFiles...) { + if strings.HasPrefix(p, "/") { + p = `.` + p + } + + if p == `` || p == `.` || strings.HasPrefix(p, `..`) || strings.HasSuffix(p, string(filepath.Separator)) { + continue + } + + paths = append(paths, p) + } + + return paths +} diff --git a/api/exec/compose_stack_test.go b/api/exec/compose_stack_test.go index c61285ebd..0b5dec2a3 100644 --- a/api/exec/compose_stack_test.go +++ b/api/exec/compose_stack_test.go @@ -64,3 +64,21 @@ func Test_createEnvFile(t *testing.T) { }) } } + +func Test_getStackFiles(t *testing.T) { + stack := &portainer.Stack{ + EntryPoint: "./file", // picks entry point + AdditionalFiles: []string{ + ``, // ignores empty string + `.`, // ignores . + `..`, // ignores .. + `./dir/`, // ignrores paths that end with trailing / + `/with-root-prefix`, // replaces "root" based paths with relative + `./relative`, // keeps relative paths + `../escape`, // prevents dir escape + }, + } + + filePaths := getStackFiles(stack) + assert.ElementsMatch(t, filePaths, []string{`./file`, `./with-root-prefix`, `./relative`}) +}