Skip to content

Commit 7993641

Browse files
authored
Merge pull request #749 from syf2211/fix/730-vsx-cross-ecosystem-pollution
fix(scanner): scope OpenVSX extension OSV lookups to marketplace ecosystem
2 parents 23b07a0 + 966a815 commit 7993641

4 files changed

Lines changed: 126 additions & 16 deletions

File tree

pkg/models/manifest_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@ import (
66
"github.com/stretchr/testify/assert"
77
)
88

9+
func TestGetOsvEcosystem(t *testing.T) {
10+
cases := []struct {
11+
name string
12+
input string
13+
expected string
14+
}{
15+
{name: "vscode marketplace", input: EcosystemVSCodeExtensions, expected: "vscode"},
16+
{name: "openvsx marketplace", input: EcosystemOpenVSXExtensions, expected: "vscode:open-vsx.org"},
17+
{name: "npm unchanged", input: EcosystemNpm, expected: ""},
18+
{name: "distro unchanged", input: "Alpine:v3.23", expected: ""},
19+
}
20+
21+
for _, tc := range cases {
22+
t.Run(tc.name, func(t *testing.T) {
23+
assert.Equal(t, tc.expected, GetOsvEcosystem(tc.input))
24+
})
25+
}
26+
}
27+
928
func TestNewPackageManifestFromContainerImage(t *testing.T) {
1029
cases := []struct {
1130
name string

pkg/models/models.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,20 @@ func (pm *PackageManifest) GetSpecEcosystem() modelspec.Ecosystem {
325325
}
326326
}
327327

328+
// GetOsvEcosystem returns the OSV ecosystem identifier used for vulnerability
329+
// lookups. Extension marketplaces use OSV-specific names that differ from
330+
// internal model ecosystem strings.
331+
func GetOsvEcosystem(modelEcosystem string) string {
332+
switch modelEcosystem {
333+
case EcosystemVSCodeExtensions:
334+
return "vscode"
335+
case EcosystemOpenVSXExtensions:
336+
return "vscode:open-vsx.org"
337+
default:
338+
return ""
339+
}
340+
}
341+
328342
// Map the control tower spec ecosystem to model ecosystem
329343
func GetModelEcosystem(ecosystem packagev1.Ecosystem) string {
330344
switch ecosystem {

pkg/scanner/enrich_insightsv2.go

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,7 @@ func (e *insightsBasedPackageEnricherV2) Name() string {
3737
return "Insights API v2"
3838
}
3939

40-
// Enrich will enrich the package using Insights V2 API. However, most of the
41-
// analysers and reporters in vet are coupled with Insights V1 data model. Till
42-
// we are able to drive a major refactor to decouple them, we need to convert V2
43-
// data model to V1 data model while preserving the V2 data. This will ensure
44-
//
45-
// - Existing analysers and reporters continue to work without any changes.
46-
// - We can start using V2 data model in new analysers and reporters.
47-
func (e *insightsBasedPackageEnricherV2) Enrich(pkg *models.Package,
48-
cb PackageDependencyCallbackFn,
49-
) error {
40+
func buildInsightsV2Request(pkg *models.Package) *insightsv2.GetPackageVersionInsightRequest {
5041
req := &insightsv2.GetPackageVersionInsightRequest{
5142
PackageVersion: &packagev1.PackageVersion{
5243
Package: &packagev1.Package{
@@ -57,13 +48,15 @@ func (e *insightsBasedPackageEnricherV2) Enrich(pkg *models.Package,
5748
},
5849
}
5950

60-
// For distro ecosystems (e.g. Alpine:v3.23, Ubuntu:22.04) that don't map to
61-
// a known protobuf enum, pass the raw ecosystem string so control-tower can
62-
// scope the OSV query to the correct distro advisory database.
63-
//
64-
// + Check if the ecosystem is unspecified, to prevent from overlapping with supported ecosystem.
65-
if osvRawEcosystem := pkg.Manifest.Ecosystem; osvRawEcosystem != "" &&
51+
// Extension marketplaces need explicit OSV ecosystem scoping so npm advisories
52+
// with colliding package names are not applied to VS Code / OpenVSX extensions.
53+
if osvEcosystem := models.GetOsvEcosystem(pkg.Manifest.Ecosystem); osvEcosystem != "" {
54+
req.OsvEcosystem = &osvEcosystem
55+
} else if osvRawEcosystem := pkg.Manifest.Ecosystem; osvRawEcosystem != "" &&
6656
pkg.GetControlTowerSpecEcosystem() == packagev1.Ecosystem_ECOSYSTEM_UNSPECIFIED {
57+
// For distro ecosystems (e.g. Alpine:v3.23, Ubuntu:22.04) that don't map to
58+
// a known protobuf enum, pass the raw ecosystem string so control-tower can
59+
// scope the OSV query to the correct distro advisory database.
6760
req.OsvEcosystem = &osvRawEcosystem
6861
}
6962

@@ -74,6 +67,21 @@ func (e *insightsBasedPackageEnricherV2) Enrich(pkg *models.Package,
7467
req.OsvSourceName = &osvSourceName
7568
}
7669

70+
return req
71+
}
72+
73+
// Enrich will enrich the package using Insights V2 API. However, most of the
74+
// analysers and reporters in vet are coupled with Insights V1 data model. Till
75+
// we are able to drive a major refactor to decouple them, we need to convert V2
76+
// data model to V1 data model while preserving the V2 data. This will ensure
77+
//
78+
// - Existing analysers and reporters continue to work without any changes.
79+
// - We can start using V2 data model in new analysers and reporters.
80+
func (e *insightsBasedPackageEnricherV2) Enrich(pkg *models.Package,
81+
cb PackageDependencyCallbackFn,
82+
) error {
83+
req := buildInsightsV2Request(pkg)
84+
7785
res, err := e.client.GetPackageVersionInsight(context.Background(), req)
7886
if err != nil {
7987
logger.Debugf("Failed to enrich package: %s/%s: %v",

pkg/scanner/enrich_insightsv2_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,77 @@ import (
55

66
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
77
"github.com/stretchr/testify/assert"
8+
9+
"github.com/safedep/vet/pkg/models"
810
)
911

12+
func TestBuildInsightsV2Request(t *testing.T) {
13+
t.Run("scopes openvsx extensions to vscode:open-vsx.org OSV ecosystem", func(t *testing.T) {
14+
pkg := &models.Package{
15+
Manifest: models.NewPackageManifestFromLocal(
16+
"/home/user/.cursor/extensions/extensions.json",
17+
models.EcosystemOpenVSXExtensions,
18+
),
19+
PackageDetails: models.NewPackageDetail(
20+
models.EcosystemOpenVSXExtensions,
21+
"llvm-vs-code-extensions.vscode-clangd",
22+
"0.4.0",
23+
),
24+
}
25+
26+
req := buildInsightsV2Request(pkg)
27+
28+
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_OPENVSX, req.GetPackageVersion().GetPackage().GetEcosystem())
29+
assert.Equal(t, "llvm-vs-code-extensions.vscode-clangd", req.GetPackageVersion().GetPackage().GetName())
30+
assert.Equal(t, "0.4.0", req.GetPackageVersion().GetVersion())
31+
assert.Equal(t, "vscode:open-vsx.org", req.GetOsvEcosystem())
32+
})
33+
34+
t.Run("scopes vscode extensions to vscode OSV ecosystem", func(t *testing.T) {
35+
pkg := &models.Package{
36+
Manifest: models.NewPackageManifestFromLocal(
37+
"/home/user/.vscode/extensions/extensions.json",
38+
models.EcosystemVSCodeExtensions,
39+
),
40+
PackageDetails: models.NewPackageDetail(
41+
models.EcosystemVSCodeExtensions,
42+
"publisher.extension",
43+
"1.0.0",
44+
),
45+
}
46+
47+
req := buildInsightsV2Request(pkg)
48+
49+
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_VSCODE, req.GetPackageVersion().GetPackage().GetEcosystem())
50+
assert.Equal(t, "vscode", req.GetOsvEcosystem())
51+
})
52+
53+
t.Run("passes raw distro ecosystem when protobuf enum is unspecified", func(t *testing.T) {
54+
ecosystem := "Alpine:v3.23"
55+
pkg := &models.Package{
56+
Manifest: models.NewPackageManifestFromLocal("lib/apk/db/installed", ecosystem),
57+
PackageDetails: models.NewPackageDetail(ecosystem, "busybox", "1.36.1"),
58+
}
59+
60+
req := buildInsightsV2Request(pkg)
61+
62+
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_UNSPECIFIED, req.GetPackageVersion().GetPackage().GetEcosystem())
63+
assert.Equal(t, ecosystem, req.GetOsvEcosystem())
64+
})
65+
66+
t.Run("does not set OSV ecosystem for npm packages", func(t *testing.T) {
67+
pkg := &models.Package{
68+
Manifest: models.NewPackageManifestFromLocal("package-lock.json", models.EcosystemNpm),
69+
PackageDetails: models.NewPackageDetail(models.EcosystemNpm, "lodash", "4.17.21"),
70+
}
71+
72+
req := buildInsightsV2Request(pkg)
73+
74+
assert.Equal(t, packagev1.Ecosystem_ECOSYSTEM_NPM, req.GetPackageVersion().GetPackage().GetEcosystem())
75+
assert.Empty(t, req.GetOsvEcosystem())
76+
})
77+
}
78+
1079
func TestCurrentVersionFromAvailableVersions(t *testing.T) {
1180
t.Run("prefers registry default version", func(t *testing.T) {
1281
versions := []*packagev1.PackageAvailableVersion{

0 commit comments

Comments
 (0)