From c7d75585dd735d6795ed4494b2a01aec99462ff3 Mon Sep 17 00:00:00 2001 From: hainenber Date: Mon, 4 Sep 2023 16:48:20 +0700 Subject: [PATCH] fix(cdx): validate external refs before encoding --- .../cyclonedxhelpers/external_references.go | 17 ++++++-- .../external_references_test.go | 43 ++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/external_references.go b/syft/formats/common/cyclonedxhelpers/external_references.go index 59f388717a3b..29770070a5a7 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references.go +++ b/syft/formats/common/cyclonedxhelpers/external_references.go @@ -2,6 +2,7 @@ package cyclonedxhelpers import ( "fmt" + "net/url" "strings" "github.com/CycloneDX/cyclonedx-go" @@ -15,9 +16,11 @@ import ( func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference { var refs []cyclonedx.ExternalReference if hasMetadata(p) { + // Skip adding extracted URL and Homepage metadata + // as "external_reference" if the metadata isn't IRI-compliant switch metadata := p.Metadata.(type) { case pkg.ApkMetadata: - if metadata.URL != "" { + if metadata.URL != "" && isValidExternalRef(metadata.URL) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.URL, Type: cyclonedx.ERTypeDistribution, @@ -31,20 +34,20 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference { }) } case pkg.NpmPackageJSONMetadata: - if metadata.URL != "" { + if metadata.URL != "" && isValidExternalRef(metadata.URL) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.URL, Type: cyclonedx.ERTypeDistribution, }) } - if metadata.Homepage != "" { + if metadata.Homepage != "" && isValidExternalRef(metadata.Homepage) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.Homepage, Type: cyclonedx.ERTypeWebsite, }) } case pkg.GemMetadata: - if metadata.Homepage != "" { + if metadata.Homepage != "" && isValidExternalRef(metadata.Homepage) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.Homepage, Type: cyclonedx.ERTypeWebsite, @@ -158,3 +161,9 @@ func refComment(c *cyclonedx.Component, typ cyclonedx.ExternalReferenceType) str } return "" } + +// isValidExternalRef checks for IRI-comppliance for input string to be added into "external_reference" +func isValidExternalRef(s string) bool { + parsed, err := url.Parse(s) + return err == nil && parsed != nil && parsed.Host != "" +} diff --git a/syft/formats/common/cyclonedxhelpers/external_references_test.go b/syft/formats/common/cyclonedxhelpers/external_references_test.go index c6ce0355b4dc..67fd73778daf 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references_test.go +++ b/syft/formats/common/cyclonedxhelpers/external_references_test.go @@ -32,7 +32,7 @@ func Test_encodeExternalReferences(t *testing.T) { }, }, { - name: "from npm", + name: "from npm with valid URL", input: pkg.Package{ Metadata: pkg.NpmPackageJSONMetadata{ URL: "http://a-place.gov", @@ -42,6 +42,18 @@ func Test_encodeExternalReferences(t *testing.T) { {URL: "http://a-place.gov", Type: cyclonedx.ERTypeDistribution}, }, }, + { + name: "from npm with invalid URL but valid Homepage", + input: pkg.Package{ + Metadata: pkg.NpmPackageJSONMetadata{ + URL: "b-place", + Homepage: "http://b-place.gov", + }, + }, + expected: &[]cyclonedx.ExternalReference{ + {URL: "http://b-place.gov", Type: cyclonedx.ERTypeWebsite}, + }, + }, { name: "from cargo lock", input: pkg.Package{ @@ -132,3 +144,32 @@ func Test_encodeExternalReferences(t *testing.T) { }) } } + +func Test_isValidExternalRef(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + { + name: "valid URL for external_reference, git protocol", + input: "git+https://github.com/abc/def.git", + expected: true, + }, + { + name: "valid URL for external_reference, git protocol", + input: "git+https://github.com/abc/def.git", + expected: true, + }, + { + name: "invalid URL for external_reference", + input: "abc/def", + expected: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.expected, isValidExternalRef(test.input)) + }) + } +}