fix: don't split real paths on !/ in LocalPath and ContainerPath

`LocalPath` searched `FullNestedPath()` for the last `!/`, which is just `RealPath` for a file not in
an archive. A host file like `/data/Hello!/world.txt` came back as `/world.txt`, so lookups and
exclusions ran against the wrong path even with nested cataloging off. `ContainerPath` had the same
problem when `FullPath` was set to `RealPath`.

Both now cut `RealPath` off first and only look for a separator in the entry chain after it, which
can't contain `!/` since entry names with it are refused.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2026-10-21 11:23:34 -04:00
parent 2897361337
commit 99723d91dd
4 changed files with 23 additions and 9 deletions
+6 -3
View File
@@ -90,11 +90,14 @@ func (t *Traversal) FullTraversalArchivePath() string {
// ContainerPath returns the full path of the archive directly holding the file at c, or blank for a
// file on the scanned filesystem, whether or not its FullPath is set.
func ContainerPath(c file.Coordinates) string {
i := strings.LastIndex(c.FullPath, file.ArchivePathSeparator)
if i < 0 {
entries, ok := archiveEntries(c)
if !ok {
return ""
}
return c.FullPath[:i]
if i := strings.LastIndex(entries, file.ArchivePathSeparator); i >= 0 {
return c.RealPath + file.ArchivePathSeparator + entries[:i]
}
return c.RealPath
}
// archiveEntries is the "!/"-delimited chain of entry paths after RealPath, reporting false for a file
+3
View File
@@ -32,6 +32,9 @@ func TestContainerPath(t *testing.T) {
assert.Equal(t, "", ContainerPath(file.NewCoordinates("/app.war", "")))
assert.Equal(t, "", ContainerPath(file.Coordinates{RealPath: "/app.war", FullPath: "/app.war"}), "a top-level file whose FullPath is set")
assert.Equal(t, "", ContainerPath(file.Coordinates{RealPath: "/odd!/name"}), "a separator in a top-level real path is not an archive boundary")
assert.Equal(t, "", ContainerPath(file.Coordinates{RealPath: "/odd!/name", FullPath: "/odd!/name"}), "nor when FullPath is set")
assert.Equal(t, "/odd!/name.zip", ContainerPath(locationIn("/odd!/name.zip", "a.txt").Coordinates))
assert.Equal(t, "/odd!/name.zip!/b.jar", ContainerPath(locationIn("/odd!/name.zip", "b.jar!/a.txt").Coordinates))
assert.Equal(t, "/app.war", ContainerPath(locationIn("/app.war", "WEB-INF/lib/dep.jar").Coordinates))
assert.Equal(t, "/app.war!/WEB-INF/lib/dep.jar",
ContainerPath(locationIn("/app.war", "WEB-INF/lib/dep.jar!/META-INF/MANIFEST.MF").Coordinates))
+10 -6
View File
@@ -30,14 +30,18 @@ func (c Coordinates) FullNestedPath() string {
return c.FullPath
}
// LocalPath is the rooted path within the resolver holding the file: the segment of FullNestedPath after
// the last archive separator, keeping the separator's slash, or RealPath for a file not within an archive.
// LocalPath is the rooted path within the resolver holding the file: the last entry path in FullPath, or
// RealPath for a file not within an archive. RealPath is cut off before looking for a separator, so a
// "!/" in a real path is never taken for an archive boundary.
func (c Coordinates) LocalPath() string {
fullPath := c.FullNestedPath()
if i := strings.LastIndex(fullPath, ArchivePathSeparator); i >= 0 {
return fullPath[i+len(ArchivePathSeparator)-1:]
entries, ok := strings.CutPrefix(c.FullPath, c.RealPath+ArchivePathSeparator)
if !ok {
return c.RealPath
}
return fullPath
if i := strings.LastIndex(entries, ArchivePathSeparator); i >= 0 {
entries = entries[i+len(ArchivePathSeparator):]
}
return "/" + entries
}
func NewCoordinates(realPath, fsID string) Coordinates {
+4
View File
@@ -106,4 +106,8 @@ func TestCoordinates_LocalPath(t *testing.T) {
assert.Equal(t, "/a.jar", NewCoordinates("/a.jar", "").LocalPath())
assert.Equal(t, "/a.jar", Coordinates{RealPath: "/a.jar"}.LocalPath())
assert.Equal(t, "/META-INF/MANIFEST.MF", Coordinates{RealPath: "/a.war", FullPath: "/a.war!/lib/b.jar!/META-INF/MANIFEST.MF"}.LocalPath())
assert.Equal(t, "/data/Hello!/world.txt", NewCoordinates("/data/Hello!/world.txt", "").LocalPath(), "a separator in a real path is not an archive boundary")
assert.Equal(t, "/data/Hello!/world.txt", Coordinates{RealPath: "/data/Hello!/world.txt", FullPath: "/data/Hello!/world.txt"}.LocalPath())
assert.Equal(t, "/x.txt", Coordinates{RealPath: "/data/Hello!/a.zip", FullPath: "/data/Hello!/a.zip!/x.txt"}.LocalPath())
assert.Equal(t, "/x.txt", Coordinates{RealPath: "/data/Hello!/a.zip", FullPath: "/data/Hello!/a.zip!/b.jar!/x.txt"}.LocalPath())
}