Skip to content

Commit

Permalink
✨ Add --local option to CLI (#1211)
Browse files Browse the repository at this point in the history
* unit tests

* remove log

* fix

* gate local access

* comment
  • Loading branch information
laurentsimon authored Nov 3, 2021
1 parent 59edb12 commit 8805ac5
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 26 deletions.
47 changes: 32 additions & 15 deletions clients/localdir/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ func isDir(p string) (bool, error) {
return fileInfo.IsDir(), nil
}

func listFiles(clientPath string, predicate func(string) (bool, error)) ([]string, error) {
func trimPrefix(pathfn, clientPath string) string {
cleanPath := path.Clean(pathfn)
prefix := fmt.Sprintf("%s%s", clientPath, string(os.PathSeparator))
return strings.TrimPrefix(cleanPath, prefix)
}

func listFiles(clientPath string) ([]string, error) {
files := []string{}
err := filepath.Walk(clientPath, func(pathfn string, info fs.FileInfo, err error) error {
if err != nil {
Expand All @@ -92,17 +98,8 @@ func listFiles(clientPath string, predicate func(string) (bool, error)) ([]strin
}

// Remove prefix of the folder.
cleanPath := path.Clean(pathfn)
prefix := fmt.Sprintf("%s%s", clientPath, string(os.PathSeparator))
p := strings.TrimPrefix(cleanPath, prefix)
matches, err := predicate(p)
if err != nil {
return err
}

if matches {
files = append(files, p)
}
p := trimPrefix(pathfn, clientPath)
files = append(files, p)

return nil
})
Expand All @@ -113,13 +110,33 @@ func listFiles(clientPath string, predicate func(string) (bool, error)) ([]strin
return files, nil
}

func applyPredicate(clientFiles []string,
errFiles error, predicate func(string) (bool, error)) ([]string, error) {
if errFiles != nil {
return nil, errFiles
}

files := []string{}
for _, pathfn := range clientFiles {
matches, err := predicate(pathfn)
if err != nil {
return nil, err
}

if matches {
files = append(files, pathfn)
}
}

return files, nil
}

// ListFiles implements RepoClient.ListFiles.
func (client *localDirClient) ListFiles(predicate func(string) (bool, error)) ([]string, error) {
client.once.Do(func() {
client.files, client.errFiles = listFiles(client.path, predicate)
client.files, client.errFiles = listFiles(client.path)
})

return client.files, client.errFiles
return applyPredicate(client.files, client.errFiles, predicate)
}

func getFileContent(clientpath, filename string) ([]byte, error) {
Expand Down
3 changes: 2 additions & 1 deletion clients/localdir/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ func TestClient_GetFileListAndContent(t *testing.T) {

// Test ListFiles API.
for _, listfiletest := range testcase.listfileTests {
matchedFiles, err := listFiles(testcase.inputFolder, listfiletest.predicate)
files, e := listFiles(testcase.inputFolder)
matchedFiles, err := applyPredicate(files, e, listfiletest.predicate)
if !errors.Is(err, listfiletest.err) {
t.Errorf("test failed: expected - %v, got - %v", listfiletest.err, err)
continue
Expand Down
40 changes: 30 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (

var (
repo string
local string
checksToRun []string
metaData []string
// This one has to use goflag instead of pflag because it's defined by zap.
Expand All @@ -64,15 +65,15 @@ const (
// These strings must be the same as the ones used in
// checks.yaml for the "repos" field.
const (
repoTypeUnknown = "unknown"
repoTypeLocal = "local"
repoTypeGitHub = "GitHub"
repoTypeLocal = "local"
repoTypeGitHub = "GitHub"
)

const (
scorecardLong = "A program that shows security scorecard for an open source software."
scorecardUse = `./scorecard --repo=<repo_url> [--checks=check1,...] [--show-details] [--policy=file]
or ./scorecard --{npm,pypi,rubgems}=<package_name> [--checks=check1,...] [--show-details] [--policy=file]`
scorecardUse = `./scorecard [--repo=<repo_url>] [--local=folder] [--checks=check1,...]
[--show-details] [--policy=file] or ./scorecard --{npm,pypi,rubgems}=<package_name>
[--checks=check1,...] [--show-details] [--policy=file]`
scorecardShort = "Security Scorecards"
)

Expand Down Expand Up @@ -110,7 +111,7 @@ func getSupportedChecks(r string, checkDocs docs.Doc) ([]string, error) {
for check := range allChecks {
c, e := checkDocs.GetCheck(check)
if e != nil {
return nil, fmt.Errorf("checkDocs.GetCheck: %w", e)
return nil, sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("checkDocs.GetCheck: %v", e))
}
types := c.GetSupportedRepoTypes()
for _, t := range types {
Expand Down Expand Up @@ -174,7 +175,6 @@ func getEnabledChecks(sp *spol.ScorecardPolicy, argsChecks []string,
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
}
}
enabledChecks = checks.AllChecks
}

// If a policy was passed as argument, ensure all checks
Expand Down Expand Up @@ -209,10 +209,21 @@ func getRepoAccessors(ctx context.Context, uri string, logger *zap.Logger) (clie
// GitHub URL.
return repo, githubrepo.CreateGithubRepoClient(ctx, logger), repoTypeGitHub, nil
}
return nil, nil, repoTypeUnknown,
return nil, nil, "",
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("unspported URI: %s: [%v, %v]", uri, errLocal, errGitHub))
}

func getURI(repo, local string) (string, error) {
if repo != "" && local != "" {
return "", sce.WithMessage(sce.ErrScorecardInternal,
"--repo and --local options cannot be used together")
}
if local != "" {
return fmt.Sprintf("file://%s", local), nil
}
return repo, nil
}

var rootCmd = &cobra.Command{
Use: scorecardUse,
Short: scorecardShort,
Expand All @@ -230,6 +241,10 @@ var rootCmd = &cobra.Command{
log.Fatal("policy not supported yet")
}

if local != "" && !v4 {
log.Fatal("--local option not supported yet")
}

// Validate format.
if !validateFormat(format) {
log.Fatalf("unsupported format '%s'", format)
Expand All @@ -240,6 +255,11 @@ var rootCmd = &cobra.Command{
log.Fatalf("readPolicy: %v", err)
}

// Get the URI.
uri, err := getURI(repo, local)
if err != nil {
log.Fatal(err)
}
if npm != "" {
if git, err := fetchGitRepositoryFromNPM(npm); err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -278,7 +298,7 @@ var rootCmd = &cobra.Command{
// nolint
defer logger.Sync() // Flushes buffer, if any.

repoURI, repoClient, repoType, err := getRepoAccessors(ctx, repo, logger)
repoURI, repoClient, repoType, err := getRepoAccessors(ctx, uri, logger)
if err != nil {
log.Fatal(err)
}
Expand All @@ -305,7 +325,6 @@ var rootCmd = &cobra.Command{
fmt.Fprintf(os.Stderr, "Starting [%s]\n", checkName)
}
}

repoResult, err := pkg.RunScorecards(ctx, repoURI, enabledChecks, repoClient)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -469,6 +488,7 @@ func init() {
// Add the zap flag manually
rootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
rootCmd.Flags().StringVar(&repo, "repo", "", "repository to check")
rootCmd.Flags().StringVar(&local, "local", "", "local folder to check")
rootCmd.Flags().StringVar(
&npm, "npm", "",
"npm package to check, given that the npm package has a GitHub repository")
Expand Down
1 change: 1 addition & 0 deletions pkg/scorecard.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func runEnabledChecks(ctx context.Context,

func getRepoCommitHash(r clients.RepoClient) (string, error) {
commits, err := r.ListCommits()

if err != nil && !errors.Is(err, clients.ErrUnsupportedFeature) {
return "", sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("ListCommits:%v", err.Error()))
}
Expand Down

0 comments on commit 8805ac5

Please sign in to comment.