From 305ee8705284897223e6112577aeef3f33c90e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:39:51 +0700 Subject: [PATCH] fix(cdx): validate external refs before encoding (#2091) Signed-off-by: hainenber --- .../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 59f388717..29770070a 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 c6ce0355b..67fd73778 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)) + }) + } +}