Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions syft/format/common/cyclonedxhelpers/to_format_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
formatinternal "github.com/anchore/syft/syft/format/internal"
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
Expand Down Expand Up @@ -55,7 +56,6 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
artifacts := s.Artifacts

for _, coordinate := range coordinates {
var metadata *file.Metadata
// File Info
fileMetadata, exists := artifacts.FileMetadata[coordinate]
// no file metadata then don't include in SBOM
Expand All @@ -70,7 +70,6 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
// skip dir, symlinks and sockets for the final bom
continue
}
metadata = &fileMetadata

// Digests
var digests []file.Digest
Expand All @@ -79,10 +78,14 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
}

cdxHashes := digestsToHashes(digests)
relativePath, err := formatinternal.ConvertAbsoluteToRelative(coordinate.RealPath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As was noted on the issue, converting the RealPath to a relative path in this manner seems like the wrong thing to do: if I scanned /subdir and there's a symlink to ../foo, this would be resolved to the absolute path of /foo, which is a RealPath, which then would be converted to the relative path foo, but there is no /subdir/foo. As noted in another comment, this should probably be ../foo

if err != nil {
relativePath = coordinate.RealPath
}
components = append(components, cyclonedx.Component{
BOMRef: string(coordinate.ID()),
Type: cyclonedx.ComponentTypeFile,
Name: metadata.Path,
Name: relativePath,
Hashes: &cdxHashes,
})
}
Expand Down
59 changes: 55 additions & 4 deletions syft/format/common/cyclonedxhelpers/to_format_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func Test_FileComponents(t *testing.T) {
},
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Name: "test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
Expand Down Expand Up @@ -214,7 +214,7 @@ func Test_FileComponents(t *testing.T) {
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Name: "test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
Expand Down Expand Up @@ -246,7 +246,7 @@ func Test_FileComponents(t *testing.T) {
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Name: "test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz678910"},
Expand Down Expand Up @@ -282,7 +282,7 @@ func Test_FileComponents(t *testing.T) {
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Name: "test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
Expand Down Expand Up @@ -317,6 +317,57 @@ func Test_FileComponents(t *testing.T) {
}
}

func TestToFormatModel_FileComponentName_BasePathParity(t *testing.T) {
tests := []struct {
name string
realPath string
metadataPath string
wantName string
}{
{
name: "base-relative path strips leading slash (SPDX parity)",
realPath: "/usr/bin/foo",
metadataPath: "/absolute/scanner/path/usr/bin/foo",
wantName: "usr/bin/foo",
},
{
name: "path without leading slash is preserved as-is",
realPath: "relative/path/bar",
metadataPath: "/absolute/scanner/path/relative/path/bar",
wantName: "relative/path/bar",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
coordinate := file.Coordinates{RealPath: tt.realPath}
s := sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(),
FileMetadata: map[file.Coordinates]file.Metadata{
coordinate: {Path: tt.metadataPath, Type: stfile.TypeRegular},
},
FileDigests: map[file.Coordinates][]file.Digest{
coordinate: {},
},
},
}
result := ToFormatModel(s)
require.NotNil(t, result.Components)
var fileComp *cyclonedx.Component
for i := range *result.Components {
c := (*result.Components)[i]
if c.Type == cyclonedx.ComponentTypeFile {
fileComp = &c
break
}
}
require.NotNil(t, fileComp, "expected a file component in CycloneDX output")
assert.Equal(t, tt.wantName, fileComp.Name)
})
}
}

func Test_toBomDescriptor(t *testing.T) {
type args struct {
name string
Expand Down
19 changes: 3 additions & 16 deletions syft/format/common/spdxhelpers/to_format_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
formatInternal "github.com/anchore/syft/syft/format/internal"
formatinternal "github.com/anchore/syft/syft/format/internal"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we avoid these import renames?

"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
Expand Down Expand Up @@ -680,7 +680,7 @@ func lookupRelationship(ty artifact.RelationshipType) (bool, helpers.Relationshi
func toFiles(s sbom.SBOM) (results []*spdx.File) {
artifacts := s.Artifacts

_, coordinateSorter := formatInternal.GetLocationSorters(s)
_, coordinateSorter := formatinternal.GetLocationSorters(s)

coordinates := s.AllCoordinates()
slices.SortFunc(coordinates, coordinateSorter)
Expand Down Expand Up @@ -871,20 +871,7 @@ func trimPatchVersion(semver string) string {
// spdx requires that the file name field is a relative filename
// with the root of the package archive or directory
func convertAbsoluteToRelative(absPath string) (string, error) {
// Ensure the absolute path is absolute (although it should already be)
if !path.IsAbs(absPath) {
// already relative
log.Debugf("%s is already relative", absPath)
return absPath, nil
}

// we use "/" here given that we're converting absolute paths from root to relative
relPath, found := strings.CutPrefix(absPath, "/")
if !found {
return "", fmt.Errorf("error calculating relative path: %s", absPath)
}

return relPath, nil
return formatinternal.ConvertAbsoluteToRelative(absPath)
}

func convertOtherLicense(otherLicenses []spdx.OtherLicense) []*spdx.OtherLicense {
Expand Down
20 changes: 20 additions & 0 deletions syft/format/internal/relativepath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package internal

import (
"fmt"
"path"
"strings"
)

func ConvertAbsoluteToRelative(absPath string) (string, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be something like what filepath.Rel is doing, though I don't think we can use that function because the paths here are normalized to forwards slashes

if !path.IsAbs(absPath) {
return absPath, nil
}

relPath, found := strings.CutPrefix(absPath, "/")
if !found {
return "", fmt.Errorf("error calculating relative path: %s", absPath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns an error converting a relative path to a relative path?

}

return relPath, nil
}
45 changes: 45 additions & 0 deletions syft/format/internal/relativepath_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package internal

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestConvertAbsoluteToRelative(t *testing.T) {
tests := []struct {
name string
absPath string
want string
}{
{
name: "absolute path",
absPath: "/usr/bin/foo",
want: "usr/bin/foo",
},
{
name: "relative path",
absPath: "relative/path/bar",
want: "relative/path/bar",
},
{
name: "root path",
absPath: "/",
want: "",
},
{
name: "dot relative path",
absPath: "./foo",
want: "./foo",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ConvertAbsoluteToRelative(tt.absPath)
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}