diff --git a/backend/archive/zip/zip.go b/backend/archive/zip/zip.go index ec9ba77d7..6af2d21d7 100644 --- a/backend/archive/zip/zip.go +++ b/backend/archive/zip/zip.go @@ -146,8 +146,10 @@ func (f *Fs) readZip() (singleObject bool, err error) { } remote = path.Join(f.prefix, remote) if f.root != "" { - // Ignore all files outside the root - if !strings.HasPrefix(remote, f.root) { + // Ignore all files outside the root, requiring a path + // boundary so that root "foo" does not also match a + // sibling entry such as "foobar" + if remote != f.root && !strings.HasPrefix(remote, f.root+"/") { continue } if remote == f.root { diff --git a/backend/archive/zip/zip_internal_test.go b/backend/archive/zip/zip_internal_test.go index f0bb3bb03..5b7a0252d 100644 --- a/backend/archive/zip/zip_internal_test.go +++ b/backend/archive/zip/zip_internal_test.go @@ -83,3 +83,24 @@ func TestReadZipSlip(t *testing.T) { } assert.Equal(t, []string{"good.txt"}, remotes) } + +// Mounting with a non-empty root must only expose entries within that +// root directory, not sibling directories that merely share a name +// prefix (root "foo" must not match "foobar"). +func TestReadZipRootBoundary(t *testing.T) { + ctx := context.Background() + dir := t.TempDir() + name := writeZip(t, dir, "test.zip", + "foo/a.txt", + "foobar/b.txt", + ) + + localFs, err := cache.Get(ctx, dir) + require.NoError(t, err) + + f, err := New(ctx, localFs, name, "", "foo") + require.NoError(t, err) + + remotes := allRemotes(t, f) + assert.Equal(t, []string{"a.txt"}, remotes) +}