Skip to content

Commit

Permalink
encode upstream qualifier on os package pURLs (anchore#769)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
  • Loading branch information
wagoodman committed Jan 25, 2022
1 parent 0e4cd37 commit bce378b
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 23 deletions.
12 changes: 9 additions & 3 deletions syft/pkg/apk_metadata.go
Expand Up @@ -49,6 +49,14 @@ type ApkFileRecord struct {

// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
func (m ApkMetadata) PackageURL(distro *linux.Release) string {
qualifiers := map[string]string{
purlArchQualifier: m.Architecture,
}

if m.OriginPackage != "" {
qualifiers[purlUpstreamQualifier] = m.OriginPackage
}

return packageurl.NewPackageURL(
// note: this is currently a candidate and not technically within spec
// see https://github.com/package-url/purl-spec#other-candidate-types-to-define
Expand All @@ -57,9 +65,7 @@ func (m ApkMetadata) PackageURL(distro *linux.Release) string {
m.Package,
m.Version,
purlQualifiers(
map[string]string{
purlArchQualifier: m.Architecture,
},
qualifiers,
distro,
),
"",
Expand Down
16 changes: 15 additions & 1 deletion syft/pkg/apk_metadata_test.go
Expand Up @@ -31,7 +31,7 @@ func TestApkMetadata_pURL(t *testing.T) {
expected: "pkg:alpine/p@v?arch=a&distro=alpine-3.4.6",
},
{
name: "missing architecure",
name: "missing architecture",
metadata: ApkMetadata{
Package: "p",
Version: "v",
Expand Down Expand Up @@ -67,6 +67,20 @@ func TestApkMetadata_pURL(t *testing.T) {
},
expected: "pkg:alpine/g%20plus%20plus@v84?arch=am86&distro=alpine-3.15.0",
},
{
name: "add source information as qualifier",
metadata: ApkMetadata{
Package: "p",
Version: "v",
Architecture: "a",
OriginPackage: "origin",
},
distro: linux.Release{
ID: "alpine",
VersionID: "3.4.6",
},
expected: "pkg:alpine/p@v?arch=a&upstream=origin&distro=alpine-3.4.6",
},
}

for _, test := range tests {
Expand Down
18 changes: 15 additions & 3 deletions syft/pkg/dpkg_metadata.go
@@ -1,6 +1,7 @@
package pkg

import (
"fmt"
"sort"

"github.com/anchore/syft/syft/file"
Expand Down Expand Up @@ -43,6 +44,19 @@ func (m DpkgMetadata) PackageURL(distro *linux.Release) string {
if distro != nil {
namespace = distro.ID
}

qualifiers := map[string]string{
purlArchQualifier: m.Architecture,
}

if m.Source != "" {
if m.SourceVersion != "" {
qualifiers[purlUpstreamQualifier] = fmt.Sprintf("%s@%s", m.Source, m.SourceVersion)
} else {
qualifiers[purlUpstreamQualifier] = m.Source
}
}

return packageurl.NewPackageURL(
// TODO: replace with `packageurl.TypeDebian` upon merge of https://github.com/package-url/packageurl-go/pull/21
// TODO: or, since we're now using an Anchore fork of this module, we could do this sooner.
Expand All @@ -51,9 +65,7 @@ func (m DpkgMetadata) PackageURL(distro *linux.Release) string {
m.Package,
m.Version,
purlQualifiers(
map[string]string{
purlArchQualifier: m.Architecture,
},
qualifiers,
distro,
),
"",
Expand Down
30 changes: 27 additions & 3 deletions syft/pkg/dpkg_metadata_test.go
Expand Up @@ -25,7 +25,6 @@ func TestDpkgMetadata_pURL(t *testing.T) {
},
metadata: DpkgMetadata{
Package: "p",
Source: "s",
Version: "v",
},
expected: "pkg:deb/debian/p@v?distro=debian-11",
Expand All @@ -38,7 +37,6 @@ func TestDpkgMetadata_pURL(t *testing.T) {
},
metadata: DpkgMetadata{
Package: "p",
Source: "s",
Version: "v",
Architecture: "a",
},
Expand All @@ -48,11 +46,37 @@ func TestDpkgMetadata_pURL(t *testing.T) {
name: "missing distro",
metadata: DpkgMetadata{
Package: "p",
Source: "s",
Version: "v",
},
expected: "pkg:deb/p@v",
},
{
name: "with upstream qualifier with source pkg name info",
distro: &linux.Release{
ID: "debian",
VersionID: "11",
},
metadata: DpkgMetadata{
Package: "p",
Source: "s",
Version: "v",
},
expected: "pkg:deb/debian/p@v?upstream=s&distro=debian-11",
},
{
name: "with upstream qualifier with source pkg name and version info",
distro: &linux.Release{
ID: "debian",
VersionID: "11",
},
metadata: DpkgMetadata{
Package: "p",
Source: "s",
Version: "v",
SourceVersion: "2.3",
},
expected: "pkg:deb/debian/p@v?upstream=s@2.3&distro=debian-11",
},
}

for _, test := range tests {
Expand Down
2 changes: 1 addition & 1 deletion syft/pkg/python_package_metadata.go
Expand Up @@ -106,7 +106,7 @@ func (p PythonDirectURLOriginInfo) vcsURLQualifier() packageurl.Qualifiers {
if p.VCS != "" {
// Taken from https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#known-qualifiers-keyvalue-pairs
// packageurl-go still doesn't support all qualifier names
return packageurl.Qualifiers{{Key: purlVCSURL, Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
return packageurl.Qualifiers{{Key: purlVCSURLQualifier, Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
}
return nil
}
4 changes: 4 additions & 0 deletions syft/pkg/rpmdb_metadata.go
Expand Up @@ -63,6 +63,10 @@ func (m RpmdbMetadata) PackageURL(distro *linux.Release) string {
qualifiers[purlEpochQualifier] = strconv.Itoa(*m.Epoch)
}

if m.SourceRpm != "" {
qualifiers[purlUpstreamQualifier] = m.SourceRpm
}

return packageurl.NewPackageURL(
packageurl.TypeRPM,
namespace,
Expand Down
36 changes: 25 additions & 11 deletions syft/pkg/rpmdb_metadata_test.go
Expand Up @@ -18,33 +18,33 @@ func TestRpmMetadata_pURL(t *testing.T) {
expected string
}{
{
name: "with arch and epoch",
name: "go case",
distro: &linux.Release{
ID: "centos",
VersionID: "7",
ID: "rhel",
VersionID: "8.4",
},
metadata: RpmdbMetadata{
Name: "p",
Version: "v",
Arch: "a",
Release: "r",
Epoch: intRef(1),
Epoch: nil,
},
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1&distro=centos-7",
expected: "pkg:rpm/rhel/p@v-r?distro=rhel-8.4",
},
{
name: "go case",
name: "with arch and epoch",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
ID: "centos",
VersionID: "7",
},
metadata: RpmdbMetadata{
Name: "p",
Version: "v",
Arch: "a",
Release: "r",
Epoch: nil,
Epoch: intRef(1),
},
expected: "pkg:rpm/rhel/p@v-r?distro=rhel-8.4",
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1&distro=centos-7",
},
{
name: "missing distro",
Expand All @@ -56,6 +56,20 @@ func TestRpmMetadata_pURL(t *testing.T) {
},
expected: "pkg:rpm/p@v-r",
},
{
name: "with upstream source rpm info",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
},
metadata: RpmdbMetadata{
Name: "p",
Version: "v",
Release: "r",
SourceRpm: "sourcerpm",
},
expected: "pkg:rpm/rhel/p@v-r?upstream=sourcerpm&distro=rhel-8.4",
},
}

for _, test := range tests {
Expand Down
5 changes: 4 additions & 1 deletion syft/pkg/url.go
Expand Up @@ -14,7 +14,10 @@ const (
purlArchQualifier = "arch"
purlDistroQualifier = "distro"
purlEpochQualifier = "epoch"
purlVCSURL = "vcs_url"
purlVCSURLQualifier = "vcs_url"

// this qualifier is not in the pURL spec, but is used by grype to perform indirect matching based on source information
purlUpstreamQualifier = "upstream"
)

type urlIdentifier interface {
Expand Down

0 comments on commit bce378b

Please sign in to comment.