Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Commit

Permalink
add legal path tests to other asset types
Browse files Browse the repository at this point in the history
docker, dockerlayer, github, helm, inline, local and web

terraform, amazoneks, azureaks and googlegke assets all rely on the inline type internally
  • Loading branch information
laverya committed Feb 23, 2019
1 parent 0a989d0 commit 651bad5
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 19 deletions.
7 changes: 7 additions & 0 deletions pkg/lifecycle/render/docker/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/replicatedhq/ship/pkg/images"
"github.com/replicatedhq/ship/pkg/lifecycle/render/root"
"github.com/replicatedhq/ship/pkg/templates"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/viper"
)

Expand Down Expand Up @@ -96,6 +98,11 @@ func (p *DefaultStep) Execute(
}
destIsDockerURL := destinationURL.Scheme == "docker"
if !destIsDockerURL {
err = util.IsLegalPath(dest)
if err != nil {
return errors.Wrap(err, "find docker image dest")
}

basePath := filepath.Dir(dest)
debug.Log("event", "mkdirall.attempt", "dest", dest, "basePath", basePath)
if err := rootFs.MkdirAll(basePath, 0755); err != nil {
Expand Down
7 changes: 7 additions & 0 deletions pkg/lifecycle/render/dockerlayer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/replicatedhq/ship/pkg/api"
"github.com/replicatedhq/ship/pkg/lifecycle/render/docker"
"github.com/replicatedhq/ship/pkg/lifecycle/render/root"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/afero"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -66,6 +68,11 @@ func (u *Unpacker) Execute(
return errors.Wrap(err, "resolve unpack paths")
}

err = util.IsLegalPath(basePath)
if err != nil {
return errors.Wrap(err, "write github asset")
}

debug.Log(
"event", "execute",
"savePath", savePath,
Expand Down
13 changes: 3 additions & 10 deletions pkg/lifecycle/render/github/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/replicatedhq/ship/pkg/specs/gogetter"
"github.com/replicatedhq/ship/pkg/state"
"github.com/replicatedhq/ship/pkg/templates"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/afero"
"github.com/spf13/viper"
Expand Down Expand Up @@ -251,10 +252,6 @@ func getDestPath(githubPath string, asset api.GitHubAsset, builder *templates.Bu
return "", errors.Wrapf(err, "get destination directory from %q", asset.Dest)
}

if filepath.IsAbs(destDir) {
return "", fmt.Errorf("cannot write to an absolute path: %s", destDir)
}

if stripPath {
// remove asset.Path's directory from the beginning of githubPath
sourcePathDir := filepath.ToSlash(filepath.Dir(asset.Path)) + "/"
Expand All @@ -269,13 +266,9 @@ func getDestPath(githubPath string, asset api.GitHubAsset, builder *templates.Bu

combinedPath := filepath.Join(destDir, githubPath)

relPath, err := filepath.Rel(".", combinedPath)
err = util.IsLegalPath(combinedPath)
if err != nil {
return "", errors.Wrap(err, "find relative path to dest")
}

if strings.Contains(relPath, "..") {
return "", fmt.Errorf("cannot write to a path that is a parent of the working dir: %s", relPath)
return "", errors.Wrap(err, "write github asset")
}

return combinedPath, nil
Expand Down
5 changes: 5 additions & 0 deletions pkg/lifecycle/render/helm/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ func (f *LocalTemplater) cleanUpAndOutputRenderedFiles(
tempRenderedChartTemplatesDir := path.Join(tempRenderedChartDir, "templates")
tempRenderedSubChartsDir := path.Join(tempRenderedChartDir, subChartsDirName)

err := util.IsLegalPath(asset.Dest)
if err != nil {
return errors.Wrap(err, "write helm asset")
}

if f.Viper.GetBool("rm-asset-dest") {
debug.Log("event", "baseDir.rm", "path", asset.Dest)
if err := f.FS.RemoveAll(asset.Dest); err != nil {
Expand Down
40 changes: 32 additions & 8 deletions pkg/lifecycle/render/inline/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/replicatedhq/ship/pkg/api"
"github.com/replicatedhq/ship/pkg/lifecycle/render/root"
"github.com/replicatedhq/ship/pkg/templates"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/viper"
)

Expand Down Expand Up @@ -65,29 +67,51 @@ func (r *LocalRenderer) Execute(
return errors.Wrap(err, "init builder")
}

built, err := builder.String(asset.Contents)
builtAsset, err := templateInline(builder, asset)
if err != nil {
return errors.Wrap(err, "building contents")
}

err = util.IsLegalPath(builtAsset.Dest)
if err != nil {
return errors.Wrap(err, "write inline asset")
}

basePath := filepath.Dir(asset.Dest)
debug.Log("event", "mkdirall.attempt", "dest", asset.Dest, "basePath", basePath)
debug.Log("event", "mkdirall.attempt", "dest", builtAsset.Dest, "basePath", basePath)
if err := rootFs.MkdirAll(basePath, 0755); err != nil {
debug.Log("event", "mkdirall.fail", "err", err, "dest", asset.Dest, "basePath", basePath)
return errors.Wrapf(err, "write directory to %s", asset.Dest)
debug.Log("event", "mkdirall.fail", "err", err, "dest", builtAsset.Dest, "basePath", basePath)
return errors.Wrapf(err, "write directory to %s", builtAsset.Dest)
}

mode := os.FileMode(0644)
if asset.Mode != os.FileMode(0) {
if builtAsset.Mode != os.FileMode(0) {
debug.Log("event", "applying override permissions")
mode = asset.Mode
mode = builtAsset.Mode
}

if err := rootFs.WriteFile(asset.Dest, []byte(built), mode); err != nil {
if err := rootFs.WriteFile(builtAsset.Dest, []byte(builtAsset.Contents), mode); err != nil {
debug.Log("event", "execute.fail", "err", err)
return errors.Wrapf(err, "Write inline asset to %s", asset.Dest)
return errors.Wrapf(err, "Write inline asset to %s", builtAsset.Dest)
}
return nil

}
}

func templateInline(builder *templates.Builder, asset api.InlineAsset) (api.InlineAsset, error) {
builtAsset := asset
var err error

builtAsset.Contents, err = builder.String(asset.Contents)
if err != nil {
return builtAsset, errors.Wrap(err, "building contents")
}

builtAsset.Dest, err = builder.String(asset.Dest)
if err != nil {
return builtAsset, errors.Wrap(err, "building dest")
}

return builtAsset, nil
}
53 changes: 52 additions & 1 deletion pkg/lifecycle/render/inline/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func TestInlineRender(t *testing.T) {
templateContext map[string]interface{}
configGroups []libyaml.ConfigGroup
expect map[string]interface{}
expectErr bool
}{
{
name: "happy path",
Expand All @@ -40,6 +41,52 @@ func TestInlineRender(t *testing.T) {
templateContext: map[string]interface{}{},
configGroups: []libyaml.ConfigGroup{},
},
{
name: "templated dest path",
asset: api.InlineAsset{
Contents: "hello!",
AssetShared: api.AssetShared{
Dest: "{{repl if true}}foo.txt{{repl else}}notfoo.txt{{repl end}}",
},
},
expect: map[string]interface{}{
"foo.txt": "hello!",
},

meta: api.ReleaseMetadata{},
templateContext: map[string]interface{}{},
configGroups: []libyaml.ConfigGroup{},
},
{
name: "absolute dest path",
asset: api.InlineAsset{
Contents: "hello!",
AssetShared: api.AssetShared{
Dest: "/bin/runc",
},
},
expect: map[string]interface{}{},

meta: api.ReleaseMetadata{},
templateContext: map[string]interface{}{},
configGroups: []libyaml.ConfigGroup{},
expectErr: true,
},
{
name: "parent dir dest path",
asset: api.InlineAsset{
Contents: "hello!",
AssetShared: api.AssetShared{
Dest: "../../../bin/runc",
},
},
expect: map[string]interface{}{},

meta: api.ReleaseMetadata{},
templateContext: map[string]interface{}{},
configGroups: []libyaml.ConfigGroup{},
expectErr: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand All @@ -65,7 +112,11 @@ func TestInlineRender(t *testing.T) {
test.templateContext,
test.configGroups,
)(context.Background())
req.NoError(err)
if !test.expectErr {
req.NoError(err)
} else {
req.Error(err)
}

for filename, expectContents := range test.expect {
contents, err := rootFs.ReadFile(filename)
Expand Down
12 changes: 12 additions & 0 deletions pkg/lifecycle/render/local/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/pkg/errors"
"github.com/replicatedhq/libyaml"
"github.com/replicatedhq/ship/pkg/api"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/afero"
)

Expand Down Expand Up @@ -47,6 +49,16 @@ func (r *LocalRenderer) Execute(
return func(ctx context.Context) error {
debug := level.Debug(log.With(r.Logger, "step.type", "render", "render.phase", "execute", "asset.type", "local"))

err := util.IsLegalPath(asset.Dest)
if err != nil {
return errors.Wrap(err, "local asset dest")
}

err = util.IsLegalPath(asset.Path)
if err != nil {
return errors.Wrap(err, "local asset path")
}

if err := r.Fs.MkdirAll(filepath.Dir(asset.Dest), 0777); err != nil {
return errors.Wrapf(err, "mkdir %s", asset.Dest)
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/lifecycle/render/web/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/replicatedhq/ship/pkg/api"
"github.com/replicatedhq/ship/pkg/lifecycle/render/root"
"github.com/replicatedhq/ship/pkg/templates"
"github.com/replicatedhq/ship/pkg/util"

"github.com/spf13/afero"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -82,6 +84,11 @@ func (p *DefaultStep) Execute(
return errors.Wrapf(err, "Build web asset")
}

err = util.IsLegalPath(built.Dest)
if err != nil {
return errors.Wrap(err, "write web asset")
}

basePath := filepath.Dir(asset.Dest)
debug.Log("event", "mkdirall.attempt", "root", rootFs.RootPath, "dest", built.Dest, "basePath", basePath)
if err := rootFs.MkdirAll(basePath, 0755); err != nil {
Expand Down
30 changes: 30 additions & 0 deletions pkg/lifecycle/render/web/step_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,36 @@ func TestWebStep(t *testing.T) {
ExpectFiles: map[string]interface{}{},
ExpectedErr: errors.New("Get web asset from http://foo.bar: received response with status 500"),
},
{
Name: "illegal dest path",
Asset: api.WebAsset{
AssetShared: api.AssetShared{
Dest: "/bin/runc",
},
URL: "http://foo.bar",
},
RegisterResponders: func() {
httpmock.RegisterResponder("GET", "http://foo.bar",
httpmock.NewStringResponder(200, "hi from foo.bar"))
},
ExpectFiles: map[string]interface{}{},
ExpectedErr: errors.Wrap(errors.New("cannot write to an absolute path: /bin/runc"), "write web asset"),
},
{
Name: "illegal dest path",
Asset: api.WebAsset{
AssetShared: api.AssetShared{
Dest: "../../../bin/runc",
},
URL: "http://foo.bar",
},
RegisterResponders: func() {
httpmock.RegisterResponder("GET", "http://foo.bar",
httpmock.NewStringResponder(200, "hi from foo.bar"))
},
ExpectFiles: map[string]interface{}{},
ExpectedErr: errors.Wrap(errors.New("cannot write to a path that is a parent of the working dir: ../../../bin/runc"), "write web asset"),
},
}

for _, test := range tests {
Expand Down
28 changes: 28 additions & 0 deletions pkg/util/legal_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package util

import (
"fmt"
"path/filepath"
"strings"

"github.com/pkg/errors"
)

// IsLegalPath checks if the provided path is a relative path within the current working directory. If it is not, it returns an error.
func IsLegalPath(path string) error {

if filepath.IsAbs(path) {
return fmt.Errorf("cannot write to an absolute path: %s", path)
}

relPath, err := filepath.Rel(".", path)
if err != nil {
return errors.Wrap(err, "find relative path to dest")
}

if strings.Contains(relPath, "..") {
return fmt.Errorf("cannot write to a path that is a parent of the working dir: %s", relPath)
}

return nil
}
39 changes: 39 additions & 0 deletions pkg/util/legal_path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package util

import "testing"

func TestIsLegalPath(t *testing.T) {
tests := []struct {
name string
path string
wantErr bool
}{
{
name: "relative path",
path: "./happy/path",
wantErr: false,
},
{
name: "absolute path",
path: "/unhappy/path",
wantErr: true,
},
{
name: "relative parent path",
path: "../../unhappy/path",
wantErr: true,
},
{
name: "embedded relative parent path",
path: "./happy/../../../unhappy/path",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := IsLegalPath(tt.path); (err != nil) != tt.wantErr {
t.Errorf("IsLegalPath() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

0 comments on commit 651bad5

Please sign in to comment.