/
options.go
247 lines (226 loc) Β· 8.34 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package options
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"strings"
"github.com/zricethezav/gitleaks/v3/version"
"github.com/jessevdk/go-flags"
log "github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
)
// 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 = 0
LeaksPresent = 1
ErrorEncountered = 2
)
// 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"`
Config string `long:"config" description:"config path"`
Disk bool `long:"disk" description:"Clones repo(s) to disk"`
Version bool `long:"version" description:"version number"`
Username string `long:"username" description:"Username for git repo"`
Password string `long:"password" description:"Password for git repo"`
AccessToken string `long:"access-token" description:"Access token for git repo"`
Commit string `long:"commit" description:"sha of commit to audit"`
FilesAtCommit string `long:"files-at-commit" description:"sha of commit to audit all files at commit"`
Threads int `long:"threads" description:"Maximum number of threads gitleaks spawns"`
SSH string `long:"ssh-key" description:"path to ssh key used for auth"`
Uncommited bool `long:"uncommitted" description:"run gitleaks on uncommitted code"`
RepoPath string `long:"repo-path" description:"Path to repo"`
OwnerPath string `long:"owner-path" description:"Path to owner directory (repos discovered)"`
Branch string `long:"branch" description:"Branch to audit"`
Report string `long:"report" description:"path to write json leaks file"`
ReportFormat string `long:"report-format" default:"json" description:"json or csv"`
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\""`
PrettyPrint bool `long:"pretty" description:"Pretty print json if leaks are present"`
CommitFrom string `long:"commit-from" description:"Commit to start audit from"`
CommitTo string `long:"commit-to" description:"Commit to stop audit"`
Timeout string `long:"timeout" description:"Time allowed per audit. Ex: 10us, 30s, 1m, 1h10m1s"`
Depth int `long:"depth" description:"Number of commits to audit"`
// Hosts
Host string `long:"host" description:"git hosting service like gitlab or github. Supported hosts include: Github, Gitlab"`
BaseURL string `long:"baseurl" description:"Base URL for API requests. Defaults to the public GitLab or GitHub API, but can be set to a domain endpoint to use with a self hosted server."`
Organization string `long:"org" description:"organization to audit"`
User string `long:"user" description:"user to audit"`
PullRequest string `long:"pr" description:"pull/merge request url"`
ExcludeForks bool `long:"exclude-forks" description:"audit excludes forks"`
}
// ParseOptions is responsible for parsing options passed in by cli. An Options struct
// is returned if successful. This struct is passed around the program
// and will determine how the program executes. If err, an err message or help message
// will be displayed and the program will exit with code 0.
func ParseOptions() (Options, error) {
var opts Options
parser := flags.NewParser(&opts, flags.Default)
_, err := parser.Parse()
if err != nil {
if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type != flags.ErrHelp {
parser.WriteHelp(os.Stdout)
}
os.Exit(0)
}
if opts.Version {
if version.Version == "" {
fmt.Println("Gitleaks uses LDFLAGS to pull most recent version. Build with 'make build' for version")
} else {
fmt.Printf("%s\n", version.Version)
}
os.Exit(Success)
}
if opts.Debug {
log.SetLevel(log.DebugLevel)
}
return opts, nil
}
// Guard checks to makes sure there are no invalid options set.
// If invalid sets of options are present, a descriptive error will return
// else nil is returned
func (opts Options) Guard() error {
if !oneOrNoneSet(opts.Repo, opts.OwnerPath, opts.RepoPath, opts.Host) {
return fmt.Errorf("only one target option must can be set. target options: repo, owner-path, repo-path, host")
}
if !oneOrNoneSet(opts.Organization, opts.User, opts.PullRequest) {
return fmt.Errorf("only one target option must can be set. target options: repo, owner-path, repo-path, host")
}
if !oneOrNoneSet(opts.AccessToken, opts.Password) {
log.Warn("both access-token and password are set. Only password will be attempted")
}
return nil
}
func oneOrNoneSet(optStr ...string) bool {
c := 0
for _, s := range optStr {
if s != "" {
c++
}
}
if c <= 1 {
return true
}
return false
}
// 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.
func (opts Options) CloneOptions() (*git.CloneOptions, error) {
progress := ioutil.Discard
if opts.Verbose {
progress = os.Stdout
}
if strings.HasPrefix(opts.Repo, "git") {
// using git protocol so needs ssh auth
auth, err := SSHAuth(opts)
if err != nil {
return nil, err
}
return &git.CloneOptions{
URL: opts.Repo,
Auth: auth,
Progress: progress,
}, nil
}
if opts.Password != "" && opts.Username != "" {
// auth using username and password
return &git.CloneOptions{
URL: opts.Repo,
Auth: &http.BasicAuth{
Username: opts.Username,
Password: opts.Password,
},
Progress: progress,
}, nil
}
if opts.AccessToken != "" {
return &git.CloneOptions{
URL: opts.Repo,
Auth: &http.BasicAuth{
Username: "gitleaks_user",
Password: opts.AccessToken,
},
Progress: progress,
}, nil
}
if os.Getenv("GITLEAKS_ACCESS_TOKEN") != "" {
return &git.CloneOptions{
URL: opts.Repo,
Auth: &http.BasicAuth{
Username: "gitleaks_user",
Password: os.Getenv("GITLEAKS_ACCESS_TOKEN"),
},
Progress: progress,
}, nil
}
// No Auth, publicly available
return &git.CloneOptions{
URL: opts.Repo,
Progress: progress,
}, nil
}
// 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) {
if opts.SSH != "" {
return ssh.NewPublicKeysFromFile("git", opts.SSH, "")
}
c, err := user.Current()
if err != nil {
return nil, err
}
defaultPath := fmt.Sprintf("%s/.ssh/id_rsa", c.HomeDir)
return ssh.NewPublicKeysFromFile("git", defaultPath, "")
}
// 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 == "" {
return true
}
return false
}
// CheckUncommitted returns a boolean that indicates whether or not gitleaks should check unstaged pre-commit changes
// or if gitleaks should check the entire git history
func (opts Options) CheckUncommitted() bool {
// check to make sure no remote shit is set
if opts.Uncommited {
return true
}
if opts == (Options{}) {
return true
}
if opts.Repo != "" {
return false
}
if opts.RepoPath != "" {
return false
}
if opts.OwnerPath != "" {
return false
}
if opts.Host != "" {
return false
}
return true
}
// GetAccessToken accepts options and returns a string which is the access token to a git host.
// Setting this option or environment var is necessary if performing an audit with any of the git hosting providers
// in the host pkg. The access token set by cli options takes precedence over env vars.
func GetAccessToken(opts Options) string {
if opts.AccessToken != "" {
return opts.AccessToken
}
return os.Getenv("GITLEAKS_ACCESS_TOKEN")
}