Skip to content

Commit 66708ba

Browse files
✨ Feature: Dependency-diff ecosystem naming convention mapping (GitHub -> OSV) (#2088)
* save * save * save * save * save * save
1 parent 8f96d6b commit 66708ba

File tree

6 files changed

+189
-5
lines changed

6 files changed

+189
-5
lines changed

dependencydiff/dependencydiff.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,34 @@ func GetDependencyDiffResults(
7979
if err != nil {
8080
return nil, fmt.Errorf("error in fetchRawDependencyDiffData: %w", err)
8181
}
82+
// Map the ecosystem naming convention from GitHub to OSV.
83+
err = mapDependencyEcosystemNaming(dCtx.dependencydiffs)
84+
if err != nil {
85+
return nil, fmt.Errorf("error in mapDependencyEcosystemNaming: %w", err)
86+
}
8287
err = getScorecardCheckResults(&dCtx)
8388
if err != nil {
8489
return nil, fmt.Errorf("error getting scorecard check results: %w", err)
8590
}
8691
return dCtx.results, nil
8792
}
8893

94+
func mapDependencyEcosystemNaming(deps []dependency) error {
95+
for i := range deps {
96+
if deps[i].Ecosystem == nil {
97+
continue
98+
}
99+
mappedEcosys, err := toEcosystem(*deps[i].Ecosystem)
100+
if err != nil {
101+
wrappedErr := fmt.Errorf("error mapping dependency ecosystem: %w", err)
102+
return wrappedErr
103+
}
104+
deps[i].Ecosystem = asPointer(string(mappedEcosys))
105+
106+
}
107+
return nil
108+
}
109+
89110
func initRepoAndClientByChecks(dCtx *dependencydiffContext, dSrcRepo string) error {
90111
repo, repoClient, ossFuzzClient, ciiClient, vulnsClient, err := checker.GetClients(
91112
dCtx.ctx, dSrcRepo, "", dCtx.logger,
@@ -171,3 +192,7 @@ func getScorecardCheckResults(dCtx *dependencydiffContext) error {
171192
}
172193
return nil
173194
}
195+
196+
func asPointer(s string) *string {
197+
return &s
198+
}

dependencydiff/dependencydiff_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package dependencydiff
1616

1717
import (
1818
"context"
19+
"errors"
1920
"path"
2021
"testing"
2122

@@ -158,3 +159,66 @@ func Test_getScorecardCheckResults(t *testing.T) {
158159
})
159160
}
160161
}
162+
163+
func Test_mapDependencyEcosystemNaming(t *testing.T) {
164+
t.Parallel()
165+
//nolint
166+
tests := []struct {
167+
name string
168+
deps []dependency
169+
errWanted error
170+
}{
171+
{
172+
name: "error invalid github ecosystem",
173+
deps: []dependency{
174+
{
175+
Name: "dependency_1",
176+
Ecosystem: asPointer("not_supported"),
177+
},
178+
{
179+
Name: "dependency_2",
180+
Ecosystem: asPointer("gomod"),
181+
},
182+
},
183+
errWanted: errInvalid,
184+
},
185+
{
186+
name: "error cannot find mapping",
187+
deps: []dependency{
188+
{
189+
Name: "dependency_3",
190+
Ecosystem: asPointer("actions"),
191+
},
192+
},
193+
errWanted: errInvalid,
194+
},
195+
{
196+
name: "correct mapping",
197+
deps: []dependency{
198+
{
199+
Name: "dependency_4",
200+
Ecosystem: asPointer("gomod"),
201+
},
202+
{
203+
Name: "dependency_5",
204+
Ecosystem: asPointer("pip"),
205+
},
206+
{
207+
Name: "dependency_6",
208+
Ecosystem: asPointer("cargo"),
209+
},
210+
},
211+
},
212+
}
213+
for _, tt := range tests {
214+
tt := tt
215+
t.Run(tt.name, func(t *testing.T) {
216+
t.Parallel()
217+
err := mapDependencyEcosystemNaming(tt.deps)
218+
if tt.errWanted != nil && errors.Is(tt.errWanted, err) {
219+
t.Errorf("not a wanted error, want:%v, got:%v", tt.errWanted, err)
220+
return
221+
}
222+
})
223+
}
224+
}

dependencydiff/errors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ import "errors"
1818

1919
// static Errors for mapping
2020
var (
21-
errInvalid = errors.New("invalid")
21+
errMappingNotFound = errors.New("ecosystem mapping not found")
22+
errInvalid = errors.New("invalid")
2223
)

dependencydiff/mapping.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2022 Security Scorecard Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dependencydiff
16+
17+
import (
18+
"fmt"
19+
)
20+
21+
// Ecosystem is a package ecosystem supported by OSV, GitHub, etc.
22+
type ecosystem string
23+
24+
// OSV ecosystem naming data source: https://ossf.github.io/osv-schema/#affectedpackage-field
25+
// nolint
26+
const (
27+
// The Go ecosystem.
28+
ecosystemGo ecosystem = "Go"
29+
30+
// The NPM ecosystem.
31+
ecosystemNpm ecosystem = "npm"
32+
33+
// The Android ecosystem
34+
ecosystemAndroid ecosystem = "Android" // nolint:unused
35+
36+
// The crates.io ecosystem for RUST.
37+
ecosystemCrates ecosystem = "crates.io"
38+
39+
// For reports from the OSS-Fuzz project that have no more appropriate ecosystem.
40+
ecosystemOssFuzz ecosystem = "OSS-Fuzz" // nolint:unused
41+
42+
// The Python PyPI ecosystem. PyPI is the main package source of pip.
43+
ecosystemPyPI ecosystem = "PyPI"
44+
45+
// The RubyGems ecosystem.
46+
ecosystemRubyGems ecosystem = "RubyGems"
47+
48+
// The PHP package manager ecosystem. Packagist is the main Composer repository.
49+
ecosystemPackagist ecosystem = "Packagist"
50+
51+
// The Maven Java package ecosystem.
52+
ecosystemMaven ecosystem = "Maven"
53+
54+
// The NuGet package ecosystem.
55+
ecosystemNuGet ecosystem = "Nuget"
56+
57+
// The Linux kernel.
58+
ecosystemLinux ecosystem = "Linux" // nolint:unused
59+
60+
// The Debian package ecosystem.
61+
ecosystemDebian ecosystem = "Debian" // nolint:unused
62+
63+
// Hex is the package manager of Erlang.
64+
// TODO: GitHub doesn't support hex as the ecosystem for Erlang yet. Add this to the map in the future.
65+
ecosystemHex ecosystem = "Hex" // nolint:unused
66+
)
67+
68+
var (
69+
//gitHubToOSV defines the ecosystem naming mapping relationship between GitHub and others.
70+
gitHubToOSV = map[string]ecosystem{
71+
// GitHub ecosystem naming data source: https://docs.github.com/en/code-security/supply-chain-security/
72+
// understanding-your-software-supply-chain/about-the-dependency-graph#supported-package-ecosystems
73+
"gomod": ecosystemGo, /* go.mod and go.sum */
74+
"cargo": ecosystemCrates,
75+
"pip": ecosystemPyPI, /* pip and poetry */
76+
"npm": ecosystemNpm, /* npm and yarn */
77+
"maven": ecosystemMaven,
78+
"composer": ecosystemPackagist,
79+
"rubygems": ecosystemRubyGems,
80+
"nuget": ecosystemNuGet,
81+
}
82+
)
83+
84+
func toEcosystem(e string) (ecosystem, error) {
85+
if ecosystemOSV, found := gitHubToOSV[e]; found {
86+
return ecosystemOSV, nil
87+
}
88+
return "", fmt.Errorf("%w for github entry %s", errMappingNotFound, e)
89+
}

dependencydiff/raw_dependencies.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,10 @@ func fetchRawDependencyDiffData(dCtx *dependencydiffContext) error {
6868
if err != nil {
6969
return fmt.Errorf("error parsing the dependency-diff reponse: %w", err)
7070
}
71+
for _, d := range dCtx.dependencydiffs {
72+
if !d.ChangeType.IsValid() {
73+
return fmt.Errorf("%w: change type", errInvalid)
74+
}
75+
}
7176
return nil
7277
}

pkg/dependencydiff_result.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ const (
3535
)
3636

3737
// IsValid determines if a ChangeType is valid.
38-
func (ct *ChangeType) IsValid() bool {
39-
switch *ct {
38+
func (ct ChangeType) IsValid() bool {
39+
switch ct {
4040
case Added, Updated, Removed:
4141
return true
4242
default:
@@ -45,7 +45,7 @@ func (ct *ChangeType) IsValid() bool {
4545
}
4646

4747
// ScorecardResultWithError is used for the dependency-diff module to record the scorecard result
48-
// and a potential error field if the Scorecard run fails.
48+
// and a error field to record potential errors when the Scorecard run fails.
4949
type ScorecardResultWithError struct {
5050
// ScorecardResult is the scorecard result for the dependency repo.
5151
ScorecardResult *ScorecardResult
@@ -74,7 +74,7 @@ type DependencyCheckResult struct {
7474
// Version is the package version of the dependency.
7575
Version *string
7676

77-
// ScorecardResultWithError is the scorecard checking results of the dependency.
77+
// ScorecardResultWithError is the scorecard checking result of the dependency.
7878
ScorecardResultWithError ScorecardResultWithError
7979

8080
// Name is the name of the dependency.

0 commit comments

Comments
 (0)