Skip to content

Commit 36e5ed8

Browse files
authored
Fix PURLToPackage function and move it (#439)
Turns out our `PURLToPackage` function was returning incorrect results for ecosystems that contain a namespace like golang, the returned result was simply missing the full namespace (github.com/author/...). When adding the namespace, there's also some exceptions with some ecosystems (e.g. Maven uses `:`, debian and alpine repeats their name in their namespace, etc). This also moves the `PURLToPackage` to the `models` package instead of `osvscanner`, deprecating the existing one in `osvscanner` because: - Makes more sense, it actually has nothing to do with the scanner itself, but is converting between PURLs and a structure under `model`. - Prevents cyclic imports when used elsewhere (in the offline scanning PR, and in the upcoming PURL parsing PR that I'm currently working on) Also added additional tests to clarify behavior and prevent regressions in the future.
1 parent feb7abb commit 36e5ed8

6 files changed

Lines changed: 162 additions & 47 deletions

File tree

cmd/osv-scanner/main_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,22 +144,22 @@ func TestRun(t *testing.T) {
144144
wantStdout: `
145145
Scanning dir ./fixtures/sbom-insecure/postgres-stretch.cdx.xml
146146
Scanned %%/fixtures/sbom-insecure/postgres-stretch.cdx.xml as CycloneDX SBOM and found 136 packages
147-
+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
148-
| OSV URL | CVSS | ECOSYSTEM | PACKAGE | VERSION | SOURCE |
149-
+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
150-
| https://osv.dev/DLA-3022-1 | | Debian | dpkg | 1.18.25 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
151-
| https://osv.dev/GHSA-v95c-p5hm-xq8f | 6 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
152-
| https://osv.dev/GO-2022-0274 | | | | | |
153-
| https://osv.dev/GHSA-f3fp-gc8g-vw66 | 5.9 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
154-
| https://osv.dev/GHSA-g2j6-57v7-gm8c | 6.1 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
155-
| https://osv.dev/GHSA-m8cg-xc2p-r3fc | 2.5 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
156-
| https://osv.dev/GHSA-vpvm-3wq2-2wvm | 7 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
157-
| https://osv.dev/GHSA-p782-xgp4-8hr8 | 5.3 | Go | sys | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
158-
| https://osv.dev/GO-2022-0493 | | | | | |
159-
| https://osv.dev/DLA-3012-1 | | Debian | libxml2 | 2.9.4+dfsg1-2.2+deb9u6 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
160-
| https://osv.dev/DLA-3008-1 | | Debian | openssl | 1.1.0l-1~deb9u5 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
161-
| https://osv.dev/DLA-3051-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
162-
+-------------------------------------+------+-----------+---------+------------------------------------+-------------------------------------------------+
147+
+-------------------------------------+------+-----------+--------------------------------+------------------------------------+-------------------------------------------------+
148+
| OSV URL | CVSS | ECOSYSTEM | PACKAGE | VERSION | SOURCE |
149+
+-------------------------------------+------+-----------+--------------------------------+------------------------------------+-------------------------------------------------+
150+
| https://osv.dev/DLA-3022-1 | | Debian | dpkg | 1.18.25 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
151+
| https://osv.dev/GHSA-v95c-p5hm-xq8f | 6 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
152+
| https://osv.dev/GO-2022-0274 | | | | | |
153+
| https://osv.dev/GHSA-f3fp-gc8g-vw66 | 5.9 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
154+
| https://osv.dev/GHSA-g2j6-57v7-gm8c | 6.1 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
155+
| https://osv.dev/GHSA-m8cg-xc2p-r3fc | 2.5 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
156+
| https://osv.dev/GHSA-vpvm-3wq2-2wvm | 7 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
157+
| https://osv.dev/GHSA-p782-xgp4-8hr8 | 5.3 | Go | golang.org/x/sys | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
158+
| https://osv.dev/GO-2022-0493 | | | | | |
159+
| https://osv.dev/DLA-3012-1 | | Debian | libxml2 | 2.9.4+dfsg1-2.2+deb9u6 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
160+
| https://osv.dev/DLA-3008-1 | | Debian | openssl | 1.1.0l-1~deb9u5 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
161+
| https://osv.dev/DLA-3051-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
162+
+-------------------------------------+------+-----------+--------------------------------+------------------------------------+-------------------------------------------------+
163163
`,
164164
wantStderr: "",
165165
},

pkg/models/purl_to_package.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package models
2+
3+
import (
4+
"github.com/package-url/packageurl-go"
5+
)
6+
7+
var purlEcosystems = map[string]Ecosystem{
8+
"cargo": EcosystemCratesIO,
9+
"deb": EcosystemDebian,
10+
"hex": EcosystemHex,
11+
"golang": EcosystemGo,
12+
"maven": EcosystemMaven,
13+
"nuget": EcosystemNuGet,
14+
"npm": EcosystemNPM,
15+
"composer": EcosystemPackagist,
16+
"generic": EcosystemOSSFuzz,
17+
"pypi": EcosystemPyPI,
18+
"gem": EcosystemRubyGems,
19+
}
20+
21+
// PURLToPackage converts a Package URL string to models.PackageInfo
22+
func PURLToPackage(purl string) (PackageInfo, error) {
23+
parsedPURL, err := packageurl.FromString(purl)
24+
if err != nil {
25+
return PackageInfo{}, err
26+
}
27+
ecosystem, ok := purlEcosystems[parsedPURL.Type]
28+
if !ok {
29+
ecosystem = Ecosystem(parsedPURL.Type)
30+
}
31+
32+
// PackageInfo expects the full namespace in the name for ecosystems that specify it.
33+
name := parsedPURL.Name
34+
if parsedPURL.Namespace != "" {
35+
switch ecosystem { //nolint:exhaustive
36+
case EcosystemMaven:
37+
// Maven uses : to separate namespace and package
38+
name = parsedPURL.Namespace + ":" + parsedPURL.Name
39+
case EcosystemDebian, EcosystemAlpine:
40+
// Debian and Alpine repeats their namespace in PURL, so don't add it to the name
41+
name = parsedPURL.Name
42+
default:
43+
name = parsedPURL.Namespace + "/" + parsedPURL.Name
44+
}
45+
}
46+
47+
return PackageInfo{
48+
Name: name,
49+
Ecosystem: string(ecosystem),
50+
Version: parsedPURL.Version,
51+
}, nil
52+
}

pkg/models/purl_to_package_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package models_test
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/google/osv-scanner/pkg/models"
8+
)
9+
10+
func TestPURLToPackage(t *testing.T) {
11+
t.Parallel()
12+
type args struct {
13+
purl string
14+
}
15+
tests := []struct {
16+
name string
17+
args args
18+
want models.PackageInfo
19+
wantErr bool
20+
}{
21+
{
22+
name: "valid PURL",
23+
args: args{
24+
purl: "pkg:cargo/[email protected]",
25+
},
26+
want: models.PackageInfo{
27+
Name: "memoffset",
28+
Version: "0.6.1",
29+
Ecosystem: string(models.EcosystemCratesIO),
30+
},
31+
},
32+
{
33+
name: "valid PURL golang",
34+
args: args{
35+
purl: "pkg:golang/github.com/gogo/[email protected]",
36+
},
37+
want: models.PackageInfo{
38+
Name: "github.com/gogo/protobuf",
39+
Version: "5.6.0",
40+
Ecosystem: string(models.EcosystemGo),
41+
},
42+
},
43+
{
44+
name: "valid PURL maven",
45+
args: args{
46+
purl: "pkg:maven/org.hdrhistogram/[email protected]",
47+
},
48+
want: models.PackageInfo{
49+
Name: "org.hdrhistogram:HdrHistogram",
50+
Version: "2.1.12",
51+
Ecosystem: string(models.EcosystemMaven),
52+
},
53+
},
54+
{
55+
name: "valid Debian maven",
56+
args: args{
57+
purl: "pkg:deb/debian/[email protected]+deb11u1",
58+
},
59+
want: models.PackageInfo{
60+
Name: "nginx",
61+
Version: "2.36.1-8+deb11u1",
62+
Ecosystem: string(models.EcosystemDebian),
63+
},
64+
},
65+
{
66+
name: "invalid PURL",
67+
args: args{
68+
purl: "pkg-golang/github.com/gogo/protobuf.0",
69+
},
70+
want: models.PackageInfo{},
71+
wantErr: true,
72+
},
73+
}
74+
for _, tt := range tests {
75+
tt := tt
76+
t.Run(tt.name, func(t *testing.T) {
77+
t.Parallel()
78+
got, err := models.PURLToPackage(tt.args.purl)
79+
if (err != nil) != tt.wantErr {
80+
t.Errorf("PURLToPackage() error = %v, wantErr %v", err, tt.wantErr)
81+
return
82+
}
83+
if !reflect.DeepEqual(got, tt.want) {
84+
t.Errorf("PURLToPackage() = %v, want %v", got, tt.want)
85+
}
86+
})
87+
}
88+
}

pkg/osvscanner/osvscanner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func scanSBOMFile(r reporter.Reporter, query *osv.BatchedQuery, path string, fro
244244
count := 0
245245
ignoredCount := 0
246246
err = provider.GetPackages(file, func(id sbom.Identifier) error {
247-
_, err := PURLToPackage(id.PURL)
247+
_, err := models.PURLToPackage(id.PURL)
248248
if err != nil {
249249
ignoredCount++
250250
//nolint:nilerr

pkg/osvscanner/purl_to_package.go

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,11 @@ package osvscanner
22

33
import (
44
"github.com/google/osv-scanner/pkg/models"
5-
"github.com/package-url/packageurl-go"
65
)
76

8-
var purlEcosystems = map[string]string{
9-
"cargo": "crates.io",
10-
"deb": "Debian",
11-
"hex": "Hex",
12-
"golang": "Go",
13-
"maven": "Maven",
14-
"nuget": "NuGet",
15-
"npm": "npm",
16-
"composer": "Packagist",
17-
"generic": "OSS-Fuzz",
18-
"pypi": "PyPI",
19-
"gem": "RubyGems",
20-
}
21-
7+
// PURLToPackage converts a Package URL string to models.PackageInfo
8+
//
9+
// Deprecated: Use the PURLToPackage in the models package instead.
2210
func PURLToPackage(purl string) (models.PackageInfo, error) {
23-
parsedPURL, err := packageurl.FromString(purl)
24-
if err != nil {
25-
return models.PackageInfo{}, err
26-
}
27-
ecosystem := purlEcosystems[parsedPURL.Type]
28-
if ecosystem == "" {
29-
ecosystem = parsedPURL.Type
30-
}
31-
32-
return models.PackageInfo{
33-
Name: parsedPURL.Name,
34-
Ecosystem: ecosystem,
35-
Version: parsedPURL.Version,
36-
}, nil
11+
return models.PURLToPackage(purl)
3712
}

pkg/osvscanner/vulnerability_result.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func groupResponseBySource(r reporter.Reporter, query osv.BatchedQuery, resp *os
3030
pkg.Package.Ecosystem = "GIT"
3131
} else if query.Package.PURL != "" {
3232
var err error
33-
pkg.Package, err = PURLToPackage(query.Package.PURL)
33+
pkg.Package, err = models.PURLToPackage(query.Package.PURL)
3434
if err != nil {
3535
r.PrintError(fmt.Sprintf("Failed to parse purl: %s, with error: %s",
3636
query.Package.PURL, err))

0 commit comments

Comments
 (0)