فهرست منبع

fixing all golint and adding some more tests

zach rice 6 سال پیش
والد
کامیت
e446ba0738

+ 2 - 2
Makefile

@@ -11,8 +11,8 @@ test-cover:
 
 test:
 	go get golang.org/x/lint/golint
-	go fmt
-	golint
+	go fmt ./...
+	golint ./...
 	go test ./... --race $(PKG) -v
 
 test-integration:

+ 3 - 3
audit/audit.go

@@ -7,6 +7,7 @@ import (
 	"path"
 )
 
+// Run accepts a manager and begins an audit based on the options/configs set in the manager.
 func Run(m *manager.Manager) error {
 	if m.Opts.OwnerPath != "" {
 		files, err := ioutil.ReadDir(m.Opts.OwnerPath)
@@ -17,7 +18,7 @@ func Run(m *manager.Manager) error {
 			if !f.IsDir() {
 				continue
 			}
-			m.Opts.RepoPath = fmt.Sprintf("%s/%s",m.Opts.OwnerPath, f.Name())
+			m.Opts.RepoPath = fmt.Sprintf("%s/%s", m.Opts.OwnerPath, f.Name())
 			if err := runHelper(NewRepo(m)); err != nil {
 				// TODO or send to errchan?
 				return err
@@ -46,10 +47,9 @@ func runHelper(r *Repo) error {
 			return nil
 		}
 	} else {
-		if err := r.Clone(); err != nil {
+		if err := r.Clone(nil); err != nil {
 			return err
 		}
 	}
 	return r.Audit()
 }
-

+ 16 - 13
audit/audit_test.go

@@ -31,7 +31,6 @@ func TestAudit(t *testing.T) {
 			opts: options.Options{
 				RepoPath: "../test_data/test_repos/test_repo_1",
 				Report:   "../test_data/test_local_repo_one_aws_leak.json.got",
-
 			},
 			wantPath: "../test_data/test_local_repo_one_aws_leak.json",
 		},
@@ -123,8 +122,7 @@ func TestAudit(t *testing.T) {
 			description: "test owner path",
 			opts: options.Options{
 				OwnerPath: "../test_data/test_repos/",
-				Report:   "../test_data/test_local_owner_aws_leak.json.got",
-
+				Report:    "../test_data/test_local_owner_aws_leak.json.got",
 			},
 			wantPath: "../test_data/test_local_owner_aws_leak.json",
 		},
@@ -137,13 +135,21 @@ func TestAudit(t *testing.T) {
 			},
 			wantPath: "../test_data/test_entropy.json",
 		},
+		{
+			description: "test entropy and regex",
+			opts: options.Options{
+				RepoPath: "../test_data/test_repos/test_repo_1",
+				Report:   "../test_data/test_regex_entropy.json.got",
+				Config:   "../test_data/test_configs/regex_entropy.toml",
+			},
+			wantPath: "../test_data/test_regex_entropy.json",
+		},
 		{
 			description: "test local repo four entropy alternative config",
 			opts: options.Options{
-				RepoPath: "../test_data/test_repos/test_repo_4",
-				Report:   "../test_data/test_local_repo_four_alt_config_entropy.json.got",
+				RepoPath:   "../test_data/test_repos/test_repo_4",
+				Report:     "../test_data/test_local_repo_four_alt_config_entropy.json.got",
 				RepoConfig: true,
-
 			},
 			wantPath: "../test_data/test_local_repo_four_alt_config_entropy.json.got",
 		},
@@ -277,7 +283,6 @@ func TestAuditUncommited(t *testing.T) {
 			}
 		}
 	}
-
 }
 
 func fileCheck(wantPath, gotPath string) error {
@@ -294,11 +299,10 @@ func fileCheck(wantPath, gotPath string) error {
 	if strings.Trim(string(want), "\n") != strings.Trim(string(got), "\n") {
 		dmp := diffmatchpatch.New()
 		diffs := dmp.DiffMain(string(want), string(got), false)
-		return fmt.Errorf("does not equal: %s\n", dmp.DiffPrettyText(diffs))
-	} else {
-		if err := os.Remove(gotPath); err != nil {
-			return err
-		}
+		return fmt.Errorf("does not equal: %s", dmp.DiffPrettyText(diffs))
+	}
+	if err := os.Remove(gotPath); err != nil {
+		return err
 	}
 	return nil
 }
@@ -320,4 +324,3 @@ func moveDotGit(from, to string) error {
 	}
 	return nil
 }
-

+ 6 - 12
audit/repo.go

@@ -27,13 +27,12 @@ import (
 type Repo struct {
 	*git.Repository
 
-	// AlternativeConfig is used when the --repo-config option is set.
+	// config is used when the --repo-config option is set.
 	// This allows users to load up configs specific to their repos.
 	// Imagine the scenario where you are doing an audit of a large organization
 	// and you want certain repos to look for specific rules. If those specific repos
 	// have a gitleaks.toml or .gitleaks.toml config then those configs will be used specifically
 	// for those repo audits.
-	AlternativeConfig config.Config
 	config config.Config
 
 	Name    string
@@ -44,21 +43,18 @@ type Repo struct {
 func NewRepo(m *manager.Manager) *Repo {
 	return &Repo{
 		Manager: m,
-		config: m.Config,
+		config:  m.Config,
 	}
 }
 
 // Clone will clone a repo and return a Repo struct which contains a go-git repo. The clone method
 // is determined by the clone options set in Manager.metadata.cloneOptions
-func (repo *Repo) Clone(cloneOptions ...*git.CloneOptions) error {
+func (repo *Repo) Clone(cloneOption *git.CloneOptions) error {
 	var (
-		repository  *git.Repository
-		err         error
-		cloneOption *git.CloneOptions
+		repository *git.Repository
+		err        error
 	)
-	if len(cloneOptions) != 0 {
-		cloneOption = cloneOptions[0]
-	} else {
+	if cloneOption == nil {
 		cloneOption = repo.Manager.CloneOptions
 	}
 
@@ -228,7 +224,6 @@ func (repo *Repo) Audit() error {
 			return nil
 		}
 
-		// TODO check whitelist Commit
 		if isCommitWhiteListed(c.Hash.String(), repo.config.Whitelist.Commits) {
 			return nil
 		}
@@ -304,4 +299,3 @@ func (repo *Repo) loadRepoConfig() (config.Config, error) {
 	_, err = toml.DecodeReader(f, &tomlLoader)
 	return tomlLoader.Parse()
 }
-

+ 36 - 2
audit/util.go

@@ -99,6 +99,16 @@ func trippedEntropy(line string, rule config.Rule) bool {
 	return false
 }
 
+func ruleContainRegex(rule config.Rule) bool {
+	if rule.Regex == nil {
+		return false
+	}
+	if rule.Regex.String() == "" {
+		return false
+	}
+	return true
+}
+
 // InspectString accepts a string, commit object, repo, and filename. This function iterates over
 // all the rules set by the gitleaks config. If the rule contains entropy checks then entropy will be checked first.
 // Next, if the rule contains a regular expression then that will be checked.
@@ -106,11 +116,11 @@ func InspectString(content string, c *object.Commit, repo *Repo, filename string
 	for _, rule := range repo.config.Rules {
 		// check entropy
 		if len(rule.Entropy) != 0 {
-			// TODO
 			// an optimization would be to switch the regex from FindAllIndex to FindString
 			// since we are iterating on the lines if entropy rules exist...
 			for _, line := range strings.Split(content, "\n") {
-				if trippedEntropy(line, rule) {
+				entropyTripped := trippedEntropy(line, rule)
+				if entropyTripped && !ruleContainRegex(rule) {
 					_line := line
 					if len(_line) > maxLineLen {
 						_line = line[0 : maxLineLen-1]
@@ -128,8 +138,32 @@ func InspectString(content string, c *object.Commit, repo *Repo, filename string
 						Tags:     strings.Join(rule.Tags, ", "),
 						File:     filename,
 					})
+				} else if entropyTripped {
+					// entropy has been tripped which means if there is a regex specified in the same
+					// rule, we need to inspect the line for a regex match. In otherwords, the current rule has
+					// both entropy and regex set which work in combination. This helps narrow down false positives
+					// on searches for generic passwords in code.
+					match := rule.Regex.FindString(line)
+					if match != "" {
+						// both the regex and entropy in this rule have been tripped which means this line
+						// contains a leak
+						repo.Manager.SendLeaks(manager.Leak{
+							Line:     line,
+							Offender: match,
+							Commit:   c.Hash.String(),
+							Message:  c.Message,
+							Repo:     repo.Name,
+							Rule:     rule.Description,
+							Author:   c.Author.Name,
+							Email:    c.Author.Email,
+							Date:     c.Author.When,
+							Tags:     strings.Join(rule.Tags, ", "),
+							File:     filename,
+						})
+					}
 				}
 			}
+			return
 		}
 		if rule.Regex.String() == "" {
 			continue

+ 1 - 4
config/config.go

@@ -9,7 +9,7 @@ import (
 	"strings"
 )
 
-// WhiteList is struct containing items that if encountered will whitelist
+// Whitelist is struct containing items that if encountered will whitelist
 // a commit/line of code that would be considered a leak.
 type Whitelist struct {
 	Description string
@@ -121,9 +121,6 @@ func (tomlLoader TomlLoader) Parse() (Config, error) {
 			if err != nil {
 				return cfg, fmt.Errorf("problem loading config: %v", err)
 			}
-			if err != nil {
-				return cfg, fmt.Errorf("problem loading config: %v", err)
-			}
 			whitelists = append(whitelists, Whitelist{
 				Description: wl.Description,
 				File:        fileRe,

+ 1 - 1
config/config_test.go

@@ -18,7 +18,7 @@ func TestParse(t *testing.T) {
 	}{
 		{
 			description: "default config",
-			opts: options.Options{},
+			opts:        options.Options{},
 		},
 		{
 			description: "test successful load",

+ 3 - 1
config/default.go

@@ -1,5 +1,8 @@
 package config
 
+// DefaultConfig is the default gitleaks configuration. If --config={path-to-config} is set than the config located
+// at {path-to-config} will be used. Alternatively, if --repo-config is set then gitleaks will attempt to
+// use the config set in a gitleaks.toml or .gitleaks.toml file in the repo that is run with --repo-config set.
 const DefaultConfig = `
 title = "gitleaks config"
 
@@ -133,4 +136,3 @@ title = "gitleaks config"
 	description = "image whitelists"
 	file = '''(.*?)(jpg|gif|doc|pdf|bin)$'''
 `
-

+ 0 - 1
go.sum

@@ -70,7 +70,6 @@ github.com/xanzy/go-gitlab v0.21.0 h1:Ru55sR4TBoDNsAKwCOpzeaGtbiWj7xTksVmzBJbLu6
 github.com/xanzy/go-gitlab v0.21.0/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og=
 github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
 github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
-github.com/zricethezav/gitleaks-ng v0.0.1 h1:4WgulZbJ5QanI6f5kbhVZjuMhUeyEE+R8y3YvHuf9rA=
 golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=

+ 23 - 18
hosts/github.go

@@ -2,7 +2,6 @@ package hosts
 
 import (
 	"context"
-	"fmt"
 	"github.com/google/go-github/github"
 	log "github.com/sirupsen/logrus"
 	"github.com/zricethezav/gitleaks/audit"
@@ -17,24 +16,15 @@ import (
 	"sync"
 )
 
-type GithubError struct {
-	Err    string
-	Repo   string
-	Commit string
-}
-
-func (githubError *GithubError) Error() string {
-	return fmt.Sprintf("repo: %s, err: %s",
-		githubError.Repo, githubError.Err)
-}
-
+// Github wraps a github client and manager. This struct implements what the Host interface defines.
 type Github struct {
 	client  *github.Client
-	errChan chan GithubError
 	manager manager.Manager
 	wg      sync.WaitGroup
 }
 
+// NewGithubClient accepts a manager struct and returns a Github host pointer which will be used to
+// perform a github audit on an organization, user, or PR.
 func NewGithubClient(m manager.Manager) *Github {
 	ctx := context.Background()
 	token := oauth2.StaticTokenSource(
@@ -44,7 +34,6 @@ func NewGithubClient(m manager.Manager) *Github {
 	return &Github{
 		manager: m,
 		client:  github.NewClient(oauth2.NewClient(ctx, token)),
-		errChan: make(chan GithubError),
 	}
 }
 
@@ -53,6 +42,7 @@ func (g *Github) Audit() {
 	ctx := context.Background()
 	listOptions := github.ListOptions{
 		PerPage: 100,
+		Page:    1,
 	}
 
 	var githubRepos []*github.Repository
@@ -70,12 +60,18 @@ func (g *Github) Audit() {
 			_githubRepos, resp, err = g.client.Repositories.ListByOrg(ctx, g.manager.Opts.Organization,
 				&github.RepositoryListByOrgOptions{ListOptions: listOptions})
 		}
-
 		githubRepos = append(githubRepos, _githubRepos...)
 
 		if resp == nil {
 			break
 		}
+
+		if resp.LastPage != 0 {
+			log.Infof("gathering github repos... progress: page %d of %d", listOptions.Page, resp.LastPage)
+		} else {
+			log.Infof("gathering github repos... progress: page %d of %d", listOptions.Page, listOptions.Page)
+		}
+
 		listOptions.Page = resp.NextPage
 		if err != nil || listOptions.Page == 0 {
 			break
@@ -87,11 +83,20 @@ func (g *Github) Audit() {
 		err := r.Clone(&git.CloneOptions{
 			URL: *repo.CloneURL,
 		})
-		r.Name = *repo.Name
 		if err != nil {
-			log.Warn(err)
+			log.Warn("unable to clone via https and access token, attempting with ssh now")
+			auth, err := options.SSHAuth(g.manager.Opts)
+			if err != nil {
+				log.Warnf("unable to get ssh auth, skipping clone and audit for repo %s: %+v\n", *repo.CloneURL, err)
+			}
+			err = r.Clone(&git.CloneOptions{
+				URL:  *repo.SSHURL,
+				Auth: auth,
+			})
+			if err != nil {
+				log.Warnf("err cloning %s, skipping clone and audit: %+v\n", *repo.SSHURL, err)
+			}
 		}
-
 		if err = r.Audit(); err != nil {
 			log.Warn(err)
 		}

+ 5 - 15
hosts/gitlab.go

@@ -2,7 +2,6 @@ package hosts
 
 import (
 	"context"
-	"fmt"
 	log "github.com/sirupsen/logrus"
 	"github.com/xanzy/go-gitlab"
 	"github.com/zricethezav/gitleaks/audit"
@@ -11,31 +10,21 @@ import (
 	"sync"
 )
 
-type GitlabError struct {
-	Err    string
-	Repo   string
-	Commit string
-}
-
-func (gitlabError *GitlabError) Error() string {
-	return fmt.Sprintf("repo: %s, err: %s",
-		gitlabError.Repo, gitlabError.Err)
-}
-
+// Gitlab wraps a gitlab client and manager. This struct implements what the Host interface defines.
 type Gitlab struct {
 	client  *gitlab.Client
-	errChan chan GitlabError
 	manager manager.Manager
 	ctx     context.Context
 	wg      sync.WaitGroup
 }
 
+// NewGitlabClient accepts a manager struct and returns a Gitlab host pointer which will be used to
+// perform a gitlab audit on an group or user.
 func NewGitlabClient(m manager.Manager) *Gitlab {
 	return &Gitlab{
 		manager: m,
 		ctx:     context.Background(),
 		client:  gitlab.NewClient(nil, options.GetAccessToken(m.Opts)),
-		errChan: make(chan GitlabError),
 	}
 }
 
@@ -87,6 +76,7 @@ func (g *Gitlab) Audit() {
 		cloneOpts := g.manager.CloneOptions
 		cloneOpts.URL = p.HTTPURLToRepo
 		err := r.Clone(cloneOpts)
+		// TODO handle clone retry with ssh like github host
 		r.Name = p.Name
 
 		if err = r.Audit(); err != nil {
@@ -95,7 +85,7 @@ func (g *Gitlab) Audit() {
 	}
 }
 
-// Audit(MR)PR TODO
+// AuditPR TODO not implemented
 func (g *Gitlab) AuditPR() {
 	log.Error("AuditPR is not implemented in Gitlab host yet...")
 }

+ 3 - 0
hosts/host.go

@@ -10,11 +10,14 @@ const (
 	_gitlab
 )
 
+// Host is an interface used for defining external git hosting providers like github and gitlab.
+// TODO add bitbucket
 type Host interface {
 	Audit()
 	AuditPR()
 }
 
+// Run kicks off a host audit. This function accepts a manager and determines what host it should audit
 func Run(m *manager.Manager) error {
 	var host Host
 	switch getHost(m.Opts.Host) {

+ 5 - 5
hosts/hosts_test.go

@@ -31,15 +31,15 @@ func TestGithub(t *testing.T) {
 	}{
 		{
 			opts: options.Options{
-				Host: "github",
-				User: "gitleakstest",
+				Host:        "github",
+				User:        "gitleakstest",
 				AccessToken: os.Getenv("GITHUB_TOKEN"),
 			},
 			desiredLeaks: 2,
 		},
 		{
 			opts: options.Options{
-				Host: "github",
+				Host:        "github",
 				PullRequest: "https://github.com/gitleakstest/gronit/pull/1",
 				AccessToken: os.Getenv("GITHUB_TOKEN"),
 			},
@@ -85,8 +85,8 @@ func TestGitlab(t *testing.T) {
 	}{
 		{
 			opts: options.Options{
-				Host: "gitlab",
-				User: "gitleakstest",
+				Host:        "gitlab",
+				User:        "gitleakstest",
 				AccessToken: os.Getenv("GITLAB_TOKEN"),
 			},
 			desiredLeaks: 2,

+ 14 - 0
manager/manager.go

@@ -34,6 +34,8 @@ type Manager struct {
 	metadata Metadata
 }
 
+// Leak is a struct that contains information about some line of code that contains
+// sensitive information as determined by the rules set in a gitleaks config
 type Leak struct {
 	Line     string    `json:"line"`
 	Offender string    `json:"offender"`
@@ -49,14 +51,24 @@ type Leak struct {
 	Severity string    `json:"severity"`
 }
 
+// AuditTime is a type used to determine total audit time
 type AuditTime int64
+
+// PatchTime is a type used to determine total patch time during an audit
 type PatchTime int64
+
+// CloneTime is a type used to determine total clone time
 type CloneTime int64
+
+// RegexTime is a type used to determine the time each rules' regex takes. This is especially useful
+// if you notice that gitleaks is taking a long time. You can use --debug to see the output of the regexTime
+// so you can determine which regex is not performing well.
 type RegexTime struct {
 	Time  int64
 	Regex string
 }
 
+// Metadata is a struct used to communicate metadata about an audit like timings and total commit counts.
 type Metadata struct {
 	mux  sync.Mutex
 	data map[string]interface{}
@@ -110,6 +122,7 @@ func (manager *Manager) receiveLeaks() {
 	}
 }
 
+// GetMetadata returns the metadata. TODO this may not need to be private
 func (manager *Manager) GetMetadata() Metadata {
 	return manager.metadata
 }
@@ -132,6 +145,7 @@ func (manager *Manager) receiveMetadata() {
 	}
 }
 
+// IncrementCommits increments total commits during an audit by i.
 func (manager *Manager) IncrementCommits(i int) {
 	manager.metadata.mux.Lock()
 	manager.metadata.Commits += i

+ 6 - 7
manager/manager_test.go

@@ -6,7 +6,6 @@ import (
 	"testing"
 )
 
-
 // TODO
 // add more substantial tests... but since literally every pkg uses manager
 // these tests are kind of redundant
@@ -73,17 +72,17 @@ func TestSendReceiveMeta(t *testing.T) {
 			})
 		}
 		md := m.GetMetadata()
-		if md.cloneTime != test.cloneTime * int64(test.iterations) {
+		if md.cloneTime != test.cloneTime*int64(test.iterations) {
 			t.Errorf("clone time mismatch, got %d, wanted %d",
-				md.cloneTime, test.cloneTime * int64(test.iterations))
+				md.cloneTime, test.cloneTime*int64(test.iterations))
 		}
-		if md.AuditTime != test.auditTime * int64(test.iterations) {
+		if md.AuditTime != test.auditTime*int64(test.iterations) {
 			t.Errorf("audit time mismatch, got %d, wanted %d",
-				md.AuditTime, test.auditTime * int64(test.iterations))
+				md.AuditTime, test.auditTime*int64(test.iterations))
 		}
-		if md.patchTime != test.patchTime * int64(test.iterations) {
+		if md.patchTime != test.patchTime*int64(test.iterations) {
 			t.Errorf("clone time mismatch, got %d, wanted %d",
-				md.patchTime, test.patchTime * int64(test.iterations))
+				md.patchTime, test.patchTime*int64(test.iterations))
 		}
 	}
 }

+ 10 - 6
options/options.go

@@ -14,12 +14,16 @@ import (
 	"strings"
 )
 
+// No leaks or early exit due to invalid options
+// This block defines the exit codes. Success
 const (
+	// No leaks or early exit due to invalid options
 	Success int = iota + 1
 	LeaksPresent
 	ErrorEncountered
 )
 
+// Options stores values of command line options
 type Options struct {
 	Verbose     bool   `short:"v" long:"verbose" description:"Show verbose output from audit"`
 	Repo        string `short:"r" long:"repo" description:"Target repository"`
@@ -40,7 +44,7 @@ type Options struct {
 	Report      string `long:"report" description:"path to write json leaks file"`
 	Redact      bool   `long:"redact" description:"redact secrets from log messages and leaks"`
 	Debug       bool   `long:"debug" description:"log debug messages"`
-	RepoConfig   bool   `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\" or \"gitleaks.toml\""`
+	RepoConfig  bool   `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\" or \"gitleaks.toml\""`
 
 	// Hosts
 	Host         string `long:"host" description:"git hosting service like gitlab or github. Supported hosts include: Github, Gitlab"`
@@ -84,7 +88,7 @@ func (opts Options) Guard() error {
 	return nil
 }
 
-// cloneOptions returns a git.cloneOptions pointer. The authentication method
+// CloneOptions returns a git.cloneOptions pointer. The authentication method
 // is determined by what is passed in via command-Line options. If No
 // Username/PW or AccessToken is available and the repo target is not using the
 // git protocol then the repo must be a available via no auth.
@@ -96,7 +100,7 @@ func (opts Options) CloneOptions() (*git.CloneOptions, error) {
 
 	if strings.HasPrefix(opts.Repo, "git") {
 		// using git protocol so needs ssh auth
-		auth, err := sshAuth(opts)
+		auth, err := SSHAuth(opts)
 		if err != nil {
 			return nil, err
 		}
@@ -145,11 +149,11 @@ func (opts Options) CloneOptions() (*git.CloneOptions, error) {
 	}, nil
 }
 
-// sshAuth tried to generate ssh public keys based on what was passed via cli. If no
+// SSHAuth tried to generate ssh public keys based on what was passed via cli. If no
 // path was passed via cli then this will attempt to retrieve keys from the default
 // location for ssh keys, $HOME/.ssh/id_rsa. This function is only called if the
 // repo url using the git:// protocol.
-func sshAuth(opts Options) (*ssh.PublicKeys, error) {
+func SSHAuth(opts Options) (*ssh.PublicKeys, error) {
 	if opts.SSH != "" {
 		return ssh.NewPublicKeysFromFile("git", opts.SSH, "")
 	}
@@ -161,7 +165,7 @@ func sshAuth(opts Options) (*ssh.PublicKeys, error) {
 	return ssh.NewPublicKeysFromFile("git", defaultPath, "")
 }
 
-// openLocal checks what options are set, if no remote targets are set
+// OpenLocal checks what options are set, if no remote targets are set
 // then return true
 func (opts Options) OpenLocal() bool {
 	if opts.Uncommited || opts.RepoPath != "" || opts.Repo == "" {

+ 9 - 0
test_data/test_configs/regex_entropy.toml

@@ -0,0 +1,9 @@
+[[rules]]
+	description = "entropy and regex"
+	regex = '''(?i)key(.{0,20})?['|"][0-9a-zA-Z]{16,45}['|"]'''
+    entropies = [
+        "4.5-4.7",
+        "5.5-6.3",
+    ]
+	tags = ["entropy"]
+

+ 16 - 0
test_data/test_regex_entropy.json

@@ -0,0 +1,16 @@
+[
+ {
+  "line": "    aws_access_key_id='AKIAIO5FODNN7EXAMPLE',",
+  "offender": "key_id='AKIAIO5FODNN7EXAMPLE'",
+  "commit": "6557c92612d3b35979bd426d429255b3bf9fab74",
+  "repo": "test_repo_1",
+  "rule": "entropy and regex",
+  "commitMessage": "commit 1 with secrets\n",
+  "author": "zach rice",
+  "email": "zricer@protonmail.com",
+  "file": "server.test.py",
+  "date": "2019-10-24T09:29:27-04:00",
+  "tags": "entropy",
+  "severity": ""
+ }
+]

+ 2 - 1
version/version.go

@@ -1,5 +1,6 @@
 package version
 
+// Version is loaded via  LDFLAGS:
 // VERSION := `git fetch --tags && git tag | sort -V | tail -1`
 // LDFLAGS=-ldflags "-X=github.com/zricethezav/gitleaks-ng/version.Version=$(VERSION)"
-var Version string
+var Version string