diff --git a/README.md b/README.md
index caf23e1d9641..64fc69f0307c 100644
--- a/README.md
+++ b/README.md
@@ -406,7 +406,7 @@ RESULTS
##### Using a Package manager
-For projects in the `--npm`, `--pypi`, or `--rubygems` ecosystems, you have the
+For projects in the `--npm`, `--pypi`, `--rubygems`, or `--nuget` ecosystems, you have the
option to run Scorecard using a package manager. Provide the package name to
run the checks on the corresponding GitHub source code.
diff --git a/cmd/package_managers.go b/cmd/package_managers.go
index a18ecbd25a8b..b267c3dc7432 100644
--- a/cmd/package_managers.go
+++ b/cmd/package_managers.go
@@ -17,8 +17,11 @@ package cmd
import (
"encoding/json"
+ "encoding/xml"
"fmt"
+ "golang.org/x/exp/slices"
+
sce "github.com/ossf/scorecard/v4/errors"
)
@@ -27,7 +30,7 @@ type packageMangerResponse struct {
exists bool
}
-func fetchGitRepositoryFromPackageManagers(npm, pypi, rubygems string,
+func fetchGitRepositoryFromPackageManagers(npm, pypi, rubygems, nuget string,
manager packageManagerClient,
) (packageMangerResponse, error) {
if npm != "" {
@@ -51,6 +54,13 @@ func fetchGitRepositoryFromPackageManagers(npm, pypi, rubygems string,
associatedRepo: gitRepo,
}, err
}
+ if nuget != "" {
+ gitRepo, err := fetchGitRepositoryFromNuget(nuget, manager)
+ return packageMangerResponse{
+ exists: true,
+ associatedRepo: gitRepo,
+ }, err
+ }
return packageMangerResponse{}, nil
}
@@ -77,6 +87,34 @@ type rubyGemsSearchResults struct {
SourceCodeURI string `json:"source_code_uri"`
}
+type nugetIndexResult struct {
+ ID string `json:"@id"`
+ Type string `json:"@type"`
+}
+
+type nugetIndexResults struct {
+ Resources []nugetIndexResult `json:"resources"`
+}
+
+type nugetpackageIndexResults struct {
+ Versions []string `json:"versions"`
+}
+
+type nugetNuspec struct {
+ XMLName xml.Name `xml:"package"`
+ Metadata nuspecMetadata `xml:"metadata"`
+}
+
+type nuspecMetadata struct {
+ XMLName xml.Name `xml:"metadata"`
+ Repository nuspecRepository `xml:"repository"`
+}
+
+type nuspecRepository struct {
+ XMLName xml.Name `xml:"repository"`
+ URL string `xml:"url,attr"`
+}
+
// Gets the GitHub repository URL for the npm package.
func fetchGitRepositoryFromNPM(packageName string, packageManager packageManagerClient) (string, error) {
npmSearchURL := "https://registry.npmjs.org/-/v1/search?text=%s&size=1"
@@ -138,3 +176,60 @@ func fetchGitRepositoryFromRubyGems(packageName string, manager packageManagerCl
}
return v.SourceCodeURI, nil
}
+
+// Gets the GitHub repository URL for the nuget package.
+func fetchGitRepositoryFromNuget(packageName string, manager packageManagerClient) (string, error) {
+ nugetIndexURL := "https://api.nuget.org/v3/index.json"
+ respIndex, err := manager.GetURI(nugetIndexURL)
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to get nuget index json: %v", err))
+ }
+ defer respIndex.Body.Close()
+ nugetIndexResults := &nugetIndexResults{}
+ err = json.NewDecoder(respIndex.Body).Decode(nugetIndexResults)
+
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse nuget index json: %v", err))
+ }
+ packageBaseAddressIndex := slices.IndexFunc(nugetIndexResults.Resources,
+ func(n nugetIndexResult) bool { return n.Type == "PackageBaseAddress/3.0.0" })
+ if packageBaseAddressIndex == -1 {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, "failed to find package base URI at nuget index json")
+ }
+
+ nugetPackageBaseURL := nugetIndexResults.Resources[packageBaseAddressIndex].ID
+
+ respPackageIndex, err := manager.Get(nugetPackageBaseURL+"%s/index.json", packageName)
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to get nuget package index json: %v", err))
+ }
+ defer respPackageIndex.Body.Close()
+ packageIndexResults := &nugetpackageIndexResults{}
+ err = json.NewDecoder(respPackageIndex.Body).Decode(packageIndexResults)
+
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse nuget package index json: %v", err))
+ }
+
+ lastVersion := packageIndexResults.Versions[len(packageIndexResults.Versions)-1]
+
+ respPackageSpec, err := manager.Get(nugetPackageBaseURL+"%[1]v/"+lastVersion+"/%[1]v.nuspec", packageName)
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to get nuget package spec json: %v", err))
+ }
+ defer respPackageSpec.Body.Close()
+
+ packageSpecResults := &nugetNuspec{}
+ err = xml.NewDecoder(respPackageSpec.Body).Decode(packageSpecResults)
+
+ if err != nil {
+ return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse nuget nuspec xml: %v", err))
+ }
+ if packageSpecResults.Metadata == (nuspecMetadata{}) ||
+ packageSpecResults.Metadata.Repository == (nuspecRepository{}) ||
+ packageSpecResults.Metadata.Repository.URL == "" {
+ return "", sce.WithMessage(sce.ErrScorecardInternal,
+ fmt.Sprintf("source repo is not defined for nuget package %v", packageName))
+ }
+ return packageSpecResults.Metadata.Repository.URL, nil
+}
diff --git a/cmd/package_managers_test.go b/cmd/package_managers_test.go
index b4e398109f4b..6a07810f565d 100644
--- a/cmd/package_managers_test.go
+++ b/cmd/package_managers_test.go
@@ -20,6 +20,7 @@ import (
"errors"
"io"
"net/http"
+ "strings"
"testing"
"github.com/golang/mock/gomock"
@@ -706,3 +707,541 @@ func Test_fetchGitRepositoryFromRubyGems(t *testing.T) {
})
}
}
+
+func Test_fetchGitRepositoryFromNuget(t *testing.T) {
+ t.Parallel()
+ type args struct {
+ packageName string
+ resultIndex string
+ resultPackageIndex string
+ resultPackageSpec string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {
+ name: "fetchGitRepositoryFromNuget",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: `
+ {
+ "versions": [
+ "13.0.2",
+ "13.0.3-beta1",
+ "13.0.3"
+ ]
+ }
+ `,
+ resultPackageSpec: `
+
+
+ Foo
+ 13.0.3
+ Foo.NET
+ Foo Foo
+ Foo Foo
+ false
+ MIT
+ https://licenses.nuget.org/MIT
+ https://www.newtonsoft.com/json
+ https://www.foo.com/content/images/nugeticon.png
+ Foo.NET is a popular foo framework for .NET
+ Copyright ©Foo Foo 2008
+ foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ },
+ want: "foo",
+ wantErr: false,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_error_index",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: "",
+ resultPackageIndex: "",
+ resultPackageSpec: "",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_bad_index",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: "foo",
+ resultPackageIndex: "",
+ resultPackageSpec: "",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_error_package_index",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: "",
+ resultPackageSpec: "",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_bad_package_index",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: "foo",
+ resultPackageSpec: "",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_error_package_spec",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: `
+ {
+ "versions": [
+ "13.0.2",
+ "13.0.3-beta1",
+ "13.0.3"
+ ]
+ }
+ `,
+ resultPackageSpec: "",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_bad_package_spec",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: `
+ {
+ "versions": [
+ "13.0.2",
+ "13.0.3-beta1",
+ "13.0.3"
+ ]
+ }
+ `,
+ resultPackageSpec: "foo",
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "fetchGitRepositoryFromNuget_missing_repo",
+
+ args: args{
+ packageName: "nuget-package",
+ resultIndex: `
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://azuresearch-ussc.nuget.org/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (secondary)"
+ },
+ {
+ "@id": "https://azuresearch-usnc.nuget.org/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "https://api.nuget.org/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "https://api.nuget.org/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format ..."
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "https://www.nuget.org/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ }
+ ]
+ }
+ `,
+ resultPackageIndex: `
+ {
+ "versions": [
+ "13.0.2",
+ "13.0.3-beta1",
+ "13.0.3"
+ ]
+ }
+ `,
+ resultPackageSpec: `
+
+
+ Foo
+ 13.0.3
+ Foo.NET
+ Foo Foo
+ Foo Foo
+ false
+ MIT
+ https://licenses.nuget.org/MIT
+ https://www.newtonsoft.com/json
+ https://www.foo.com/content/images/nugeticon.png
+ Foo.NET is a popular foo framework for .NET
+ Copyright ©Foo Foo 2008
+ foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ },
+ want: "",
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ ctrl := gomock.NewController(t)
+ p := NewMockpackageManagerClient(ctrl)
+ p.EXPECT().GetURI(gomock.Any()).
+ DoAndReturn(func(url string) (*http.Response, error) {
+ if tt.wantErr && tt.args.resultIndex == "" {
+ //nolint
+ return nil, errors.New("error")
+ }
+
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewBufferString(tt.args.resultIndex)),
+ }, nil
+ }).AnyTimes()
+ p.EXPECT().Get(gomock.Any(), tt.args.packageName).
+ DoAndReturn(func(url, packageName string) (*http.Response, error) {
+ if tt.wantErr && tt.args.resultPackageIndex == "" {
+ //nolint
+ return nil, errors.New("error")
+ }
+
+ if tt.wantErr && tt.args.resultPackageSpec == "" {
+ //nolint
+ return nil, errors.New("error")
+ }
+ if strings.HasSuffix(url, "index.json") {
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewBufferString(tt.args.resultPackageIndex)),
+ }, nil
+ } else if strings.HasSuffix(url, ".nuspec") {
+ return &http.Response{
+ StatusCode: 200,
+ Body: io.NopCloser(bytes.NewBufferString(tt.args.resultPackageSpec)),
+ }, nil
+ }
+ return nil, nil
+ }).AnyTimes()
+ got, err := fetchGitRepositoryFromNuget(tt.args.packageName, p)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fetchGitRepositoryFromNuget() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("fetchGitRepositoryFromNuget() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/cmd/packagemanager_client.go b/cmd/packagemanager_client.go
index f453d22cfd02..d98c15506a6e 100644
--- a/cmd/packagemanager_client.go
+++ b/cmd/packagemanager_client.go
@@ -22,16 +22,28 @@ import (
type packageManagerClient interface {
Get(URI string, packagename string) (*http.Response, error)
+
+ GetURI(URI string) (*http.Response, error)
}
type packageManager struct{}
// nolint: noctx
func (c *packageManager) Get(url, packageName string) (*http.Response, error) {
+ return c.getRemoteURL(fmt.Sprintf(url, packageName))
+}
+
+// nolint: noctx
+func (c *packageManager) GetURI(url string) (*http.Response, error) {
+ return c.getRemoteURL(url)
+}
+
+// nolint: noctx
+func (c *packageManager) getRemoteURL(url string) (*http.Response, error) {
const timeout = 10
client := &http.Client{
Timeout: timeout * time.Second,
}
//nolint
- return client.Get(fmt.Sprintf(url, packageName))
+ return client.Get(url)
}
diff --git a/cmd/packagemanager_mockclient.go b/cmd/packagemanager_mockclient.go
index 40cc49d369fc..daecee250cd8 100644
--- a/cmd/packagemanager_mockclient.go
+++ b/cmd/packagemanager_mockclient.go
@@ -58,8 +58,23 @@ func (m *MockpackageManagerClient) Get(URI, packagename string) (*http.Response,
return ret0, ret1
}
+// GetURI mocks base method.
+func (m *MockpackageManagerClient) GetURI(URI string) (*http.Response, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetURI", URI)
+ ret0, _ := ret[0].(*http.Response)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
// Get indicates an expected call of Get.
func (mr *MockpackageManagerClientMockRecorder) Get(URI, packagename interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockpackageManagerClient)(nil).Get), URI, packagename)
}
+
+// GetURI indicates an expected call of GetURI.
+func (mr *MockpackageManagerClientMockRecorder) GetURI(URI interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetURI", reflect.TypeOf((*MockpackageManagerClient)(nil).GetURI), URI)
+}
diff --git a/cmd/root.go b/cmd/root.go
index da4cd0610d01..608b4935d7be 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -37,7 +37,7 @@ import (
const (
scorecardLong = "A program that shows the OpenSSF scorecard for an open source software."
- scorecardUse = `./scorecard (--repo= | --local= | --{npm,pypi,rubygems}=)
+ scorecardUse = `./scorecard (--repo= | --local= | --{npm,pypi,rubygems,nuget}=)
[--checks=check1,...] [--show-details]`
scorecardShort = "OpenSSF Scorecard"
)
@@ -74,7 +74,7 @@ func New(o *options.Options) *cobra.Command {
func rootCmd(o *options.Options) error {
p := &packageManager{}
// Set `repo` from package managers.
- pkgResp, err := fetchGitRepositoryFromPackageManagers(o.NPM, o.PyPI, o.RubyGems, p)
+ pkgResp, err := fetchGitRepositoryFromPackageManagers(o.NPM, o.PyPI, o.RubyGems, o.Nuget, p)
if err != nil {
return fmt.Errorf("fetchGitRepositoryFromPackageManagers: %w", err)
}
diff --git a/options/flags.go b/options/flags.go
index 66a48c2b85af..1652c9fd18ae 100644
--- a/options/flags.go
+++ b/options/flags.go
@@ -45,6 +45,9 @@ const (
// FlagRubyGems is the flag name for specifying a RubyGems repository.
FlagRubyGems = "rubygems"
+ // FlagNuget is the flag name for specifying a Nuget repository.
+ FlagNuget = "nuget"
+
// FlagMetadata is the flag name for specifying metadata for the project.
FlagMetadata = "metadata"
@@ -120,6 +123,13 @@ func (o *Options) AddFlags(cmd *cobra.Command) {
"rubygems package to check, given that the rubygems package has a GitHub repository",
)
+ cmd.Flags().StringVar(
+ &o.Nuget,
+ FlagNuget,
+ o.Nuget,
+ "nuget package to check, given that the nuget package has a GitHub repository",
+ )
+
cmd.Flags().StringSliceVar(
&o.Metadata,
FlagMetadata,
diff --git a/options/options.go b/options/options.go
index 164c356b8218..5be1fda1febe 100644
--- a/options/options.go
+++ b/options/options.go
@@ -37,6 +37,7 @@ type Options struct {
NPM string
PyPI string
RubyGems string
+ Nuget string
PolicyFile string
// TODO(action): Add logic for writing results to file
ResultsFile string
@@ -113,7 +114,7 @@ var (
errPolicyFileNotSupported = errors.New("policy file is not supported yet")
errRawOptionNotSupported = errors.New("raw option is not supported yet")
errRepoOptionMustBeSet = errors.New(
- "exactly one of `repo`, `npm`, `pypi`, `rubygems` or `local` must be set",
+ "exactly one of `repo`, `npm`, `pypi`, `rubygems`, `nuget` or `local` must be set",
)
errSARIFNotSupported = errors.New("SARIF format is not supported yet")
errValidate = errors.New("some options could not be validated")
@@ -124,11 +125,12 @@ var (
func (o *Options) Validate() error {
var errs []error
- // Validate exactly one of `--repo`, `--npm`, `--pypi`, `--rubygems`, `--local` is enabled.
+ // Validate exactly one of `--repo`, `--npm`, `--pypi`, `--rubygems`, `--nuget`, `--local` is enabled.
if boolSum(o.Repo != "",
o.NPM != "",
o.PyPI != "",
o.RubyGems != "",
+ o.Nuget != "",
o.Local != "") != 1 {
errs = append(
errs,
diff --git a/options/options_test.go b/options/options_test.go
index b69d5c35e07a..8098e8ebc907 100644
--- a/options/options_test.go
+++ b/options/options_test.go
@@ -21,7 +21,7 @@ import (
)
// Cannot run parallel tests because of the ENV variables.
-//nolint
+// nolint
func TestOptions_Validate(t *testing.T) {
type fields struct {
Repo string
@@ -32,6 +32,7 @@ func TestOptions_Validate(t *testing.T) {
NPM string
PyPI string
RubyGems string
+ Nuget string
PolicyFile string
ResultsFile string
ChecksToRun []string
@@ -99,6 +100,7 @@ func TestOptions_Validate(t *testing.T) {
NPM: tt.fields.NPM,
PyPI: tt.fields.PyPI,
RubyGems: tt.fields.RubyGems,
+ Nuget: tt.fields.Nuget,
PolicyFile: tt.fields.PolicyFile,
ResultsFile: tt.fields.ResultsFile,
ChecksToRun: tt.fields.ChecksToRun,