| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- package main
- import (
- "context"
- "github.com/google/go-github/github"
- "github.com/mitchellh/go-homedir"
- "golang.org/x/oauth2"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "path"
- "path/filepath"
- "regexp"
- "strings"
- )
- var (
- regexes map[string]*regexp.Regexp
- stopWords []string
- base64Chars string
- hexChars string
- assignRegex *regexp.Regexp
- fileDiffRegex *regexp.Regexp
- gitLeaksPath string
- gitLeaksClonePath string
- gitLeaksReportPath string
- )
- type RepoDesc struct {
- name string
- url string
- path string
- owner *Owner
- }
- type Owner struct {
- name string
- url string
- accountType string
- path string
- reportPath string
- }
- func init() {
- base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
- hexChars = "1234567890abcdefABCDEF"
- stopWords = []string{"setting", "info", "env", "environment"}
- regexes = map[string]*regexp.Regexp{
- "PKCS8": regexp.MustCompile("-----BEGIN PRIVATE KEY-----"),
- "RSA": regexp.MustCompile("-----BEGIN RSA PRIVATE KEY-----"),
- "SSH": regexp.MustCompile("-----BEGIN OPENSSH PRIVATE KEY-----"),
- "Facebook": regexp.MustCompile("(?i)facebook.*['|\"][0-9a-f]{32}['|\"]"),
- "Twitter": regexp.MustCompile("(?i)twitter.*['|\"][0-9a-zA-Z]{35,44}['|\"]"),
- "Github": regexp.MustCompile("(?i)github.*[['|\"]0-9a-zA-Z]{35,40}['|\"]"),
- "AWS": regexp.MustCompile("AKIA[0-9A-Z]{16}"),
- "Reddit": regexp.MustCompile("(?i)reddit.*['|\"][0-9a-zA-Z]{14}['|\"]"),
- "Heroku": regexp.MustCompile("(?i)heroku.*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}"),
- // "Custom": regexp.MustCompile(".*")
- }
- assignRegex = regexp.MustCompile(`(=|:|:=|<-)`)
- fileDiffRegex = regexp.MustCompile("diff --git a.+b/")
- homeDir, err := homedir.Dir()
- if err != nil {
- log.Fatal("Cant find home dir")
- }
- gitLeaksPath = filepath.Join(homeDir, ".gitleaks")
- if _, err := os.Stat(gitLeaksPath); os.IsNotExist(err) {
- os.Mkdir(gitLeaksPath, os.ModePerm)
- }
- gitLeaksClonePath = filepath.Join(gitLeaksPath, "clones")
- if _, err := os.Stat(gitLeaksClonePath); os.IsNotExist(err) {
- os.Mkdir(gitLeaksClonePath, os.ModePerm)
- }
- gitLeaksReportPath = filepath.Join(gitLeaksPath, "report")
- if _, err := os.Stat(gitLeaksReportPath); os.IsNotExist(err) {
- os.Mkdir(gitLeaksReportPath, os.ModePerm)
- }
- }
- // getOwner
- func getOwner(opts *Options) *Owner {
- var owner Owner
- if opts.RepoURL != "" {
- splitSlashes := strings.Split(opts.RepoURL, "/")
- owner = Owner{
- name: splitSlashes[len(splitSlashes)-2],
- url: opts.RepoURL,
- accountType: "users",
- }
- } else if opts.UserURL != "" {
- _, ownerName := path.Split(opts.UserURL)
- owner = Owner{
- name: ownerName,
- url: opts.UserURL,
- accountType: "user",
- }
- } else if opts.OrgURL != "" {
- _, ownerName := path.Split(opts.OrgURL)
- owner = Owner{
- name: ownerName,
- url: opts.OrgURL,
- accountType: "org",
- }
- }
- if opts.Tmp {
- dir, err := ioutil.TempDir("", owner.name)
- if err != nil {
- log.Fatal("Cant make temp dir")
- }
- owner.path = dir
- } else {
- owner.path = filepath.Join(gitLeaksClonePath, owner.name)
- if _, err := os.Stat(owner.path); os.IsNotExist(err) {
- os.Mkdir(owner.path, os.ModePerm)
- }
- }
- owner.reportPath = filepath.Join(gitLeaksPath, "report", owner.name)
- return &owner
- }
- // getRepos
- func getRepos(opts *Options, owner *Owner) []RepoDesc {
- var (
- allRepos []*github.Repository
- repos []*github.Repository
- repoDescs []RepoDesc
- resp *github.Response
- ctx = context.Background()
- err error
- )
- if opts.RepoURL != "" {
- _, repoName := path.Split(opts.RepoURL)
- if strings.HasSuffix(repoName, ".git") {
- repoName = repoName[:len(repoName)-4]
- }
- ownerPath := filepath.Join(owner.path, repoName)
- repo := RepoDesc{
- name: repoName,
- url: opts.RepoURL,
- owner: owner,
- path: ownerPath}
- repoDescs = append(repoDescs, repo)
- return repoDescs
- }
- tokenClient := getAccessToken(opts)
- gitClient := github.NewClient(tokenClient)
- // TODO include fork check
- orgOpt := &github.RepositoryListByOrgOptions{
- ListOptions: github.ListOptions{PerPage: 10},
- }
- userOpt := &github.RepositoryListOptions{
- ListOptions: github.ListOptions{PerPage: 10},
- }
- for {
- if opts.UserURL != "" {
- repos, resp, err = gitClient.Repositories.List(
- ctx, owner.name, userOpt)
- } else if opts.OrgURL != "" {
- repos, resp, err = gitClient.Repositories.ListByOrg(
- ctx, owner.name, orgOpt)
- }
- allRepos = append(allRepos, repos...)
- if resp.NextPage == 0 || err != nil {
- break
- }
- for _, repo := range repos {
- repoPath := filepath.Join(owner.path, *repo.Name)
- repoDescs = append(repoDescs,
- RepoDesc{
- name: *repo.Name,
- url: *repo.CloneURL,
- owner: owner,
- path: repoPath})
- }
- orgOpt.Page = resp.NextPage
- userOpt.Page = resp.NextPage
- }
- return repoDescs
- }
- // getAccessToken checks
- // 1. option
- // 2. env var
- // TODO. $HOME/.gitleaks/.creds
- func getAccessToken(opts *Options) *http.Client {
- var token string
- if opts.Token != "" {
- token = opts.Token
- } else {
- token = os.Getenv("GITHUB_TOKEN")
- }
- if token == "" {
- return nil
- }
- tokenService := oauth2.StaticTokenSource(
- &oauth2.Token{AccessToken: token},
- )
- tokenClient := oauth2.NewClient(context.Background(), tokenService)
- return tokenClient
- }
- func main() {
- args := os.Args[1:]
- opts := parseOptions(args)
- owner := getOwner(opts)
- repos := getRepos(opts, owner)
- start(repos, owner, opts)
- }
|