Skip to content

Bump packageurl-dotnet from 2.0.0-rc.2 to 2.0.0-rc.3#1738

Open
JamieMagee wants to merge 1 commit intomainfrom
jamagee/fix-linux-purl-distro-qualifier
Open

Bump packageurl-dotnet from 2.0.0-rc.2 to 2.0.0-rc.3#1738
JamieMagee wants to merge 1 commit intomainfrom
jamagee/fix-linux-purl-distro-qualifier

Conversation

@JamieMagee
Copy link
Member

LinuxComponent purls now include a distro qualifier with the release version, and Alpine packages get apk purls instead of null.

Why

The purl for pkg:deb/ubuntu/bash@1 was the same whether the package came from Ubuntu 18.04 or 20.04. ComputeBaseId() already distinguished them, but the purl didn't, so anything downstream that relied on the purl for deduplication or resolution would over-match across distro versions.

The purl spec includes a distro qualifier for exactly this reason, and Syft already emits it (e.g., distro=ubuntu-20.04). We just weren't passing it through.

While I was in here, I also noticed Alpine was returning null even though apk has been a registered purl type for a while now.

Changes

  • Emit distro=<id>-<release> qualifier on all Linux purls
  • Map "Red Hat Enterprise Linux" to redhat for namespace and qualifier (matches Syft)
  • Add apk type for Alpine
  • Split the old AlpineAndUnknownDoNotHavePurls test since Alpine does have purls now
  • Added RhelNamespaceIsRedhat test

Example before: pkg:deb/ubuntu/bash@1
Example after: pkg:deb/ubuntu/bash@1?distro=ubuntu-18.04

Fixes #1714

LinuxComponent.PackageUrl was missing the distro release -- so
pkg:deb/ubuntu/bash@1 on 18.04 and 20.04 produced identical purls
even though ComputeBaseId distinguished them. Now emits a distro
qualifier (e.g., distro=ubuntu-20.04) to match what Syft produces.

Also adds apk as a purl type for Alpine, and maps Red Hat Enterprise
Linux to the short id 
edhat for both namespace and qualifier.

Fixes #1714
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates Linux PackageUrl generation to better distinguish packages across distro releases and to emit purls for Alpine packages, aligning output more closely with purl-spec/Syft conventions.

Changes:

  • Add distro=<id>-<release> qualifier to Linux purls (deb/rpm/apk) to prevent cross-distro-release overmatching.
  • Emit apk purls for Alpine Linux packages (previously returned null).
  • Extend/adjust unit tests to validate new Linux purl behavior, including RHEL namespace expectations.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/Microsoft.ComponentDetection.Contracts/TypedComponent/LinuxComponent.cs Adds distro qualifier, introduces Alpine apk purls, and RHEL→redhat namespace/qualifier mapping logic.
test/Microsoft.ComponentDetection.Contracts.Tests/PurlGenerationTests.cs Updates and expands tests to assert distro qualifier presence and Alpine/RHEL purl behavior.
Comments suppressed due to low confidence (1)

src/Microsoft.ComponentDetection.Contracts/TypedComponent/LinuxComponent.cs:123

  • IsRHEL() only matches the literal string "Red Hat Enterprise Linux", but the Linux detector constructs LinuxComponent using distro.Id (e.g., "ubuntu", "mariner"). Unless distro.Id is exactly that long name, RHEL packages will not be classified as RPM and will continue to produce a null PackageUrl. Consider recognizing the expected distro id(s) (e.g., "rhel") and mapping them to the canonical "redhat" id used for the namespace/qualifier.
    private bool IsRHEL()
    {
        return this.Distribution.Equals("RED HAT ENTERPRISE LINUX", StringComparison.OrdinalIgnoreCase);
    }

    private bool IsAlpine()
    {
        return this.Distribution.Equals("ALPINE", StringComparison.OrdinalIgnoreCase);
    }

    private string GetDistroId()
    {
        if (this.IsRHEL())
        {
            return "redhat";
        }

        return this.Distribution.ToLowerInvariant();

Comment on lines +106 to +114
[TestMethod]
public void RhelNamespaceIsRedhat()
{
// RHEL should use "redhat" as the namespace and distro id, matching Syft conventions
var rhelComponent = new LinuxComponent("Red Hat Enterprise Linux", "9.0", "bash", "1");

rhelComponent.PackageUrl.Namespace.Should().Be("redhat");
rhelComponent.PackageUrl.Qualifiers["distro"].Should().Be("redhat-9.0");
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The new RhelNamespaceIsRedhat test uses distribution: "Red Hat Enterprise Linux", but production code creates LinuxComponent from distro.Id (short ids like "ubuntu"/"mariner"). To ensure this behavior is actually covered, add/adjust a test case that uses the distro id value you expect from Syft (e.g., "rhel") and asserts the namespace/distro qualifier mapping.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 69 to +77
if (packageType != null)
{
return new PackageUrl(packageType, this.Distribution, this.Name, this.Version, null, null);
var distroId = this.GetDistroId();
var qualifiers = new SortedDictionary<string, string>
{
{ "distro", $"{distroId}-{this.Release}" },
};

return new PackageUrl(packageType, distroId, this.Name, this.Version, qualifiers, null);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

PR title/description says packageurl-dotnet is bumped to 2.0.0-rc.3, but Directory.Packages.props in this change set still pins packageurl-dotnet to 2.0.0-rc.2. Either update the central package version to rc.3 or adjust the PR metadata to match what’s actually changing.

This issue also appears on line 106 of the same file.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Distro release is included in LinuxComponent.Id but not Purl

2 participants