Skip to content

Commit 8f1a8b6

Browse files
authored
Fix VcpkgComponent purl construction for names containing slashes (#1752)
* Fix VcpkgComponent throwing on names with slashes VcpkgComponent.PackageUrl built purl strings by interpolating this.Name directly, e.g. $"pkg:vcpkg/{this.Name}@{this.Version}". Vcpkg SPDX entries can have names like "google/brotli", which the parser then misinterprets as namespace/name path segments. Names with consecutive slashes cause an "empty segment" exception. Use the PackageUrl component constructor instead, which treats the name as a single component and percent-encodes it properly. * Add regression test
1 parent 7fb976c commit 8f1a8b6

2 files changed

Lines changed: 59 additions & 12 deletions

File tree

src/Microsoft.ComponentDetection.Contracts/TypedComponent/VcpkgComponent.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#nullable disable
22
namespace Microsoft.ComponentDetection.Contracts.TypedComponent;
33

4+
using System.Collections.Generic;
45
using System.Text.Json.Serialization;
56
using PackageUrl;
67

@@ -53,18 +54,11 @@ public override PackageUrl PackageUrl
5354
{
5455
get
5556
{
56-
if (this.PortVersion > 0)
57-
{
58-
return new PackageUrl($"pkg:vcpkg/{this.Name}@{this.Version}?port_version={this.PortVersion}");
59-
}
60-
else if (this.Version != null)
61-
{
62-
return new PackageUrl($"pkg:vcpkg/{this.Name}@{this.Version}");
63-
}
64-
else
65-
{
66-
return new PackageUrl($"pkg:vcpkg/{this.Name}");
67-
}
57+
var qualifiers = this.PortVersion > 0
58+
? new SortedDictionary<string, string> { { "port_version", this.PortVersion.ToString() } }
59+
: null;
60+
61+
return new PackageUrl("vcpkg", null, this.Name, this.Version, qualifiers, null);
6862
}
6963
}
7064

test/Microsoft.ComponentDetection.Detectors.Tests/VcpkgComponentDetectorTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,59 @@ public async Task TestTinyxmlAndResourceAsync()
150150
sbomComponent.DownloadLocation.Should().Be("git+https://github.com/leethomason/tinyxml2");
151151
}
152152

153+
[TestMethod]
154+
public async Task TestResourceWithSlashInNameProducesValidPackageUrlAsync()
155+
{
156+
var spdxFile = """
157+
{
158+
"SPDXID": "SPDXRef-DOCUMENT",
159+
"documentNamespace": "https://spdx.org/spdxdocs/brotli-x64-windows",
160+
"name": "brotli:x64-windows@1.0.9",
161+
"packages": [
162+
{
163+
"name": "brotli",
164+
"SPDXID": "SPDXRef-port",
165+
"versionInfo": "1.0.9#0",
166+
"downloadLocation": "git+https://github.com/Microsoft/vcpkg#ports/brotli",
167+
"licenseConcluded": "NOASSERTION",
168+
"licenseDeclared": "NOASSERTION",
169+
"copyrightText": "NOASSERTION"
170+
},
171+
{
172+
"SPDXID": "SPDXRef-resource-1",
173+
"name": "google/brotli",
174+
"downloadLocation": "git+https://github.com/google/brotli@1.0.9",
175+
"licenseConcluded": "NOASSERTION",
176+
"licenseDeclared": "NOASSERTION",
177+
"copyrightText": "NOASSERTION"
178+
}
179+
]
180+
}
181+
""";
182+
var (scanResult, componentRecorder) = await this
183+
.detectorTestUtility.WithFile("vcpkg.spdx.json", spdxFile)
184+
.ExecuteDetectorAsync();
185+
186+
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
187+
188+
var detectedComponents = componentRecorder.GetDetectedComponents();
189+
var components = detectedComponents.ToList();
190+
191+
components.Should().HaveCount(2);
192+
193+
var resourceComponent = (VcpkgComponent)components
194+
.First(c => ((VcpkgComponent)c.Component).SPDXID == "SPDXRef-resource-1")
195+
.Component;
196+
resourceComponent.Name.Should().Be("google/brotli");
197+
resourceComponent.Version.Should().Be("1.0.9");
198+
199+
// This was the bug: names with slashes caused MalformedPackageUrlException
200+
var purl = resourceComponent.PackageUrl;
201+
purl.Should().NotBeNull();
202+
purl.ToString().Should().Contain("vcpkg");
203+
purl.ToString().Should().Contain("brotli");
204+
}
205+
153206
[TestMethod]
154207
public async Task TestBlankJsonAsync()
155208
{

0 commit comments

Comments
 (0)