Skip to content

Commit

Permalink
fix(datasources): Properly handle datasources and other URLs beginnin…
Browse files Browse the repository at this point in the history
…g with '../' (#2255)

Signed-off-by: Dave Henderson <[email protected]>
  • Loading branch information
hairyhenderson authored Nov 10, 2024
1 parent a13844c commit 53d6ca0
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 5 deletions.
34 changes: 30 additions & 4 deletions internal/datafs/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@ func (d *dsReader) readFileContent(ctx context.Context, u *url.URL, hdr http.Hea
return &content{contentType: mimeType, b: data}, nil
}

// COPIED FROM /data/datasource.go
//
// resolveURL parses the relative URL rel against base, and returns the
// resolved URL. Differs from url.ResolveReference in that query parameters are
// added. In case of duplicates, params from rel are used.
Expand Down Expand Up @@ -232,9 +230,18 @@ func resolveURL(base *url.URL, rel string) (*url.URL, error) {
// URL.ResolveReference requires (or assumes, at least) that the base is
// absolute. We want to support relative URLs too though, so we need to
// correct for that.
out := base.ResolveReference(relURL)
if out.Scheme == "" && out.Path[0] == '/' {
var out *url.URL
switch {
case rel == "":
out = base
case base.IsAbs():
out = base.ResolveReference(relURL)
case base.Scheme == "" && base.Path[0] == '/':
// absolute path, no scheme or volume
out = base.ResolveReference(relURL)
out.Path = out.Path[1:]
default:
out = resolveRelativeURL(base, relURL)
}

if base.RawQuery != "" {
Expand All @@ -248,3 +255,22 @@ func resolveURL(base *url.URL, rel string) (*url.URL, error) {

return out, nil
}

// relative path, might even involve .. or . in the path
func resolveRelativeURL(base, relURL *url.URL) *url.URL {
// first find the difference between base and what base would be if it
// were absolute
emptyURL, _ := url.Parse("")
absBase := base.ResolveReference(emptyURL)
absBase.Path = strings.TrimPrefix(absBase.Path, "/")

diff := strings.TrimSuffix(base.Path, absBase.Path)
diff = strings.TrimSuffix(diff, "/")

out := base.ResolveReference(relURL)

// now correct the path by adding the prefix back in
out.Path = diff + out.Path

return out
}
16 changes: 15 additions & 1 deletion internal/datafs/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,24 @@ func TestResolveURL(t *testing.T) {
_, err = resolveURL(mustParseURL("git+ssh://[email protected]/foo//bar"), "baz//myfile")
require.Error(t, err)

// relative urls must remain relative
// relative base URLs must remain relative
out, err = resolveURL(mustParseURL("tmp/foo.json"), "")
require.NoError(t, err)
assert.Equal(t, "tmp/foo.json", out.String())

// relative implicit file URLs without volume or scheme are OK
out, err = resolveURL(mustParseURL("/tmp/"), "foo.json")
require.NoError(t, err)
assert.Equal(t, "tmp/foo.json", out.String())

// relative base URLs in parent directories are OK
out, err = resolveURL(mustParseURL("../../tmp/foo.json"), "")
require.NoError(t, err)
assert.Equal(t, "../../tmp/foo.json", out.String())

out, err = resolveURL(mustParseURL("../../tmp/"), "sub/foo.json")
require.NoError(t, err)
assert.Equal(t, "../../tmp/sub/foo.json", out.String())
}

func TestReadFileContent(t *testing.T) {
Expand Down
33 changes: 33 additions & 0 deletions internal/tests/integration/datasources_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,36 @@ func TestDatasources_File_Directory(t *testing.T) {
withDir(tmpDir.Path()).run()
assertSuccess(t, o, e, err, "core.yaml root key: cloud")
}

func TestDatsources_File_RelativePath(t *testing.T) {
// regression test for #2230
tmpDir := fs.NewDir(t, "gomplate-inttests",
fs.WithDir("root",
fs.WithDir("foo",
fs.WithDir("bar",
fs.WithFiles(map[string]string{
".gomplate.yaml": `
context:
qux:
url: "../../baz/qux.yaml"
`,
}),
),
),
fs.WithDir("baz",
fs.WithFiles(map[string]string{
"qux.yaml": `values:
- value1
- value2
`,
}),
),
),
)
t.Cleanup(tmpDir.Remove)

o, e, err := cmd(t, "--config", ".gomplate.yaml", "-i", "{{ .qux.values }}").
withDir(tmpDir.Join("root", "foo", "bar")).run()

assertSuccess(t, o, e, err, "[value1 value2]")
}

0 comments on commit 53d6ca0

Please sign in to comment.