mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 08:53:15 +01:00
Improve package URL support (#754)
* rename npm metadata struct Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * improve os package URLs Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * improve language package URLs Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * wire up composer pURL method Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
c350bd55f6
commit
1350d6c5bf
@ -3,15 +3,18 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ApkDBGlob = "**/lib/apk/db/installed"
|
const ApkDBGlob = "**/lib/apk/db/installed"
|
||||||
|
|
||||||
var _ FileOwner = (*ApkMetadata)(nil)
|
var (
|
||||||
|
_ FileOwner = (*ApkMetadata)(nil)
|
||||||
|
_ urlIdentifier = (*ApkMetadata)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// ApkMetadata represents all captured data for a Alpine DB package entry.
|
// ApkMetadata represents all captured data for a Alpine DB package entry.
|
||||||
// See the following sources for more information:
|
// See the following sources for more information:
|
||||||
@ -45,22 +48,22 @@ type ApkFileRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
||||||
func (m ApkMetadata) PackageURL() string {
|
func (m ApkMetadata) PackageURL(distro *linux.Release) string {
|
||||||
pURL := packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
// note: this is currently a candidate and not technically within spec
|
// note: this is currently a candidate and not technically within spec
|
||||||
// see https://github.com/package-url/purl-spec#other-candidate-types-to-define
|
// see https://github.com/package-url/purl-spec#other-candidate-types-to-define
|
||||||
"alpine",
|
"alpine",
|
||||||
"",
|
"",
|
||||||
m.Package,
|
m.Package,
|
||||||
m.Version,
|
m.Version,
|
||||||
packageurl.Qualifiers{
|
purlQualifiers(
|
||||||
{
|
map[string]string{
|
||||||
Key: "arch",
|
purlArchQualifier: m.Architecture,
|
||||||
Value: m.Architecture,
|
|
||||||
},
|
},
|
||||||
},
|
distro,
|
||||||
"")
|
),
|
||||||
return pURL.ToString()
|
"",
|
||||||
|
).ToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m ApkMetadata) OwnedFiles() (result []string) {
|
func (m ApkMetadata) OwnedFiles() (result []string) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -11,16 +12,35 @@ import (
|
|||||||
|
|
||||||
func TestApkMetadata_pURL(t *testing.T) {
|
func TestApkMetadata_pURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
name string
|
||||||
metadata ApkMetadata
|
metadata ApkMetadata
|
||||||
|
distro linux.Release
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
name: "gocase",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "p",
|
Package: "p",
|
||||||
Version: "v",
|
Version: "v",
|
||||||
Architecture: "a",
|
Architecture: "a",
|
||||||
},
|
},
|
||||||
expected: "pkg:alpine/p@v?arch=a",
|
distro: linux.Release{
|
||||||
|
ID: "alpine",
|
||||||
|
VersionID: "3.4.6",
|
||||||
|
},
|
||||||
|
expected: "pkg:alpine/p@v?arch=a&distro=alpine-3.4.6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing architecure",
|
||||||
|
metadata: ApkMetadata{
|
||||||
|
Package: "p",
|
||||||
|
Version: "v",
|
||||||
|
},
|
||||||
|
distro: linux.Release{
|
||||||
|
ID: "alpine",
|
||||||
|
VersionID: "3.4.6",
|
||||||
|
},
|
||||||
|
expected: "pkg:alpine/p@v?distro=alpine-3.4.6",
|
||||||
},
|
},
|
||||||
// verify #351
|
// verify #351
|
||||||
{
|
{
|
||||||
@ -29,7 +49,11 @@ func TestApkMetadata_pURL(t *testing.T) {
|
|||||||
Version: "v84",
|
Version: "v84",
|
||||||
Architecture: "am86",
|
Architecture: "am86",
|
||||||
},
|
},
|
||||||
expected: "pkg:alpine/g++@v84?arch=am86",
|
distro: linux.Release{
|
||||||
|
ID: "alpine",
|
||||||
|
VersionID: "3.4.6",
|
||||||
|
},
|
||||||
|
expected: "pkg:alpine/g++@v84?arch=am86&distro=alpine-3.4.6",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
@ -37,13 +61,17 @@ func TestApkMetadata_pURL(t *testing.T) {
|
|||||||
Version: "v84",
|
Version: "v84",
|
||||||
Architecture: "am86",
|
Architecture: "am86",
|
||||||
},
|
},
|
||||||
expected: "pkg:alpine/g%20plus%20plus@v84?arch=am86",
|
distro: linux.Release{
|
||||||
|
ID: "alpine",
|
||||||
|
VersionID: "3.15.0",
|
||||||
|
},
|
||||||
|
expected: "pkg:alpine/g%20plus%20plus@v84?arch=am86&distro=alpine-3.15.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.expected, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := test.metadata.PackageURL()
|
actual := test.metadata.PackageURL(&test.distro)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
@ -68,7 +68,7 @@ func Catalog(resolver source.FileResolver, release *linux.Release, catalogers ..
|
|||||||
p.CPEs = cpe.Generate(p)
|
p.CPEs = cpe.Generate(p)
|
||||||
|
|
||||||
// generate PURL (note: this is excluded from package ID, so is safe to mutate)
|
// generate PURL (note: this is excluded from package ID, so is safe to mutate)
|
||||||
p.PURL = generatePackageURL(p, release)
|
p.PURL = pkg.URL(p, release)
|
||||||
|
|
||||||
// create file-to-package relationships for files owned by the package
|
// create file-to-package relationships for files owned by the package
|
||||||
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
|
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
package cataloger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// generatePackageURL returns a package-URL representation of the given package (see https://github.com/package-url/purl-spec)
|
|
||||||
func generatePackageURL(p pkg.Package, release *linux.Release) string {
|
|
||||||
// default to pURLs on the metadata
|
|
||||||
if p.Metadata != nil {
|
|
||||||
if i, ok := p.Metadata.(interface{ PackageURL() string }); ok {
|
|
||||||
return i.PackageURL()
|
|
||||||
} else if i, ok := p.Metadata.(interface{ PackageURL(*linux.Release) string }); ok {
|
|
||||||
return i.PackageURL(release)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var purlType = p.Type.PackageURLType()
|
|
||||||
var name = p.Name
|
|
||||||
var namespace = ""
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case purlType == "":
|
|
||||||
// there is no purl type, don't attempt to craft a purl
|
|
||||||
// TODO: should this be a "generic" purl type instead?
|
|
||||||
return ""
|
|
||||||
case p.Type == pkg.GoModulePkg:
|
|
||||||
re := regexp.MustCompile(`(/)[^/]*$`)
|
|
||||||
fields := re.Split(p.Name, -1)
|
|
||||||
namespace = fields[0]
|
|
||||||
name = strings.TrimPrefix(p.Name, namespace+"/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a purl from the package data
|
|
||||||
pURL := packageurl.NewPackageURL(
|
|
||||||
purlType,
|
|
||||||
namespace,
|
|
||||||
name,
|
|
||||||
p.Version,
|
|
||||||
nil,
|
|
||||||
"")
|
|
||||||
|
|
||||||
return pURL.ToString()
|
|
||||||
}
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
package cataloger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPackageURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pkg pkg.Package
|
|
||||||
distro *linux.Release
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "golang",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "github.com/anchore/syft",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: pkg.GoModulePkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:golang/github.com/anchore/syft@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pip with vcs url",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: pkg.PythonPkg,
|
|
||||||
Metadata: pkg.PythonPackageMetadata{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
DirectURLOrigin: &pkg.PythonDirectURLOriginInfo{
|
|
||||||
VCS: "git",
|
|
||||||
URL: "https://github.com/test/test.git",
|
|
||||||
CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "pkg:pypi/name@v0.1.0?vcs_url=git+https:%2F%2Fgithub.com%2Ftest%2Ftest.git@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pip without vcs url",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: pkg.PythonPkg,
|
|
||||||
Metadata: pkg.PythonPackageMetadata{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "pkg:pypi/name@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "gem",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: pkg.GemPkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:gem/name@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "npm",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: pkg.NpmPkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:npm/name@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "deb with arch",
|
|
||||||
distro: &linux.Release{
|
|
||||||
ID: "ubuntu",
|
|
||||||
},
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: pkg.DebPkg,
|
|
||||||
Metadata: pkg.DpkgMetadata{
|
|
||||||
Package: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Architecture: "amd64",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "pkg:deb/ubuntu/name@v0.1.0?arch=amd64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "deb with epoch",
|
|
||||||
distro: &linux.Release{
|
|
||||||
ID: "centos",
|
|
||||||
},
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: pkg.RpmPkg,
|
|
||||||
Metadata: pkg.RpmdbMetadata{
|
|
||||||
Name: "name",
|
|
||||||
Version: "0.1.0",
|
|
||||||
Epoch: intRef(2),
|
|
||||||
Arch: "amd64",
|
|
||||||
Release: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "pkg:rpm/centos/name@0.1.0-3?arch=amd64&epoch=2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "deb with nil epoch",
|
|
||||||
distro: &linux.Release{
|
|
||||||
ID: "centos",
|
|
||||||
},
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: pkg.RpmPkg,
|
|
||||||
Metadata: pkg.RpmdbMetadata{
|
|
||||||
Name: "name",
|
|
||||||
Version: "0.1.0",
|
|
||||||
Epoch: nil,
|
|
||||||
Arch: "amd64",
|
|
||||||
Release: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "pkg:rpm/centos/name@0.1.0-3?arch=amd64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "deb with unknown distro",
|
|
||||||
distro: nil,
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: pkg.DebPkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:deb/name@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cargo",
|
|
||||||
pkg: pkg.Package{
|
|
||||||
Name: "name",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: pkg.RustPkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:cargo/name@v0.1.0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
actual := generatePackageURL(test.pkg, test.distro)
|
|
||||||
if actual != test.expected {
|
|
||||||
dmp := diffmatchpatch.New()
|
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
|
||||||
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func intRef(i int) *int {
|
|
||||||
return &i
|
|
||||||
}
|
|
||||||
@ -12,7 +12,10 @@ import (
|
|||||||
|
|
||||||
const DpkgDBGlob = "**/var/lib/dpkg/{status,status.d/**}"
|
const DpkgDBGlob = "**/var/lib/dpkg/{status,status.d/**}"
|
||||||
|
|
||||||
var _ FileOwner = (*DpkgMetadata)(nil)
|
var (
|
||||||
|
_ FileOwner = (*DpkgMetadata)(nil)
|
||||||
|
_ urlIdentifier = (*DpkgMetadata)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described
|
// DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described
|
||||||
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
|
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
|
||||||
@ -36,24 +39,25 @@ type DpkgFileRecord struct {
|
|||||||
|
|
||||||
// PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec)
|
||||||
func (m DpkgMetadata) PackageURL(distro *linux.Release) string {
|
func (m DpkgMetadata) PackageURL(distro *linux.Release) string {
|
||||||
if distro == nil {
|
var namespace string
|
||||||
return ""
|
if distro != nil {
|
||||||
|
namespace = distro.ID
|
||||||
}
|
}
|
||||||
pURL := packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
// TODO: replace with `packageurl.TypeDebian` upon merge of https://github.com/package-url/packageurl-go/pull/21
|
// 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.
|
// TODO: or, since we're now using an Anchore fork of this module, we could do this sooner.
|
||||||
"deb",
|
"deb",
|
||||||
distro.ID,
|
namespace,
|
||||||
m.Package,
|
m.Package,
|
||||||
m.Version,
|
m.Version,
|
||||||
packageurl.Qualifiers{
|
purlQualifiers(
|
||||||
{
|
map[string]string{
|
||||||
Key: "arch",
|
purlArchQualifier: m.Architecture,
|
||||||
Value: m.Architecture,
|
|
||||||
},
|
},
|
||||||
},
|
distro,
|
||||||
"")
|
),
|
||||||
return pURL.ToString()
|
"",
|
||||||
|
).ToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m DpkgMetadata) OwnedFiles() (result []string) {
|
func (m DpkgMetadata) OwnedFiles() (result []string) {
|
||||||
|
|||||||
@ -12,25 +12,29 @@ import (
|
|||||||
|
|
||||||
func TestDpkgMetadata_pURL(t *testing.T) {
|
func TestDpkgMetadata_pURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
distro linux.Release
|
name string
|
||||||
|
distro *linux.Release
|
||||||
metadata DpkgMetadata
|
metadata DpkgMetadata
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
distro: linux.Release{
|
name: "go case",
|
||||||
ID: "debian",
|
distro: &linux.Release{
|
||||||
|
ID: "debian",
|
||||||
|
VersionID: "11",
|
||||||
},
|
},
|
||||||
metadata: DpkgMetadata{
|
metadata: DpkgMetadata{
|
||||||
Package: "p",
|
Package: "p",
|
||||||
Source: "s",
|
Source: "s",
|
||||||
Version: "v",
|
Version: "v",
|
||||||
Architecture: "a",
|
|
||||||
},
|
},
|
||||||
expected: "pkg:deb/debian/p@v?arch=a",
|
expected: "pkg:deb/debian/p@v?distro=debian-11",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
distro: linux.Release{
|
name: "with arch info",
|
||||||
ID: "ubuntu",
|
distro: &linux.Release{
|
||||||
|
ID: "ubuntu",
|
||||||
|
VersionID: "16.04",
|
||||||
},
|
},
|
||||||
metadata: DpkgMetadata{
|
metadata: DpkgMetadata{
|
||||||
Package: "p",
|
Package: "p",
|
||||||
@ -38,13 +42,22 @@ func TestDpkgMetadata_pURL(t *testing.T) {
|
|||||||
Version: "v",
|
Version: "v",
|
||||||
Architecture: "a",
|
Architecture: "a",
|
||||||
},
|
},
|
||||||
expected: "pkg:deb/ubuntu/p@v?arch=a",
|
expected: "pkg:deb/ubuntu/p@v?arch=a&distro=ubuntu-16.04",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing distro",
|
||||||
|
metadata: DpkgMetadata{
|
||||||
|
Package: "p",
|
||||||
|
Source: "s",
|
||||||
|
Version: "v",
|
||||||
|
},
|
||||||
|
expected: "pkg:deb/p@v",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.expected, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := test.metadata.PackageURL(&test.distro)
|
actual := test.metadata.PackageURL(test.distro)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
@ -3,10 +3,14 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
"github.com/anchore/packageurl-go"
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ urlIdentifier = (*JavaMetadata)(nil)
|
||||||
|
|
||||||
var JenkinsPluginPomPropertiesGroupIDs = []string{
|
var JenkinsPluginPomPropertiesGroupIDs = []string{
|
||||||
"io.jenkins.plugins",
|
"io.jenkins.plugins",
|
||||||
"org.jenkins.plugins",
|
"org.jenkins.plugins",
|
||||||
@ -69,7 +73,7 @@ type JavaManifest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
||||||
func (m JavaMetadata) PackageURL() string {
|
func (m JavaMetadata) PackageURL(_ *linux.Release) string {
|
||||||
if m.PomProperties != nil {
|
if m.PomProperties != nil {
|
||||||
pURL := packageurl.NewPackageURL(
|
pURL := packageurl.NewPackageURL(
|
||||||
packageurl.TypeMaven,
|
packageurl.TypeMaven,
|
||||||
|
|||||||
@ -136,7 +136,7 @@ func TestJavaMetadata_pURL(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.expected, func(t *testing.T) {
|
t.Run(test.expected, func(t *testing.T) {
|
||||||
actual := test.metadata.PackageURL()
|
actual := test.metadata.PackageURL(nil)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
@ -4,8 +4,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ urlIdentifier = (*PhpComposerJSONMetadata)(nil)
|
||||||
|
|
||||||
// PhpComposerJSONMetadata represents information found from composer v1/v2 "installed.json" files as well as composer.lock files
|
// PhpComposerJSONMetadata represents information found from composer v1/v2 "installed.json" files as well as composer.lock files
|
||||||
type PhpComposerJSONMetadata struct {
|
type PhpComposerJSONMetadata struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -40,7 +43,7 @@ type PhpComposerAuthors struct {
|
|||||||
Homepage string `json:"homepage,omitempty"`
|
Homepage string `json:"homepage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m PhpComposerJSONMetadata) PackageURL() string {
|
func (m PhpComposerJSONMetadata) PackageURL(_ *linux.Release) string {
|
||||||
var name, vendor string
|
var name, vendor string
|
||||||
fields := strings.Split(m.Name, "/")
|
fields := strings.Split(m.Name, "/")
|
||||||
switch len(fields) {
|
switch len(fields) {
|
||||||
|
|||||||
@ -20,7 +20,8 @@ func TestPhpComposerJsonMetadata_pURL(t *testing.T) {
|
|||||||
Version: "1.0.1",
|
Version: "1.0.1",
|
||||||
},
|
},
|
||||||
expected: "pkg:composer/ven/name@1.0.1",
|
expected: "pkg:composer/ven/name@1.0.1",
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: "name with slashes (invalid)",
|
name: "name with slashes (invalid)",
|
||||||
metadata: PhpComposerJSONMetadata{
|
metadata: PhpComposerJSONMetadata{
|
||||||
Name: "ven/name/component",
|
Name: "ven/name/component",
|
||||||
@ -36,11 +37,23 @@ func TestPhpComposerJsonMetadata_pURL(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: "pkg:composer/name@1.0.1",
|
expected: "pkg:composer/name@1.0.1",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ignores distro",
|
||||||
|
distro: &linux.Release{
|
||||||
|
ID: "rhel",
|
||||||
|
VersionID: "8.4",
|
||||||
|
},
|
||||||
|
metadata: PhpComposerJSONMetadata{
|
||||||
|
Name: "ven/name",
|
||||||
|
Version: "1.0.1",
|
||||||
|
},
|
||||||
|
expected: "pkg:composer/ven/name@1.0.1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := test.metadata.PackageURL()
|
actual := test.metadata.PackageURL(test.distro)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
@ -4,11 +4,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
"github.com/anchore/packageurl-go"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ FileOwner = (*PythonPackageMetadata)(nil)
|
var (
|
||||||
|
_ FileOwner = (*PythonPackageMetadata)(nil)
|
||||||
|
_ urlIdentifier = (*PythonPackageMetadata)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// PythonFileDigest represents the file metadata for a single file attributed to a python package.
|
// PythonFileDigest represents the file metadata for a single file attributed to a python package.
|
||||||
type PythonFileDigest struct {
|
type PythonFileDigest struct {
|
||||||
@ -76,7 +81,7 @@ func (m PythonPackageMetadata) OwnedFiles() (result []string) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m PythonPackageMetadata) PackageURL() string {
|
func (m PythonPackageMetadata) PackageURL(_ *linux.Release) string {
|
||||||
// generate a purl from the package data
|
// generate a purl from the package data
|
||||||
pURL := packageurl.NewPackageURL(
|
pURL := packageurl.NewPackageURL(
|
||||||
packageurl.TypePyPi,
|
packageurl.TypePyPi,
|
||||||
@ -101,7 +106,7 @@ func (p PythonDirectURLOriginInfo) vcsURLQualifier() packageurl.Qualifiers {
|
|||||||
if p.VCS != "" {
|
if p.VCS != "" {
|
||||||
// Taken from https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#known-qualifiers-keyvalue-pairs
|
// 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
|
// packageurl-go still doesn't support all qualifier names
|
||||||
return packageurl.Qualifiers{{Key: "vcs_url", Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
|
return packageurl.Qualifiers{{Key: purlVCSURL, Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
|
||||||
}
|
}
|
||||||
return packageurl.Qualifiers{}
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,60 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPythonPackageMetadata_pURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
distro *linux.Release
|
||||||
|
metadata PythonPackageMetadata
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with vcs info",
|
||||||
|
metadata: PythonPackageMetadata{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
DirectURLOrigin: &PythonDirectURLOriginInfo{
|
||||||
|
VCS: "git",
|
||||||
|
URL: "https://github.com/test/test.git",
|
||||||
|
CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:pypi/name@v0.1.0?vcs_url=git+https:%2F%2Fgithub.com%2Ftest%2Ftest.git@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should not respond to release info",
|
||||||
|
distro: &linux.Release{
|
||||||
|
ID: "rhel",
|
||||||
|
VersionID: "8.4",
|
||||||
|
},
|
||||||
|
metadata: PythonPackageMetadata{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
},
|
||||||
|
expected: "pkg:pypi/name@v0.1.0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
actual := test.metadata.PackageURL(test.distro)
|
||||||
|
if actual != test.expected {
|
||||||
|
dmp := diffmatchpatch.New()
|
||||||
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPythonMetadata_FileOwner(t *testing.T) {
|
func TestPythonMetadata_FileOwner(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
metadata PythonPackageMetadata
|
metadata PythonPackageMetadata
|
||||||
|
|||||||
@ -15,7 +15,10 @@ import (
|
|||||||
|
|
||||||
const RpmDBGlob = "**/var/lib/rpm/Packages"
|
const RpmDBGlob = "**/var/lib/rpm/Packages"
|
||||||
|
|
||||||
var _ FileOwner = (*RpmdbMetadata)(nil)
|
var (
|
||||||
|
_ FileOwner = (*RpmdbMetadata)(nil)
|
||||||
|
_ urlIdentifier = (*RpmdbMetadata)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// RpmdbMetadata represents all captured data for a RPM DB package entry.
|
// RpmdbMetadata represents all captured data for a RPM DB package entry.
|
||||||
type RpmdbMetadata struct {
|
type RpmdbMetadata struct {
|
||||||
@ -47,36 +50,32 @@ type RpmdbFileMode uint16
|
|||||||
|
|
||||||
// PackageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec)
|
||||||
func (m RpmdbMetadata) PackageURL(distro *linux.Release) string {
|
func (m RpmdbMetadata) PackageURL(distro *linux.Release) string {
|
||||||
if distro == nil {
|
var namespace string
|
||||||
return ""
|
if distro != nil {
|
||||||
|
namespace = distro.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
qualifiers := packageurl.Qualifiers{
|
qualifiers := map[string]string{
|
||||||
{
|
purlArchQualifier: m.Arch,
|
||||||
Key: "arch",
|
|
||||||
Value: m.Arch,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Epoch != nil {
|
if m.Epoch != nil {
|
||||||
qualifiers = append(qualifiers,
|
qualifiers[purlEpochQualifier] = strconv.Itoa(*m.Epoch)
|
||||||
packageurl.Qualifier{
|
|
||||||
Key: "epoch",
|
|
||||||
Value: strconv.Itoa(*m.Epoch),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pURL := packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
packageurl.TypeRPM,
|
packageurl.TypeRPM,
|
||||||
distro.ID,
|
namespace,
|
||||||
m.Name,
|
m.Name,
|
||||||
// for purl the epoch is a qualifier, not part of the version
|
// for purl the epoch is a qualifier, not part of the version
|
||||||
// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section
|
// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section
|
||||||
fmt.Sprintf("%s-%s", m.Version, m.Release),
|
fmt.Sprintf("%s-%s", m.Version, m.Release),
|
||||||
qualifiers,
|
purlQualifiers(
|
||||||
"")
|
qualifiers,
|
||||||
return pURL.ToString()
|
distro,
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
).ToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m RpmdbMetadata) OwnedFiles() (result []string) {
|
func (m RpmdbMetadata) OwnedFiles() (result []string) {
|
||||||
|
|||||||
@ -12,13 +12,16 @@ import (
|
|||||||
|
|
||||||
func TestRpmMetadata_pURL(t *testing.T) {
|
func TestRpmMetadata_pURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
distro linux.Release
|
name string
|
||||||
|
distro *linux.Release
|
||||||
metadata RpmdbMetadata
|
metadata RpmdbMetadata
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
distro: linux.Release{
|
name: "with arch and epoch",
|
||||||
ID: "centos",
|
distro: &linux.Release{
|
||||||
|
ID: "centos",
|
||||||
|
VersionID: "7",
|
||||||
},
|
},
|
||||||
metadata: RpmdbMetadata{
|
metadata: RpmdbMetadata{
|
||||||
Name: "p",
|
Name: "p",
|
||||||
@ -27,26 +30,37 @@ func TestRpmMetadata_pURL(t *testing.T) {
|
|||||||
Release: "r",
|
Release: "r",
|
||||||
Epoch: intRef(1),
|
Epoch: intRef(1),
|
||||||
},
|
},
|
||||||
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1",
|
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1&distro=centos-7",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
distro: linux.Release{
|
name: "go case",
|
||||||
ID: "rhel",
|
distro: &linux.Release{
|
||||||
|
ID: "rhel",
|
||||||
|
VersionID: "8.4",
|
||||||
},
|
},
|
||||||
metadata: RpmdbMetadata{
|
metadata: RpmdbMetadata{
|
||||||
Name: "p",
|
Name: "p",
|
||||||
Version: "v",
|
Version: "v",
|
||||||
Arch: "a",
|
|
||||||
Release: "r",
|
Release: "r",
|
||||||
Epoch: nil,
|
Epoch: nil,
|
||||||
},
|
},
|
||||||
expected: "pkg:rpm/rhel/p@v-r?arch=a",
|
expected: "pkg:rpm/rhel/p@v-r?distro=rhel-8.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing distro",
|
||||||
|
metadata: RpmdbMetadata{
|
||||||
|
Name: "p",
|
||||||
|
Version: "v",
|
||||||
|
Release: "r",
|
||||||
|
Epoch: nil,
|
||||||
|
},
|
||||||
|
expected: "pkg:rpm/p@v-r",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.expected, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := test.metadata.PackageURL(&test.distro)
|
actual := test.metadata.PackageURL(test.distro)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
86
syft/pkg/url.go
Normal file
86
syft/pkg/url.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
purlArchQualifier = "arch"
|
||||||
|
purlDistroQualifier = "distro"
|
||||||
|
purlEpochQualifier = "epoch"
|
||||||
|
purlVCSURL = "vcs_url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type urlIdentifier interface {
|
||||||
|
PackageURL(*linux.Release) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func URL(p Package, release *linux.Release) string {
|
||||||
|
if p.Metadata != nil {
|
||||||
|
if i, ok := p.Metadata.(urlIdentifier); ok {
|
||||||
|
return i.PackageURL(release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the remaining cases are primarily reserved for packages without metadata struct instances
|
||||||
|
|
||||||
|
var purlType = p.Type.PackageURLType()
|
||||||
|
var name = p.Name
|
||||||
|
var namespace = ""
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case purlType == "":
|
||||||
|
// there is no purl type, don't attempt to craft a purl
|
||||||
|
// TODO: should this be a "generic" purl type instead?
|
||||||
|
return ""
|
||||||
|
case p.Type == GoModulePkg:
|
||||||
|
re := regexp.MustCompile(`(/)[^/]*$`)
|
||||||
|
fields := re.Split(p.Name, -1)
|
||||||
|
namespace = fields[0]
|
||||||
|
name = strings.TrimPrefix(p.Name, namespace+"/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a purl from the package data
|
||||||
|
return packageurl.NewPackageURL(
|
||||||
|
purlType,
|
||||||
|
namespace,
|
||||||
|
name,
|
||||||
|
p.Version,
|
||||||
|
nil,
|
||||||
|
"",
|
||||||
|
).ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func purlQualifiers(vars map[string]string, release *linux.Release) (q packageurl.Qualifiers) {
|
||||||
|
keys := make([]string, 0, len(vars))
|
||||||
|
for k := range vars {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
val := vars[k]
|
||||||
|
if val == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q = append(q, packageurl.Qualifier{
|
||||||
|
Key: k,
|
||||||
|
Value: vars[k],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if release != nil && release.ID != "" && release.VersionID != "" {
|
||||||
|
q = append(q, packageurl.Qualifier{
|
||||||
|
Key: purlDistroQualifier,
|
||||||
|
Value: fmt.Sprintf("%s-%s", release.ID, release.VersionID),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
200
syft/pkg/url_test.go
Normal file
200
syft/pkg/url_test.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPackageURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pkg Package
|
||||||
|
distro *linux.Release
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "golang",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "github.com/anchore/syft",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Type: GoModulePkg,
|
||||||
|
},
|
||||||
|
expected: "pkg:golang/github.com/anchore/syft@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "python",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: PythonPkg,
|
||||||
|
Metadata: PythonPackageMetadata{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:pypi/name@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gem",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Type: GemPkg,
|
||||||
|
},
|
||||||
|
expected: "pkg:gem/name@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "npm",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Type: NpmPkg,
|
||||||
|
},
|
||||||
|
expected: "pkg:npm/name@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deb",
|
||||||
|
distro: &linux.Release{
|
||||||
|
ID: "ubuntu",
|
||||||
|
VersionID: "20.04",
|
||||||
|
},
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: DebPkg,
|
||||||
|
Metadata: DpkgMetadata{
|
||||||
|
Package: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Architecture: "amd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:deb/ubuntu/name@v0.1.0?arch=amd64&distro=ubuntu-20.04",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rpm",
|
||||||
|
distro: &linux.Release{
|
||||||
|
ID: "centos",
|
||||||
|
VersionID: "7",
|
||||||
|
},
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: RpmPkg,
|
||||||
|
Metadata: RpmdbMetadata{
|
||||||
|
Name: "name",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Epoch: intRef(2),
|
||||||
|
Arch: "amd64",
|
||||||
|
Release: "3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:rpm/centos/name@0.1.0-3?arch=amd64&epoch=2&distro=centos-7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cargo",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Type: RustPkg,
|
||||||
|
},
|
||||||
|
expected: "pkg:cargo/name@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "apk",
|
||||||
|
distro: &linux.Release{
|
||||||
|
ID: "alpine",
|
||||||
|
VersionID: "3.4.6",
|
||||||
|
},
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: ApkPkg,
|
||||||
|
Metadata: ApkMetadata{
|
||||||
|
Package: "name",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
Architecture: "amd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:alpine/name@v0.1.0?arch=amd64&distro=alpine-3.4.6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "php-composer",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: PhpComposerPkg,
|
||||||
|
Metadata: PhpComposerJSONMetadata{
|
||||||
|
Name: "vendor/name",
|
||||||
|
Version: "2.0.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "pkg:composer/vendor/name@2.0.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "java",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: JavaPkg,
|
||||||
|
Metadata: JavaMetadata{
|
||||||
|
PomProperties: &PomProperties{
|
||||||
|
Path: "p",
|
||||||
|
Name: "n",
|
||||||
|
GroupID: "g.id",
|
||||||
|
ArtifactID: "a",
|
||||||
|
Version: "v",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
expected: "pkg:maven/g.id/a@v",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jenkins-plugin",
|
||||||
|
pkg: Package{
|
||||||
|
Name: "bad-name",
|
||||||
|
Version: "bad-v0.1.0",
|
||||||
|
Type: JenkinsPluginPkg,
|
||||||
|
Metadata: JavaMetadata{
|
||||||
|
PomProperties: &PomProperties{
|
||||||
|
Path: "p",
|
||||||
|
Name: "n",
|
||||||
|
GroupID: "g.id",
|
||||||
|
ArtifactID: "a",
|
||||||
|
Version: "v",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
expected: "pkg:maven/g.id/a@v",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkgTypes []string
|
||||||
|
var expectedTypes = strset.New()
|
||||||
|
for _, ty := range AllPkgs {
|
||||||
|
expectedTypes.Add(string(ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
// testing microsoft packages is not valid for purl at this time
|
||||||
|
expectedTypes.Remove(string(KbPkg))
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.pkg.Type != "" {
|
||||||
|
pkgTypes = append(pkgTypes, string(test.pkg.Type))
|
||||||
|
}
|
||||||
|
actual := URL(test.pkg, test.distro)
|
||||||
|
if actual != test.expected {
|
||||||
|
dmp := diffmatchpatch.New()
|
||||||
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, expectedTypes.List(), pkgTypes, "missing one or more package types to test against (maybe a package type was added?)")
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user