feat(swift): add vulns for cocoapods (#5037)

* add vulns for cocoapods, fix purl

* update docs

* remove go-dep-parser replace

* update purl and test

* bump github.com/DmitriyLewen/trivy-db

* remove replace for trivy-db

* remove added sbom tests

* add test for Package() func

* add wrong epoch test

* refactor docs

* add comment to join the module and submodule in purl

* docs: add an example

---------

Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
DmitriyLewen
2023-08-29 18:46:49 +06:00
committed by GitHub
parent 422fa414e8
commit 9c211d005d
13 changed files with 332 additions and 74 deletions

View File

@@ -17,7 +17,7 @@ If the target is a pre-build project, like a code repository, Trivy will analyze
On the other hand, when the target is a post-build artifact, like a container image, Trivy will analyze installed package metadata like `.gemspec`, binary files, and so on.
| Language | File | Image[^5] | Rootfs[^6] | Filesystem[^7] | Repository[^8] |
| -------------------- | ------------------------------------------------------------------------------------------ | :-------: | :--------: | :------------: | :------------: |
|----------------------|--------------------------------------------------------------------------------------------|:---------:|:----------:|:--------------:|:--------------:|
| [Ruby](ruby.md) | Gemfile.lock | - | - | ✅ | ✅ |
| | gemspec | ✅ | ✅ | - | - |
| [Python](python.md) | Pipfile.lock | - | - | ✅ | ✅ |
@@ -45,6 +45,7 @@ On the other hand, when the target is a post-build artifact, like a container im
| [Elixir](elixir.md) | mix.lock[^10] | - | - | ✅ | ✅ |
| [Dart](dart.md) | pubspec.lock | - | - | ✅ | ✅ |
| [Swift](swift.md) | Podfile.lock | - | - | ✅ | ✅ |
| | Package.resolved | - | - | ✅ | ✅ |
The path of these files does not matter.

View File

@@ -1,10 +1,44 @@
# Swift
Trivy supports [CocoaPods][cocoapods] for Swift packages.
Trivy supports [CocoaPods][cocoapods] and [Swift][swift] package managers.
The following scanners are supported.
| Package manager | SBOM | Vulnerability | License |
| --------------- | :---: | :-----------: | :-----: |
| CocoaPods | ✓ | - | - |
| Package manager | SBOM | Vulnerability | License |
|-----------------|:----:|:-------------:|:-------:|
| Swift | ✓ | | - |
| CocoaPods | ✓ | ✓ | - |
[cocoapods]: https://cocoapods.org/
The following table provides an outline of the features Trivy offers.
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position |
|:---------------:|------------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|
| Swift | Package.resolved | ✓ | Included | - | ✓ |
| CocoaPods | Podfile.lock | ✓ | Included | ✓ | - |
These may be enabled or disabled depending on the target.
See [here](./index.md) for the detail.
## Swift
Trivy parses [Package.resolved][package-resolved] file to find dependencies.
Don't forget to update (`swift package update` command) this file before scanning.
## CocoaPods
CocoaPods uses package names in `PodFile.lock`, but [GitHub Advisory Database (GHSA)][ghsa] Trivy relies on uses Git URLs.
We parse [the CocoaPods Specs][cocoapods-specs] to match package names and links.
!!! note "Limitation"
Since [GHSA][ghsa] holds only Git URLs, such as github.com/apple/swift-nio,
Trivy can't identify affected submodules, and detect all submodules maintained by the same URL.
For example, [SwiftNIOHTTP1][niohttp1] and [SwiftNIOWebSocket][niowebsocket] both are maintained under `github.com/apple/swift-nio`,
and Trivy detect CVE-2022-3215 for both of them, even though only [SwiftNIOHTTP1][niohttp1] is actually affected.
[cocoapods]: https://cocoapods.org/
[cocoapods-specs]: https://github.com/CocoaPods/Specs
[ghsa]: https://github.com/advisories?query=type%3Areviewed+ecosystem%3Aswift
[swift]: https://www.swift.org/package-manager/
[package-resolved]: https://github.com/apple/swift-package-manager/blob/4a42f2519e3f7b8a731c5ed89b47ed577df8f86c/Documentation/Usage.md#resolving-versions-packageresolved-file
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
[niohttp1]: https://cocoapods.org/pods/SwiftNIOHTTP1
[niowebsocket]: https://cocoapods.org/pods/SwiftNIOWebSocket

View File

@@ -77,7 +77,7 @@ See [here](../coverage/language/index.md#supported-languages) for the supported
### Data Sources
| Language | Source | Commercial Use | Delay[^1] |
| -------- | --------------------------------------------------- | :------------: | :-------: |
|----------|-----------------------------------------------------|:--------------:|:---------:|
| PHP | [PHP Security Advisories Database][php] | ✅ | - |
| | [GitHub Advisory Database (Composer)][php-ghsa] | ✅ | - |
| Python | [GitHub Advisory Database (pip)][python-ghsa] | ✅ | - |
@@ -93,7 +93,8 @@ See [here](../coverage/language/index.md#supported-languages) for the supported
| .NET | [GitHub Advisory Database (NuGet)][dotnet-ghsa] | ✅ | - |
| C/C++ | [GitLab Advisories Community][gitlab] | ✅ | 1 month |
| Dart | [GitHub Advisory Database (Pub)][pub-ghsa] | ✅ | - |
| Elixir | [GitHub Advisory Database (Erlang)][erlang-ghsa] | ✅ | |
| Elixir | [GitHub Advisory Database (Erlang)][erlang-ghsa] | ✅ | - |
| Swift | [GitHub Advisory Database (Swift)][swift-ghsa] | ✅ | - |
[^1]: Intentional delay between vulnerability disclosure and registration in the DB
@@ -168,6 +169,7 @@ Currently, specifying a username and password is not supported.
[pub-ghsa]: https://github.com/advisories?query=ecosystem%3Apub
[erlang-ghsa]: https://github.com/advisories?query=ecosystem%3Aerlang
[go-ghsa]: https://github.com/advisories?query=ecosystem%3Ago
[swift-ghsa]: https://github.com/advisories?query=ecosystem%3Aswift
[php]: https://github.com/FriendsOfPHP/security-advisories
[ruby]: https://github.com/rubysec/ruby-advisory-db

4
go.mod
View File

@@ -14,7 +14,7 @@ require (
github.com/alicebob/miniredis/v2 v2.30.4
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/defsec v0.91.1
github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942
github.com/aquasecurity/go-dep-parser v0.0.0-20230825043456-df72a286b673
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
@@ -23,7 +23,7 @@ require (
github.com/aquasecurity/table v1.8.0
github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da
github.com/aquasecurity/tml v0.6.1
github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff
github.com/aquasecurity/trivy-db v0.0.0-20230828105148-2c9c4da5a321
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728
github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230828123538-ef13fef6ce5b
github.com/aws/aws-sdk-go v1.44.273

8
go.sum
View File

@@ -325,8 +325,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
github.com/aquasecurity/defsec v0.91.1 h1:dBIPm6Tva9I+ZTQv+6t9wob3ZlMSu8NFqMJr4mgJC5A=
github.com/aquasecurity/defsec v0.91.1/go.mod h1:l/srzxtuuyb6c6FlqUvMp3xw2ZbvuZ0l9972MNJM7V8=
github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942 h1:VGfeUtZyya9Vsl8enDurZ7pb/NDp2aJlL2rx2g4pR6A=
github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942/go.mod h1:0+GvQF0gL4YEAAUPpNeLeGpFDxMvvIHLMd7vk9bpwko=
github.com/aquasecurity/go-dep-parser v0.0.0-20230825043456-df72a286b673 h1:RMhUzr2ZfQ8OAO26aUkqbwfxK7d3ieFtPqUhiwTxOe0=
github.com/aquasecurity/go-dep-parser v0.0.0-20230825043456-df72a286b673/go.mod h1:0+GvQF0gL4YEAAUPpNeLeGpFDxMvvIHLMd7vk9bpwko=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
github.com/aquasecurity/go-mock-aws v0.0.0-20230328195059-5bf52338aec3 h1:Vt9y1gZS5JGY3tsL9zc++Cg4ofX51CG7PaMyC5SXWPg=
@@ -345,8 +345,8 @@ github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da h1:pj/adfN
github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da/go.mod h1:852lbQLpK2nCwlR4ZLYIccxYCfoQao6q9Nl6tjz54v8=
github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo=
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff h1:+MLnPm81Msu921N/lBrKd/NwwBrrzRoTgyMq0pIUhbs=
github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff/go.mod h1:iJSGMMclPEhkYeyiN9i+gzjV9jhEv+XfPzfVgFhfvTE=
github.com/aquasecurity/trivy-db v0.0.0-20230828105148-2c9c4da5a321 h1:oAXkM8x6jMal+6p2XB78+ntPs5LGjxZhtWHdOy4crlg=
github.com/aquasecurity/trivy-db v0.0.0-20230828105148-2c9c4da5a321/go.mod h1:WJ5Qnk5ZNGWvks07GOZe2IOsuXrPfSC5c8hYGOGfrsU=
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728 h1:0eS+V7SXHgqoT99tV1mtMW6HL4HdoB9qGLMCb1fZp8A=
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230828123538-ef13fef6ce5b h1:0COfg0HtJm6uKJn/mMBQUgrVmrIxcktlDPR35LracKI=

View File

@@ -21,44 +21,39 @@
"Type": "cocoapods",
"Packages": [
{
"ID": "AppCenter/4.2.0",
"Name": "AppCenter",
"Version": "4.2.0",
"DependsOn": [
"AppCenter/Analytics/4.2.0",
"AppCenter/Crashes/4.2.0"
"ID": "_NIODataStructures@2.41.0",
"Name": "_NIODataStructures",
"Version": "2.41.0",
"Layer": {}
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2022-3215",
"PkgID": "_NIODataStructures@2.41.0",
"PkgName": "_NIODataStructures",
"InstalledVersion": "2.41.0",
"FixedVersion": "2.29.1, 2.39.1, 2.42.0",
"Status": "fixed",
"Layer": {},
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2022-3215",
"Title": "SwiftNIO vulnerable to Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')",
"Description": "`NIOHTTP1` and projects using it for generating HTTP responses, including SwiftNIO, can be subject to a HTTP Response Injection attack...",
"Severity": "MEDIUM",
"CVSS": {
"ghsa": {
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"V3Score": 5.3
}
},
"References": [
"https://github.com/apple/swift-nio/security/advisories/GHSA-7fj7-39wj-c64f",
"https://nvd.nist.gov/vuln/detail/CVE-2022-3215",
"https://github.com/apple/swift-nio/commit/a16e2f54a25b2af217044e5168997009a505930f",
"https://github.com/advisories/GHSA-7fj7-39wj-c64f"
],
"Layer": {}
},
{
"ID": "AppCenter/Analytics/4.2.0",
"Name": "AppCenter/Analytics",
"Version": "4.2.0",
"DependsOn": [
"AppCenter/Core/4.2.0"
],
"Layer": {}
},
{
"ID": "AppCenter/Core/4.2.0",
"Name": "AppCenter/Core",
"Version": "4.2.0",
"Layer": {}
},
{
"ID": "AppCenter/Crashes/4.2.0",
"Name": "AppCenter/Crashes",
"Version": "4.2.0",
"DependsOn": [
"AppCenter/Core/4.2.0"
],
"Layer": {}
},
{
"ID": "KeychainAccess/4.2.1",
"Name": "KeychainAccess",
"Version": "4.2.1",
"Layer": {}
"PublishedDate": "2023-06-07T16:01:53Z",
"LastModifiedDate": "2023-06-19T16:45:07Z"
}
]
}

View File

@@ -0,0 +1,14 @@
- bucket: "cocoapods::GitHub Security Advisory Cocoapods"
pairs:
- bucket: _NIODataStructures
pairs:
- key: CVE-2022-3215
value:
PatchedVersions:
- "2.29.1"
- "2.39.1"
- "2.42.0"
VulnerableVersions:
- "< 2.29.1"
- ">= 2.39.0, < 2.39.1"
- ">= 2.41.0, < 2.42.0"

View File

@@ -1,12 +1,16 @@
PODS:
- AppCenter (4.2.0):
- AppCenter/Analytics (= 4.2.0)
- AppCenter/Crashes (= 4.2.0)
- AppCenter/Analytics (4.2.0):
- AppCenter/Core
- AppCenter/Core (4.2.0)
- AppCenter/Crashes (4.2.0):
- AppCenter/Core
- KeychainAccess (4.2.1)
- _NIODataStructures (2.41.0)
COCOAPODS: 1.11.2
DEPENDENCIES:
- _NIODataStructures (= 2.41.0)
SPEC REPOS:
trunk:
- _NIODataStructures
SPEC CHECKSUMS:
_NIODataStructures: 3d45d8e70a1d17a15b1dc59d102c63dbc0525ffd
PODFILE CHECKSUM: 2acff18c7f9246879b6a1a2d04e5decbc9410ef4
COCOAPODS: 1.12.1

View File

@@ -66,8 +66,10 @@ func NewDriver(libType string) (Driver, bool) {
ecosystem = vulnerability.Swift
comparer = compare.GenericComparer{}
case ftypes.Cocoapods:
log.Logger.Warn("CocoaPods is supported for SBOM, not for vulnerability scanning")
return Driver{}, false
// CocoaPods uses RubyGems version specifiers
// https://guides.cocoapods.org/making/making-a-cocoapod.html#cocoapods-versioning-specifics
ecosystem = vulnerability.Cocoapods
comparer = rubygems.Comparer{}
case ftypes.CondaPkg:
log.Logger.Warn("Conda package is supported for SBOM, not for vulnerability scanning")
return Driver{}, false

View File

@@ -181,6 +181,7 @@ var (
TypeRustBinary,
TypeConanLock,
TypeCocoaPods,
TypeSwift,
TypePubSpecLock,
TypeMixLock,
}
@@ -199,6 +200,7 @@ var (
TypeConanLock,
TypeGradleLock,
TypeCocoaPods,
TypeSwift,
TypePubSpecLock,
TypeMixLock,
}

View File

@@ -27,37 +27,37 @@ func Test_cocoaPodsLockAnalyzer_Analyze(t *testing.T) {
FilePath: "testdata/happy.lock",
Libraries: types.Packages{
{
ID: "AppCenter/4.2.0",
ID: "AppCenter@4.2.0",
Name: "AppCenter",
Version: "4.2.0",
DependsOn: []string{
"AppCenter/Analytics/4.2.0",
"AppCenter/Crashes/4.2.0",
"AppCenter/Analytics@4.2.0",
"AppCenter/Crashes@4.2.0",
},
},
{
ID: "AppCenter/Analytics/4.2.0",
ID: "AppCenter/Analytics@4.2.0",
Name: "AppCenter/Analytics",
Version: "4.2.0",
DependsOn: []string{
"AppCenter/Core/4.2.0",
"AppCenter/Core@4.2.0",
},
},
{
ID: "AppCenter/Core/4.2.0",
ID: "AppCenter/Core@4.2.0",
Name: "AppCenter/Core",
Version: "4.2.0",
},
{
ID: "AppCenter/Crashes/4.2.0",
ID: "AppCenter/Crashes@4.2.0",
Name: "AppCenter/Crashes",
Version: "4.2.0",
DependsOn: []string{
"AppCenter/Core/4.2.0",
"AppCenter/Core@4.2.0",
},
},
{
ID: "KeychainAccess/4.2.1",
ID: "KeychainAccess@4.2.1",
Name: "KeychainAccess",
Version: "4.2.1",
},

View File

@@ -57,6 +57,17 @@ func (p *PackageURL) Package() *ftypes.Package {
}
}
// CocoaPods purl has no namespace, but has subpath
// https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#cocoapods
if p.Type == packageurl.TypeCocoapods && p.Subpath != "" {
// CocoaPods uses <moduleName>/<submoduleName> format for package name
// e.g. `pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib` => `GoogleUtilities/NSData+zlib`
pkg.Name = strings.Join([]string{
p.Name,
p.Subpath,
}, "/")
}
if p.Type == packageurl.TypeRPM {
rpmVer := version.NewVersion(p.Version)
pkg.Release = rpmVer.Release()
@@ -109,6 +120,8 @@ func (p *PackageURL) PackageType() string {
case packageurl.TypeNuget:
return ftypes.NuGet
case packageurl.TypeSwift:
return ftypes.Swift
case packageurl.TypeCocoapods:
return ftypes.Cocoapods
case packageurl.TypeHex:
return ftypes.Hex
@@ -154,6 +167,7 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa
name := pkg.Name
ver := utils.FormatVersion(pkg)
namespace := ""
subpath := ""
switch ptype {
case packageurl.TypeRPM:
@@ -180,6 +194,10 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa
namespace, name = parseGolang(name)
case packageurl.TypeNPM:
namespace, name = parseNpm(name)
case packageurl.TypeSwift:
namespace, name = parseSwift(name)
case packageurl.TypeCocoapods:
name, subpath = parseCocoapods(name)
case packageurl.TypeOCI:
purl, err := parseOCI(metadata)
if err != nil {
@@ -189,7 +207,7 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa
}
return PackageURL{
PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, ""),
PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath),
FilePath: pkg.FilePath,
}, nil
}
@@ -306,6 +324,22 @@ func parseComposer(pkgName string) (string, string) {
return parsePkgName(pkgName)
}
// ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#swift
func parseSwift(pkgName string) (string, string) {
return parsePkgName(pkgName)
}
// ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#cocoapods
func parseCocoapods(pkgName string) (string, string) {
var subpath string
index := strings.Index(pkgName, "/")
if index != -1 {
subpath = pkgName[index+1:]
pkgName = pkgName[:index]
}
return pkgName, subpath
}
// ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#npm
func parseNpm(pkgName string) (string, string) {
// the name must be lowercased
@@ -330,6 +364,8 @@ func purlType(t string) string {
case ftypes.Npm, ftypes.NodePkg, ftypes.Yarn, ftypes.Pnpm:
return packageurl.TypeNPM
case ftypes.Cocoapods:
return packageurl.TypeCocoapods
case ftypes.Swift:
return packageurl.TypeSwift
case ftypes.Hex:
return packageurl.TypeHex

View File

@@ -213,6 +213,40 @@ func TestNewPackageURL(t *testing.T) {
},
},
},
{
name: "swift package",
typ: ftypes.Swift,
pkg: ftypes.Package{
ID: "github.com/apple/swift-atomics@1.1.0",
Name: "github.com/apple/swift-atomics",
Version: "1.1.0",
},
want: purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeSwift,
Namespace: "github.com/apple",
Name: "swift-atomics",
Version: "1.1.0",
},
},
},
{
name: "cocoapods package",
typ: ftypes.Cocoapods,
pkg: ftypes.Package{
ID: "GoogleUtilities/NSData+zlib@7.5.2",
Name: "GoogleUtilities/NSData+zlib",
Version: "7.5.2",
},
want: purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeCocoapods,
Name: "GoogleUtilities",
Version: "7.5.2",
Subpath: "NSData+zlib",
},
},
},
{
name: "os package",
typ: os.RedHat,
@@ -377,7 +411,6 @@ func TestNewPackageURL(t *testing.T) {
}
func TestFromString(t *testing.T) {
testCases := []struct {
name string
purl string
@@ -415,6 +448,19 @@ func TestFromString(t *testing.T) {
},
},
},
{
name: "happy path for coocapods",
purl: "pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib",
want: purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeCocoapods,
Name: "GoogleUtilities",
Version: "7.5.2",
Subpath: "NSData+zlib",
Qualifiers: packageurl.Qualifiers{},
},
},
},
{
name: "happy path for hex",
purl: "pkg:hex/plug@1.14.0",
@@ -514,3 +560,125 @@ func TestFromString(t *testing.T) {
})
}
}
func TestPackage(t *testing.T) {
tests := []struct {
name string
pkgURL *purl.PackageURL
wantPkg *ftypes.Package
}{
{
name: "rpm + Qualifiers",
pkgURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeRPM,
Namespace: "redhat",
Name: "nodejs-full-i18n",
Version: "10.21.0-3.module_el8.2.0+391+8da3adc6",
Qualifiers: packageurl.Qualifiers{
{
Key: "arch",
Value: "x86_64",
},
{
Key: "epoch",
Value: "1",
},
{
Key: "modularitylabel",
Value: "nodejs:10:8020020200707141642:6a468ee4",
},
{
Key: "distro",
Value: "redhat-8",
},
},
},
},
wantPkg: &ftypes.Package{
Name: "nodejs-full-i18n",
Version: "10.21.0",
Release: "3.module_el8.2.0+391+8da3adc6",
Arch: "x86_64",
Epoch: 1,
Modularitylabel: "nodejs:10:8020020200707141642:6a468ee4",
},
},
{
name: "composer with namespace",
pkgURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeComposer,
Namespace: "symfony",
Name: "contracts",
Version: "v1.0.2",
},
},
wantPkg: &ftypes.Package{
Name: "symfony/contracts",
Version: "v1.0.2",
},
},
{
name: "maven with namespace",
pkgURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeMaven,
Namespace: "org.springframework",
Name: "spring-core",
Version: "5.0.4.RELEASE",
Qualifiers: packageurl.Qualifiers{},
},
},
wantPkg: &ftypes.Package{
Name: "org.springframework:spring-core",
Version: "5.0.4.RELEASE",
},
},
{
name: "cocoapods with subpath",
pkgURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeCocoapods,
Version: "4.2.0",
Name: "AppCenter",
Subpath: "Analytics",
Qualifiers: packageurl.Qualifiers{},
},
},
wantPkg: &ftypes.Package{
Name: "AppCenter/Analytics",
Version: "4.2.0",
},
},
{
name: "wrong epoch",
pkgURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: packageurl.TypeRPM,
Namespace: "redhat",
Name: "acl",
Version: "2.2.53-1.el8",
Qualifiers: packageurl.Qualifiers{
{
Key: "epoch",
Value: "wrong",
},
},
},
},
wantPkg: &ftypes.Package{
Name: "acl",
Version: "2.2.53",
Release: "1.el8",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.pkgURL.Package()
assert.Equal(t, tt.wantPkg, got)
})
}
}