Skip to content

Commit

Permalink
feat(saver): test sauce file is not escaping destination
Browse files Browse the repository at this point in the history
  • Loading branch information
vesse committed Jun 6, 2024
1 parent 9081b6d commit b36b622
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
26 changes: 25 additions & 1 deletion pkg/recipe/saver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"gopkg.in/yaml.v3"
)
Expand All @@ -13,6 +14,24 @@ const (
yamlIndent int = 2
)

func IsSafePath(basePath, filePath string) error {
absBasePath, err := filepath.Abs(basePath)
if err != nil {
return fmt.Errorf("invalid base path %q: %v", basePath, err)
}

absFilePath, err := filepath.Abs(filePath)
if err != nil {
return fmt.Errorf("invalid file path %q: %v", basePath, err)
}

if !strings.HasPrefix(absFilePath, absBasePath) {
return fmt.Errorf("file path escapes destination: %q outside %q", absFilePath, absBasePath)
}

return nil
}

// Save saves recipe to given destination
func (re *Recipe) Save(dest string) error {
err := os.MkdirAll(dest, defaultFileMode)
Expand Down Expand Up @@ -197,8 +216,13 @@ func saveFileMap(files map[string]File, dest string) error {
for path, file := range files {
destPath := filepath.Join(dest, path)

err := IsSafePath(dest, destPath)
if err != nil {
return err
}

// Create file's parent directories (if not already exist)
err := os.MkdirAll(filepath.Dir(destPath), 0700)
err = os.MkdirAll(filepath.Dir(destPath), 0700)
if err != nil {
return err
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/recipe/saver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package recipe
import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/futurice/jalapeno/pkg/engine"
Expand Down Expand Up @@ -117,3 +118,37 @@ func TestSaveSauce(t *testing.T) {
}
}
}

func TestSaveSauceDoesNotWriteOutsideDest(t *testing.T) {
dir, err := os.MkdirTemp("", "jalapeno-test-saver")
if err != nil {
t.Fatalf("cannot create temp dir: %s", err)
}
defer os.RemoveAll(dir)

re := NewRecipe()
re.Name = "Test"
re.Version = "v0.0.1"
re.Templates = map[string]File{
"../foo.md": NewFile([]byte("foo")),
}

err = re.Validate()
if err != nil {
t.Fatalf("test recipe was not valid: %s", err)
}

sauce, err := re.Execute(engine.New(), nil, uuid.Must(uuid.NewV4()))
if err != nil {
t.Fatalf("recipe execution failed: %s", err)
}

err = sauce.Save(dir)
if err == nil {
t.Fatalf("should not have saved sauce")
}

if !strings.Contains(err.Error(), "file path escapes destination") {
t.Fatalf("error received was not expected: %s", err)
}
}

0 comments on commit b36b622

Please sign in to comment.