From 59b3662b44c29b52761cfa2a4772786d1b84938a Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:15:34 -0500 Subject: [PATCH 1/4] feat: convert spdx absolute to relative Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- .../common/spdxhelpers/to_format_model.go | 26 ++++++++++- .../spdxhelpers/to_format_model_test.go | 45 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/syft/format/common/spdxhelpers/to_format_model.go b/syft/format/common/spdxhelpers/to_format_model.go index 643c5aa9f87..8ed3d4f898e 100644 --- a/syft/format/common/spdxhelpers/to_format_model.go +++ b/syft/format/common/spdxhelpers/to_format_model.go @@ -5,6 +5,7 @@ import ( "crypto/sha1" "fmt" "path" + "path/filepath" "regexp" "slices" "sort" @@ -627,13 +628,18 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) { comment = fmt.Sprintf("layerID: %s", coordinates.FileSystemID) } + relativePath, err := convertAbsoluteToRelative(coordinates.RealPath) + if err != nil { + // TODO: + } + results = append(results, &spdx.File{ FileSPDXIdentifier: toSPDXID(coordinates), FileComment: comment, // required, no attempt made to determine license information LicenseConcluded: noAssertion, Checksums: toFileChecksums(digests), - FileName: coordinates.RealPath, + FileName: relativePath, FileTypes: toFileTypes(metadata), LicenseInfoInFiles: []string{ // required in SPDX 2.2 helpers.NOASSERTION, @@ -831,3 +837,21 @@ func trimPatchVersion(semver string) string { } return semver } + +// 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) + absPath, err := filepath.Abs(absPath) + if err != nil { + return "", fmt.Errorf("error converting absPath to absolute path: %v", err) + } + + // we use "/" here given that we're converting absolute paths from root to relative + relPath, err := filepath.Rel("/", absPath) + if err != nil { + return "", fmt.Errorf("error calculating relative path: %v", err) + } + + return relPath, nil +} diff --git a/syft/format/common/spdxhelpers/to_format_model_test.go b/syft/format/common/spdxhelpers/to_format_model_test.go index 36201bc852d..c019d05c7c1 100644 --- a/syft/format/common/spdxhelpers/to_format_model_test.go +++ b/syft/format/common/spdxhelpers/to_format_model_test.go @@ -382,6 +382,51 @@ func Test_toPackageChecksums(t *testing.T) { } } +func Test_toFiles(t *testing.T) { + tests := []struct { + name string + in sbom.SBOM + want spdx.File + }{ + { + name: "File paths are converted to relative in final SPDX collection", + in: sbom.SBOM{ + Source: source.Description{ + Name: "alpine", + Version: "sha256:d34db33f", + Metadata: source.ImageMetadata{ + UserInput: "alpine:latest", + ManifestDigest: "sha256:d34db33f", + }, + }, + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(pkg.Package{ + Name: "pkg-1", + Version: "version-1", + }), + FileMetadata: map[file.Coordinates]file.Metadata{ + file.Coordinates{ + RealPath: "/some/path", + FileSystemID: "", + }: file.Metadata{ + Path: "/some/path", + }, + }, + }, + }, + want: spdx.File{ + FileName: "some/path", + }, + }, + } + + for _, test := range tests { + files := toFiles(test.in) + got := files[0] + assert.Equal(t, test.want.FileName, got.FileName) + } +} + func Test_toFileTypes(t *testing.T) { tests := []struct { From 1cc22483db8e2ef092d9f476daa9cd38333bea12 Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:29:39 -0500 Subject: [PATCH 2/4] chore: lint and error fix Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- cmd/syft/internal/options/source.go | 2 +- syft/format/common/spdxhelpers/to_format_model.go | 3 ++- syft/format/common/spdxhelpers/to_format_model_test.go | 4 ++-- syft/internal/fileresolver/file_indexer.go | 3 ++- syft/internal/fileresolver/file_indexer_test.go | 8 +++++--- syft/internal/fileresolver/filetree_resolver_test.go | 5 +++-- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/cmd/syft/internal/options/source.go b/cmd/syft/internal/options/source.go index 53078305c2e..97e81b9459f 100644 --- a/cmd/syft/internal/options/source.go +++ b/cmd/syft/internal/options/source.go @@ -5,11 +5,11 @@ import ( "sort" "strings" - stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/dustin/go-humanize" "github.com/scylladb/go-set/strset" "github.com/anchore/clio" + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/source/sourceproviders" ) diff --git a/syft/format/common/spdxhelpers/to_format_model.go b/syft/format/common/spdxhelpers/to_format_model.go index 8ed3d4f898e..2802bf61218 100644 --- a/syft/format/common/spdxhelpers/to_format_model.go +++ b/syft/format/common/spdxhelpers/to_format_model.go @@ -630,7 +630,8 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) { relativePath, err := convertAbsoluteToRelative(coordinates.RealPath) if err != nil { - // TODO: + log.Debugf("unable to convert relative path '%s' to absolute path: %s", coordinates.RealPath, err) + relativePath = coordinates.RealPath } results = append(results, &spdx.File{ diff --git a/syft/format/common/spdxhelpers/to_format_model_test.go b/syft/format/common/spdxhelpers/to_format_model_test.go index c019d05c7c1..315899b74ff 100644 --- a/syft/format/common/spdxhelpers/to_format_model_test.go +++ b/syft/format/common/spdxhelpers/to_format_model_test.go @@ -405,10 +405,10 @@ func Test_toFiles(t *testing.T) { Version: "version-1", }), FileMetadata: map[file.Coordinates]file.Metadata{ - file.Coordinates{ + { RealPath: "/some/path", FileSystemID: "", - }: file.Metadata{ + }: { Path: "/some/path", }, }, diff --git a/syft/internal/fileresolver/file_indexer.go b/syft/internal/fileresolver/file_indexer.go index fe110c4ced9..bc6d660ac6a 100644 --- a/syft/internal/fileresolver/file_indexer.go +++ b/syft/internal/fileresolver/file_indexer.go @@ -5,11 +5,12 @@ import ( "os" "path/filepath" + "github.com/wagoodman/go-progress" + "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/filetree" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/internal/windows" - "github.com/wagoodman/go-progress" ) type fileIndexer struct { diff --git a/syft/internal/fileresolver/file_indexer_test.go b/syft/internal/fileresolver/file_indexer_test.go index cce3981382d..165bc39d022 100644 --- a/syft/internal/fileresolver/file_indexer_test.go +++ b/syft/internal/fileresolver/file_indexer_test.go @@ -1,13 +1,15 @@ package fileresolver import ( - "github.com/anchore/stereoscope/pkg/file" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "io/fs" "os" "path" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/stereoscope/pkg/file" ) // - Verify that both the parent and the path are indexed diff --git a/syft/internal/fileresolver/filetree_resolver_test.go b/syft/internal/fileresolver/filetree_resolver_test.go index e385ec8501f..1a3d661722d 100644 --- a/syft/internal/fileresolver/filetree_resolver_test.go +++ b/syft/internal/fileresolver/filetree_resolver_test.go @@ -14,13 +14,14 @@ import ( "testing" "time" - stereoscopeFile "github.com/anchore/stereoscope/pkg/file" - "github.com/anchore/syft/syft/file" "github.com/google/go-cmp/cmp" "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" + + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/syft/file" ) // Tests for filetree resolver when directory is used for index From 7e6a80442cc7e5d9d9f86beef26d081276ebdb7b Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:38:36 -0500 Subject: [PATCH 3/4] chore: update golden fixtures to use relative paths Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- .../snapshot/TestSPDX22JSONRequredProperties.golden | 2 +- .../snapshot/TestSPDXRelationshipOrder.golden | 12 ++++++------ .../snapshot/TestSPDXRelationshipOrder.golden | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden index 8614a8c6d1e..8587d7bc01a 100644 --- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden +++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden @@ -59,7 +59,7 @@ ], "files": [ { - "fileName": "/some/file", + "fileName": "some/file", "SPDXID": "SPDXRef-File-some-file-2c5bc344430decac", "checksums": [ { diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 138166baa28..fd11fcc8b1e 100644 --- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -89,7 +89,7 @@ ], "files": [ { - "fileName": "/a1/f6", + "fileName": "a1/f6", "SPDXID": "SPDXRef-File-a1-f6-9c2f7510199b17f6", "fileTypes": [ "OTHER" @@ -107,7 +107,7 @@ "copyrightText": "NOASSERTION" }, { - "fileName": "/d1/f3", + "fileName": "d1/f3", "SPDXID": "SPDXRef-File-d1-f3-c6f5b29dca12661f", "fileTypes": [ "OTHER" @@ -125,7 +125,7 @@ "copyrightText": "NOASSERTION" }, { - "fileName": "/d2/f4", + "fileName": "d2/f4", "SPDXID": "SPDXRef-File-d2-f4-c641caa71518099f", "fileTypes": [ "OTHER" @@ -143,7 +143,7 @@ "copyrightText": "NOASSERTION" }, { - "fileName": "/f1", + "fileName": "f1", "SPDXID": "SPDXRef-File-f1-5265a4dde3edbf7c", "fileTypes": [ "OTHER" @@ -161,7 +161,7 @@ "copyrightText": "NOASSERTION" }, { - "fileName": "/f2", + "fileName": "f2", "SPDXID": "SPDXRef-File-f2-f9e49132a4b96ccd", "fileTypes": [ "OTHER" @@ -179,7 +179,7 @@ "copyrightText": "NOASSERTION" }, { - "fileName": "/z1/f5", + "fileName": "z1/f5", "SPDXID": "SPDXRef-File-z1-f5-839d99ee67d9d174", "fileTypes": [ "OTHER" diff --git a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index ac7a8585dcf..b7940ae7258 100644 --- a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -10,7 +10,7 @@ Created: redacted ##### Unpackaged files -FileName: /a1/f6 +FileName: a1/f6 SPDXID: SPDXRef-File-a1-f6-9c2f7510199b17f6 FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 @@ -18,7 +18,7 @@ LicenseConcluded: NOASSERTION LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION -FileName: /d1/f3 +FileName: d1/f3 SPDXID: SPDXRef-File-d1-f3-c6f5b29dca12661f FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 @@ -26,7 +26,7 @@ LicenseConcluded: NOASSERTION LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION -FileName: /d2/f4 +FileName: d2/f4 SPDXID: SPDXRef-File-d2-f4-c641caa71518099f FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 @@ -34,7 +34,7 @@ LicenseConcluded: NOASSERTION LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION -FileName: /f1 +FileName: f1 SPDXID: SPDXRef-File-f1-5265a4dde3edbf7c FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 @@ -42,7 +42,7 @@ LicenseConcluded: NOASSERTION LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION -FileName: /f2 +FileName: f2 SPDXID: SPDXRef-File-f2-f9e49132a4b96ccd FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 @@ -50,7 +50,7 @@ LicenseConcluded: NOASSERTION LicenseInfoInFile: NOASSERTION FileCopyrightText: NOASSERTION -FileName: /z1/f5 +FileName: z1/f5 SPDXID: SPDXRef-File-z1-f5-839d99ee67d9d174 FileType: OTHER FileChecksum: SHA1: 0000000000000000000000000000000000000000 From 98ad82bae5c18392653566edc0b91e1fc02539a0 Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:53:22 -0500 Subject: [PATCH 4/4] chore: convert IsAbs Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- syft/format/common/spdxhelpers/to_format_model.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/syft/format/common/spdxhelpers/to_format_model.go b/syft/format/common/spdxhelpers/to_format_model.go index a394666a94b..73a7515551c 100644 --- a/syft/format/common/spdxhelpers/to_format_model.go +++ b/syft/format/common/spdxhelpers/to_format_model.go @@ -5,7 +5,6 @@ import ( "crypto/sha1" "fmt" "path" - "path/filepath" "regexp" "slices" "sort" @@ -845,15 +844,16 @@ func trimPatchVersion(semver string) string { // 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) - absPath, err := filepath.Abs(absPath) - if err != nil { - return "", fmt.Errorf("error converting absPath to absolute path: %v", err) + 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, err := filepath.Rel("/", absPath) - if err != nil { - return "", fmt.Errorf("error calculating relative path: %v", err) + relPath, found := strings.CutPrefix(absPath, "/") + if !found { + return "", fmt.Errorf("error calculating relative path: %s", absPath) } return relPath, nil