diff --git a/api/internal/localizer/localizer.go b/api/internal/localizer/localizer.go index 71de9dd6f..c5c7783c4 100644 --- a/api/internal/localizer/localizer.go +++ b/api/internal/localizer/localizer.go @@ -381,7 +381,7 @@ func (lc *localizer) localizeFileWithContent(path string, content []byte) (strin // 2. avoid paths that temporarily traverse outside the current root, // i.e. ../../../scope/target/current-root. The localized file will be surrounded by // different directories than its source, and so an uncleaned path may no longer be valid. - locPath = cleanFilePath(lc.fSys, lc.root, path) + locPath = cleanedRelativePath(lc.fSys, lc.root, path) } absPath := filepath.Join(lc.dst, locPath) if err := lc.fSys.MkdirAll(filepath.Dir(absPath)); err != nil { diff --git a/api/internal/localizer/localizer_test.go b/api/internal/localizer/localizer_test.go index 9bc672f3f..ef61819e3 100644 --- a/api/internal/localizer/localizer_test.go +++ b/api/internal/localizer/localizer_test.go @@ -296,8 +296,10 @@ func TestLocalizeFileCleaned(t *testing.T) { kind: Kustomization patches: - path: ../gamma/../../../alpha/beta/./gamma/patch.yaml +- path: /alpha/beta/../beta/./gamma/patch2.yaml `, - "patch.yaml": podConfiguration, + "patch.yaml": podConfiguration, + "patch2.yaml": podConfiguration, } expected, actual := makeFileSystems(t, "/alpha/beta/gamma", kustAndPatch) @@ -307,8 +309,10 @@ patches: kind: Kustomization patches: - path: patch.yaml +- path: patch2.yaml `, - "patch.yaml": podConfiguration, + "patch.yaml": podConfiguration, + "patch2.yaml": podConfiguration, }) checkFSys(t, expected, actual) } @@ -1194,19 +1198,40 @@ func TestLocalizeResources(t *testing.T) { kind: Kustomization resources: - pod.yaml +- /a/b/pod2.yaml - ../../alpha `, - "pod.yaml": podConfiguration, + "pod.yaml": podConfiguration, + "pod2.yaml": podConfiguration, "../../alpha/kustomization.yaml": `apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namePrefix: my- `, } - expected, actual := makeFileSystems(t, "/a/b", kustAndResources) - checkRun(t, actual, "/a/b", "/", "/localized-b") - addFiles(t, expected, "/localized-b/a/b", kustAndResources) - checkFSys(t, expected, actual) + // Absolute path of `/a/b/pod2.yaml` is expected to be converted to a path + // relative to the kustomization root. + expectedKustAndResources := map[string]string{ + "kustomization.yaml": `apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- pod.yaml +- pod2.yaml +- ../../alpha +`, + "pod.yaml": podConfiguration, + "pod2.yaml": podConfiguration, + "../../alpha/kustomization.yaml": `apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namePrefix: my- +`, + } + + expectedFs, actualFs := makeFileSystems(t, "/a/b", kustAndResources) + + checkRun(t, actualFs, "/a/b", "/", "/localized-b") + addFiles(t, expectedFs, "/localized-b/a/b", expectedKustAndResources) + checkFSys(t, expectedFs, actualFs) } func TestLocalizePathError(t *testing.T) { diff --git a/api/internal/localizer/locloader.go b/api/internal/localizer/locloader.go index 1d1072bd1..33a03b4fc 100644 --- a/api/internal/localizer/locloader.go +++ b/api/internal/localizer/locloader.go @@ -89,11 +89,8 @@ func (ll *Loader) Load(path string) ([]byte, error) { if err != nil { return nil, errors.WrapPrefixf(err, "invalid file reference") } - if filepath.IsAbs(path) { - return nil, errors.Errorf("absolute paths not yet supported in alpha: file path %q is absolute", path) - } if !loader.IsRemoteFile(path) && ll.local { - cleanPath := cleanFilePath(ll.fSys, filesys.ConfirmedDir(ll.Root()), path) + cleanPath := cleanedRelativePath(ll.fSys, filesys.ConfirmedDir(ll.Root()), path) cleanAbs := filepath.Join(ll.Root(), cleanPath) dir := filesys.ConfirmedDir(filepath.Dir(cleanAbs)) // target cannot reference newDir, as this load would've failed prior to localize; diff --git a/api/internal/localizer/locloader_test.go b/api/internal/localizer/locloader_test.go index 97bc266c0..98f171ac3 100644 --- a/api/internal/localizer/locloader_test.go +++ b/api/internal/localizer/locloader_test.go @@ -276,11 +276,11 @@ func TestLoadFails(t *testing.T) { checkNewLoader(req, ldr, &args, "/a", "/a", "/a/newDir", fSys) cases := map[string]string{ - "absolute path": "/a/pod.yaml", - "directory": "b", - "non-existent file": "kubectl.yaml", - "file outside root": "../alpha/beta/gamma/delta/deployment.yaml", - "inside dst": "newDir/pod.yaml", + "directory": "b", + "non-existent file": "kubectl.yaml", + "file outside root": "../alpha/beta/gamma/delta/deployment.yaml", + "inside dst": "newDir/pod.yaml", + "winding inside dst": "/a/test/../newDir/pod.yaml", } for name, file := range cases { file := file @@ -291,8 +291,6 @@ func TestLoadFails(t *testing.T) { ldr, _, err := NewLoader("./a/../a", "/a/../a", "/a/newDir", fSys) req.NoError(err) - req.NoError(fSys.WriteFile("/a/newDir/pod.yaml", []byte(podConfiguration))) - _, err = ldr.Load(file) req.Error(err) }) diff --git a/api/internal/localizer/util.go b/api/internal/localizer/util.go index 1a5151118..1ec077875 100644 --- a/api/internal/localizer/util.go +++ b/api/internal/localizer/util.go @@ -112,9 +112,13 @@ func hasRef(repoURL string) bool { return repoSpec.Ref != "" } -// cleanFilePath returns file cleaned, where file is a relative path to root on fSys -func cleanFilePath(fSys filesys.FileSystem, root filesys.ConfirmedDir, file string) string { - abs := root.Join(file) +// cleanedRelativePath returns a cleaned relative path of file to root on fSys +func cleanedRelativePath(fSys filesys.FileSystem, root filesys.ConfirmedDir, file string) string { + abs := file + if !filepath.IsAbs(file) { + abs = root.Join(file) + } + dir, f, err := fSys.CleanedAbs(abs) if err != nil { log.Fatalf("cannot clean validated file path %q: %s", abs, err) diff --git a/api/internal/localizer/util_test.go b/api/internal/localizer/util_test.go index 91fca64ce..3d29b0384 100644 --- a/api/internal/localizer/util_test.go +++ b/api/internal/localizer/util_test.go @@ -301,3 +301,26 @@ func TestLocRootPath_SymlinkPath(t *testing.T) { require.NoError(t, err) require.Equal(t, expected, actual) } + +func TestCleanedRelativePath(t *testing.T) { + fSys := filesys.MakeFsInMemory() + require.NoError(t, fSys.MkdirAll("/root/test")) + require.NoError(t, fSys.WriteFile("/root/test/file.yaml", []byte(""))) + require.NoError(t, fSys.WriteFile("/root/filetwo.yaml", []byte(""))) + + // Absolute path is cleaned to relative path + cleanedPath := cleanedRelativePath(fSys, "/root/", "/root/test/file.yaml") + require.Equal(t, "test/file.yaml", cleanedPath) + + // Winding absolute path is cleaned to relative path + cleanedPath = cleanedRelativePath(fSys, "/root/", "/root/test/../filetwo.yaml") + require.Equal(t, "filetwo.yaml", cleanedPath) + + // Already clean relative path stays the same + cleanedPath = cleanedRelativePath(fSys, "/root/", "test/file.yaml") + require.Equal(t, "test/file.yaml", cleanedPath) + + // Winding relative path is cleaned + cleanedPath = cleanedRelativePath(fSys, "/root/", "test/../filetwo.yaml") + require.Equal(t, "filetwo.yaml", cleanedPath) +}