From f385b0d9df540655fad3d80edcc1ebf36dba5dad Mon Sep 17 00:00:00 2001 From: naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:29:31 -0500 Subject: [PATCH] Feature - run scans from npm pacakge name Implemented scans from npm package name. --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 19f08ef7..d22f1273 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,59 @@ Signed-Releases: Fail 10 Signed-Tags: Fail 10 ``` +### Package manager support + +scorecard has an option to provide `--npm` package name and it would fetch the corresponding GitHub code. + +``` +./scorecard --npm=angular +Starting [Active] +Starting [Branch-Protection] +Starting [CI-Tests] +Starting [CII-Best-Practices] +Starting [Code-Review] +Starting [Contributors] +Starting [Frozen-Deps] +Starting [Fuzzing] +Starting [Packaging] +Starting [Pull-Requests] +Starting [SAST] +Starting [Security-Policy] +Starting [Signed-Releases] +Starting [Signed-Tags] +Finished [Signed-Releases] +Finished [Fuzzing] +Finished [CII-Best-Practices] +Finished [Security-Policy] +Finished [CI-Tests] +Finished [Packaging] +Finished [SAST] +Finished [Code-Review] +Finished [Branch-Protection] +Finished [Frozen-Deps] +Finished [Signed-Tags] +Finished [Active] +Finished [Pull-Requests] +Finished [Contributors] + +RESULTS +------- +Active: Fail 10 +Branch-Protection: Fail 0 +CI-Tests: Pass 10 +CII-Best-Practices: Fail 10 +Code-Review: Pass 10 +Contributors: Pass 10 +Frozen-Deps: Fail 0 +Fuzzing: Fail 10 +Packaging: Fail 0 +Pull-Requests: Fail 9 +SAST: Fail 10 +Security-Policy: Pass 10 +Signed-Releases: Fail 0 +Signed-Tags: Fail 10 +``` + ### Authentication Before running Scorecard, you need to diff --git a/cmd/root.go b/cmd/root.go index f464999b..5c3db9a9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "os" "sort" "strconv" @@ -41,6 +42,7 @@ var ( // This one has to use goflag instead of pflag because it's defined by zap logLevel = zap.LevelFlag("verbosity", zap.InfoLevel, "override the default log level") format string + npm string showDetails bool ) @@ -51,7 +53,7 @@ const ( ) var rootCmd = &cobra.Command{ - Use: "./scorecard --repo= [--checks=check1,...] [--show-details]", + Use: "./scorecard --repo= [--checks=check1,...] [--show-details] or ./scorecard --npm= [--checks=check1,...] [--show-details]", Short: "Security Scorecards", Long: "A program that shows security scorecard for an open source software.", Run: func(cmd *cobra.Command, args []string) { @@ -74,6 +76,20 @@ var rootCmd = &cobra.Command{ log.Fatalf("invalid format flag %s. allowed values are: [default, csv, json]", format) } + if len(npm) != 0 { + if git, err := fetchGitRepoistoryFromNPM(npm); err != nil { + log.Fatal(err) + } else { + if err := cmd.Flags().Set("repo", git); err != nil { + log.Fatal(err) + } + } + } else { + if err := cmd.MarkFlagRequired("repo"); err != nil { + log.Fatal(err) + } + } + enabledChecks := []checker.NamedCheck{} if len(checksToRun) != 0 { checkNames := map[string]struct{}{} @@ -117,6 +133,16 @@ type checkResult struct { Details []string } +type npmSearchResults struct { + Objects []struct { + Package struct { + Links struct { + Repository string `json:"repository"` + } `json:"links"` + } `json:"package"` + } `json:"objects"` +} + type record struct { Repo string Date string @@ -194,14 +220,35 @@ func displayResult(result bool) string { } } +// Gets the GitHub repository URL for the npm package +func fetchGitRepoistoryFromNPM(packageName string) (string, error) { + npmsearchURL := "https://registry.npmjs.org/-/v1/search?text=%s&size=1" + client := &http.Client{ + Timeout: 10 * time.Second, + } + resp, err := client.Get(fmt.Sprintf(npmsearchURL, packageName)) + + if err != nil { + return "", err + } + + defer resp.Body.Close() + v := &npmSearchResults{} + err = json.NewDecoder(resp.Body).Decode(v) + if err != nil { + return "", err + } + if len(v.Objects) == 0 { + return "", fmt.Errorf("could not find search results for npm package %s", packageName) + } + return v.Objects[0].Package.Links.Repository, nil +} func init() { // Add the zap flag manually rootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine) rootCmd.Flags().Var(&repo, "repo", "repository to check") + rootCmd.Flags().StringVar(&npm, "npm", "", "npm package to check. If the npm package has a GitHub repository") rootCmd.Flags().StringVar(&format, "format", formatDefault, "output format. allowed values are [default, csv, json]") - if err := rootCmd.MarkFlagRequired("repo"); err != nil { - log.Panic(err) - } rootCmd.Flags().BoolVar(&showDetails, "show-details", false, "show extra details about each check") checkNames := []string{}