diff --git a/syft/file/location.go b/syft/file/location.go index 18651ee71..14256ffa3 100644 --- a/syft/file/location.go +++ b/syft/file/location.go @@ -205,12 +205,13 @@ func NewVirtualLocationFromDirectory(responsePath, responseAccessPath string, re } } -// Path is the path this location was accessed by, or its local path (see Coordinates.LocalPath). +// Path is the path this location was accessed by, or its full path (see Coordinates.FullNestedPath). For a +// file within an archive both are full paths, so use LocalPath for path arithmetic against the resolver. func (l Location) Path() string { if l.AccessPath != "" { return l.AccessPath } - return l.LocalPath() + return l.FullNestedPath() } func (l Location) String() string { diff --git a/syft/file/location_test.go b/syft/file/location_test.go index 34074c7fa..5302c951a 100644 --- a/syft/file/location_test.go +++ b/syft/file/location_test.go @@ -111,3 +111,12 @@ func TestCoordinates_LocalPath(t *testing.T) { 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()) } + +func TestLocation_Path(t *testing.T) { + nested := Coordinates{RealPath: "/a.war", FullPath: "/a.war!/lib/b.jar!/META-INF/MANIFEST.MF"} + assert.Equal(t, "/a.jar", NewLocation("/a.jar").Path()) + assert.Equal(t, "/link", NewVirtualLocation("/a.jar", "/link").Path()) + assert.Equal(t, nested.FullPath, NewLocationFromCoordinates(nested).Path()) + assert.Equal(t, nested.FullPath, Location{LocationData: LocationData{Coordinates: nested}}.Path(), "a nested location without an access path is still full") + assert.Equal(t, nested.FullPath, NewVirtualLocationFromCoordinates(nested, nested.FullPath).Path()) +} diff --git a/syft/pkg/cataloger/java/internal/maven/resolver.go b/syft/pkg/cataloger/java/internal/maven/resolver.go index 67f3b25bb..36192fd8c 100644 --- a/syft/pkg/cataloger/java/internal/maven/resolver.go +++ b/syft/pkg/cataloger/java/internal/maven/resolver.go @@ -647,7 +647,8 @@ func (r *Resolver) findParentPomByRelativePath(ctx context.Context, pom *Project if relativePath == "" { return nil } - p := pomLocation.Path() + // relative to the pom within its own archive, so "../" can't clean past the archive boundary + p := pomLocation.LocalPath() p = path.Dir(p) p = path.Join(p, relativePath) p = path.Clean(p) diff --git a/syft/pkg/cataloger/java/internal/maven/resolver_test.go b/syft/pkg/cataloger/java/internal/maven/resolver_test.go index 259a55c45..44cda5ec3 100644 --- a/syft/pkg/cataloger/java/internal/maven/resolver_test.go +++ b/syft/pkg/cataloger/java/internal/maven/resolver_test.go @@ -6,9 +6,11 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/internal/fileresolver" maventest "github.com/anchore/syft/syft/pkg/cataloger/java/internal/maven/test" ) @@ -368,3 +370,25 @@ func Test_relativePathParent(t *testing.T) { func ptr[T any](value T) *T { return &value } + +// pathRecorder records the paths it is asked for and finds nothing. +type pathRecorder struct { + file.Resolver + paths []string +} + +func (p *pathRecorder) FilesByPath(paths ...string) ([]file.Location, error) { + p.paths = append(p.paths, paths...) + return nil, nil +} + +func Test_relativePathParent_staysInsideTheArchive(t *testing.T) { + recorder := &pathRecorder{} + r := NewResolver(recorder, DefaultConfig()) + pom := &Project{Parent: &Parent{RelativePath: ptr("../../other")}} + nested := file.Coordinates{RealPath: "/data/outer.zip", FullPath: "/data/outer.zip!/a/pom.xml"} + r.pomLocations[pom] = file.NewVirtualLocationFromCoordinates(nested, nested.FullPath) + + assert.Nil(t, r.findParentPomByRelativePath(context.Background(), pom, ID{}, nil)) + assert.Equal(t, []string{"/other/pom.xml"}, recorder.paths) +}