From ca6594c346f0984a49e96094fca052017db7272d Mon Sep 17 00:00:00 2001 From: c0d3g33k Date: Sun, 24 Nov 2024 11:20:47 +0000 Subject: [PATCH] Refactor codebase --- cmd/detect.go | 35 +++++------- cmd/monitor.go | 39 ++++++++------ cmd/root.go | 18 ++----- cmd/scan.go | 22 +++----- common/common.go | 60 --------------------- common/read_json.go | 26 --------- common/save_data.go | 15 ------ config/gh_conf.go | 28 ---------- gh/connecter.go | 20 ------- gh/data.go | 44 ---------------- gh/get_new_repos.go | 45 ---------------- gh/get_org_users.go | 64 ---------------------- gh/get_pat_limit.go | 46 ---------------- gh/get_users_repos.go | 60 --------------------- go.mod | 39 +++++++------- go.sum | 79 ++++++++++++++++++++++++++-- logger/log.go | 23 -------- main.go | 5 +- notification/slack.go | 42 --------------- pkg/common/constants.go | 6 +++ {common => pkg/common}/slice_diff.go | 0 pkg/common/variables.go | 17 ++++++ pkg/config/config.go | 28 ++++++++++ pkg/github/api_limits.go | 29 ++++++++++ pkg/github/connecter.go | 63 ++++++++++++++++++++++ pkg/github/get_new_repos.go | 48 +++++++++++++++++ pkg/github/get_org_users.go | 56 ++++++++++++++++++++ {gh => pkg/github}/get_response.go | 28 ++++------ pkg/github/get_users_repos.go | 46 ++++++++++++++++ pkg/models/models.go | 50 ++++++++++++++++++ pkg/notification/notify.go | 38 +++++++++++++ pkg/notification/slack.go | 47 +++++++++++++++++ pkg/secrets/get_secrets.go | 58 ++++++++++++++++++++ {secrets => pkg/secrets}/utils.go | 36 +++++++------ pkg/ui/ui.go | 47 +++++++++++++++++ pkg/utils/read_data.go | 24 +++++++++ pkg/utils/save_data.go | 16 ++++++ pkg/utils/utils.go | 48 +++++++++++++++++ reporter/notify.go | 45 ---------------- secrets/get_secrets.go | 58 -------------------- 40 files changed, 793 insertions(+), 705 deletions(-) delete mode 100644 common/common.go delete mode 100644 common/read_json.go delete mode 100644 common/save_data.go delete mode 100644 config/gh_conf.go delete mode 100644 gh/connecter.go delete mode 100644 gh/data.go delete mode 100644 gh/get_new_repos.go delete mode 100644 gh/get_org_users.go delete mode 100644 gh/get_pat_limit.go delete mode 100644 gh/get_users_repos.go delete mode 100644 logger/log.go delete mode 100644 notification/slack.go create mode 100644 pkg/common/constants.go rename {common => pkg/common}/slice_diff.go (100%) create mode 100644 pkg/common/variables.go create mode 100644 pkg/config/config.go create mode 100644 pkg/github/api_limits.go create mode 100644 pkg/github/connecter.go create mode 100644 pkg/github/get_new_repos.go create mode 100644 pkg/github/get_org_users.go rename {gh => pkg/github}/get_response.go (56%) create mode 100644 pkg/github/get_users_repos.go create mode 100644 pkg/models/models.go create mode 100644 pkg/notification/notify.go create mode 100644 pkg/notification/slack.go create mode 100644 pkg/secrets/get_secrets.go rename {secrets => pkg/secrets}/utils.go (60%) create mode 100644 pkg/ui/ui.go create mode 100644 pkg/utils/read_data.go create mode 100644 pkg/utils/save_data.go create mode 100644 pkg/utils/utils.go delete mode 100644 reporter/notify.go delete mode 100644 secrets/get_secrets.go diff --git a/cmd/detect.go b/cmd/detect.go index 1f03534..bf90a82 100644 --- a/cmd/detect.go +++ b/cmd/detect.go @@ -1,12 +1,11 @@ package cmd import ( - "strconv" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/gh" - "github.com/boringtools/git-alerts/logger" - "github.com/boringtools/git-alerts/secrets" + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/github" + "github.com/boringtools/git-alerts/pkg/secrets" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" "github.com/spf13/cobra" ) @@ -15,29 +14,19 @@ var detectCmd = &cobra.Command{ Short: "Scan with secrets detection", Long: ``, Run: func(cmd *cobra.Command, args []string) { - envs := map[string]string{ - "org": org, - "rfp": report, - "command": cmd.Use, - "csv": strconv.FormatBool(csv), - "trufflehog": strconv.FormatBool(trufflehog), - "trufflehog-verified": strconv.FormatBool(trufflehogVerified), - "gitleaks": strconv.FormatBool(gitleaks), - } - common.SetEnvs(envs) - - gh.Connecter() - secrets.GetSecrets() + common.Command = cmd.Use + github.Connecter() + secrets.RunSecretsScan() - logger.LogP("Scan ended : ", common.GetTime()) + ui.PrintSuccess("Scan Ended : %s", utils.GetCurrentTime()) }, } func init() { rootCmd.AddCommand(detectCmd) - detectCmd.PersistentFlags().BoolVarP(&trufflehog, "trufflehog", "t", false, "Scan secrets using Trufflehog") - detectCmd.PersistentFlags().BoolVarP(&trufflehogVerified, "trufflehog-verified", "v", true, "Scan Trufflehog verified secrets") - detectCmd.PersistentFlags().BoolVarP(&gitleaks, "gitleaks", "g", false, "Scan secrets using Gitleaks") + detectCmd.PersistentFlags().BoolVarP(&common.TrufflehogScan, "trufflehog", "t", false, "Scan secrets using Trufflehog") + detectCmd.PersistentFlags().BoolVarP(&common.TrufflehogVerifiedScan, "trufflehog-verified", "v", true, "Scan secrets using Trufflehog verified option") + detectCmd.PersistentFlags().BoolVarP(&common.GitleaksScan, "gitleaks", "g", false, "Scan secrets using Gitleaks") detectCmd.MarkFlagsOneRequired("trufflehog", "trufflehog-verified", "gitleaks") } diff --git a/cmd/monitor.go b/cmd/monitor.go index dfc3a59..2ae182a 100644 --- a/cmd/monitor.go +++ b/cmd/monitor.go @@ -1,13 +1,14 @@ package cmd import ( - "strconv" + "os" - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/gh" + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/github" + "github.com/boringtools/git-alerts/pkg/notification" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" - "github.com/boringtools/git-alerts/logger" - "github.com/boringtools/git-alerts/reporter" "github.com/spf13/cobra" ) @@ -16,25 +17,29 @@ var monitorCmd = &cobra.Command{ Short: "Monitor public repositories", Long: ``, Run: func(cmd *cobra.Command, args []string) { + common.Command = cmd.Use - envs := map[string]string{ - "org": org, - "rfp": report, - "command": cmd.Use, - "csv": strconv.FormatBool(csv), - "slack": strconv.FormatBool(slack), + if common.SlackNotification { + _, isSlackHook := os.LookupEnv("SLACK_HOOK") + + if !isSlackHook { + ui.PrintError("SLACK_HOOK is not configured in ENV variable") + os.Exit(1) + } } - common.SetEnvs(envs) + if !utils.IsPreviousScanFileExists() { + ui.PrintError("Previous scan files not found, Please consider running SCAN command first") + os.Exit(1) + } - common.CheckScanFiles() - gh.Connecter() - reporter.Notify() - logger.LogP("Scan ended : ", common.GetTime()) + github.Connecter() + notification.Notify() + ui.PrintSuccess("Scan Ended : %s", utils.GetCurrentTime()) }, } func init() { rootCmd.AddCommand(monitorCmd) - monitorCmd.PersistentFlags().BoolVarP(&slack, "slack-alert", "s", false, "Slack notification") + monitorCmd.PersistentFlags().BoolVarP(&common.SlackNotification, "slack-alert", "s", false, "Slack notification") } diff --git a/cmd/root.go b/cmd/root.go index 5e7e69c..1f6fc6c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,24 +3,14 @@ package cmd import ( "os" + "github.com/boringtools/git-alerts/pkg/common" "github.com/spf13/cobra" ) -var ( - org string - report string - csv bool - slack bool - trufflehog bool - trufflehogVerified bool - gitleaks bool -) - var rootCmd = &cobra.Command{ Use: "git-alerts", Short: "A Public Git repository & misconfiguration detection tool", Long: ``, - // Run: func(cmd *cobra.Command, args []string) { }, } func Execute() { @@ -31,9 +21,7 @@ func Execute() { } func init() { - rootCmd.PersistentFlags().StringVarP(&org, "org", "o", "", "GitHub organization name") + rootCmd.PersistentFlags().StringVarP(&common.GitHubOrg, "org", "o", "", "GitHub organization name") rootCmd.MarkPersistentFlagRequired("org") - rootCmd.PersistentFlags().BoolVarP(&csv, "csv", "c", false, "CSV report format") - rootCmd.PersistentFlags().StringVarP(&report, "report-path", "r", "/tmp/", "Report file path") - + rootCmd.PersistentFlags().StringVarP(&common.ReportPath, "report-path", "r", "/tmp/", "Report file path") } diff --git a/cmd/scan.go b/cmd/scan.go index 142ab94..e7f35f1 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -1,11 +1,10 @@ package cmd import ( - "strconv" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/gh" - "github.com/boringtools/git-alerts/logger" + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/github" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" "github.com/spf13/cobra" ) @@ -15,17 +14,10 @@ var scanCmd = &cobra.Command{ Long: ``, Run: func(cmd *cobra.Command, args []string) { - envs := map[string]string{ - "org": org, - "rfp": report, - "command": cmd.Use, - "csv": strconv.FormatBool(csv), - } - - common.SetEnvs(envs) - gh.Connecter() + common.Command = cmd.Use + github.Connecter() - logger.LogP("Scan ended : ", common.GetTime()) + ui.PrintSuccess("Scan Ended : %s", utils.GetCurrentTime()) }, } diff --git a/common/common.go b/common/common.go deleted file mode 100644 index ac0e5ad..0000000 --- a/common/common.go +++ /dev/null @@ -1,60 +0,0 @@ -package common - -import ( - "os" - "time" - - "github.com/boringtools/git-alerts/config" - "github.com/boringtools/git-alerts/logger" -) - -var ( - Auth bool -) - -func GetTime() (date string) { - time := time.Now() - date = time.Format("Mon Jan 2 15:04:05 MST 2006") - return date -} - -func SetEnvs(data map[string]string) { - for key, value := range data { - os.Setenv(key, value) - } -} - -func StartChecks() { - - _, isGitHubPat := os.LookupEnv("GITHUB_PAT") - _, isSlackHook := os.LookupEnv("SLACK_HOOK") - - if isGitHubPat { - Auth = true - } - - if os.Getenv("slack") == "true" { - if !isSlackHook { - logger.LogERR("SLACK_HOOK is not configured in ENV variable") - os.Exit(1) - } - } - -} - -func CheckScanFiles() { - _, errScanFile := os.Stat(config.GhFilePaths()[1]) - - if errScanFile != nil { - logger.LogERR("Previous scan files not found") - logger.LogERR("If you are running it for the first time") - logger.LogERR("Please consider running the SCAN command first") - os.Exit(1) - } -} - -func Start() { - currentTime := GetTime() - logger.LogP("Scan started : ", currentTime) - -} diff --git a/common/read_json.go b/common/read_json.go deleted file mode 100644 index fafd5e9..0000000 --- a/common/read_json.go +++ /dev/null @@ -1,26 +0,0 @@ -package common - -import ( - "io" - "os" - - "github.com/boringtools/git-alerts/logger" -) - -func GetJsonFileContent(fileName string) (jsonData []byte) { - filePath := os.Getenv("rfp") + fileName + ".json" - - file, errFile := os.Open(filePath) - - if errFile != nil { - logger.LogERR("GetJsonFileContent - Error in location file") - } - - jsonData, errRead := io.ReadAll(file) - - if errRead != nil { - logger.LogERR("GetJsonFileContent - Error in reading file content") - } - - return jsonData -} diff --git a/common/save_data.go b/common/save_data.go deleted file mode 100644 index f37be28..0000000 --- a/common/save_data.go +++ /dev/null @@ -1,15 +0,0 @@ -package common - -import ( - "os" - - "github.com/boringtools/git-alerts/logger" -) - -func SaveToJson(content []byte, fileName string) { - - fullPath := os.Getenv("rfp") + fileName + ".json" - - os.WriteFile(fullPath, content, 0644) - logger.LogP("Data saved successfully : ", fullPath) -} diff --git a/config/gh_conf.go b/config/gh_conf.go deleted file mode 100644 index b741c29..0000000 --- a/config/gh_conf.go +++ /dev/null @@ -1,28 +0,0 @@ -package config - -import "os" - -func GhUrls() (urls []string) { - api := "https://api.github.com" - rateLimit := api + "/rate_limit" - users := api + "/orgs/" + os.Getenv("org") + "/members" - - urls = append(urls, api, rateLimit, users) - return urls -} - -func GhFileNames() (fileNames []string) { - users := os.Getenv("org") + "_users_" + os.Getenv("command") - usersRepo := os.Getenv("org") + "_users_repos_" + os.Getenv("command") - - fileNames = append(fileNames, users, usersRepo) - return fileNames -} - -func GhFilePaths() (filePaths []string) { - userRepoMonitor := os.Getenv("rfp") + GhFileNames()[1] + ".json" - userRepoScan := os.Getenv("rfp") + os.Getenv("org") + "_users_repos_scan.json" - - filePaths = append(filePaths, userRepoMonitor, userRepoScan) - return filePaths -} diff --git a/gh/connecter.go b/gh/connecter.go deleted file mode 100644 index 182246b..0000000 --- a/gh/connecter.go +++ /dev/null @@ -1,20 +0,0 @@ -package gh - -import ( - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" -) - -func Connecter() { - common.StartChecks() - common.Start() - - CheckPatLimit() - users := GetUsers() - common.SaveToJson(users, config.GhFileNames()[0]) - - usersRepo := GetUsersRepos() - common.SaveToJson(usersRepo, config.GhFileNames()[1]) - - GetData() -} diff --git a/gh/data.go b/gh/data.go deleted file mode 100644 index eac421e..0000000 --- a/gh/data.go +++ /dev/null @@ -1,44 +0,0 @@ -package gh - -import ( - "encoding/json" - "os" - - "github.com/boringtools/git-alerts/common" - - "github.com/boringtools/git-alerts/config" - "github.com/jedib0t/go-pretty/v6/table" -) - -var Summery map[string]interface{} - -var ( - usersIn interface{} - usersRepoIn interface{} -) - -func GetData() { - usersFile := common.GetJsonFileContent(config.GhFileNames()[0]) - json.Unmarshal(usersFile, &usersIn) - usersLength, _ := usersIn.([]interface{}) - - usersRepoFile := common.GetJsonFileContent(config.GhFileNames()[1]) - json.Unmarshal(usersRepoFile, &usersRepoIn) - usersReposLength, _ := usersRepoIn.([]interface{}) - - tbl := table.NewWriter() - tbl.SetOutputMirror(os.Stdout) - tbl.AppendHeader(table.Row{"Scan Summery", "Data"}) - tbl.AppendRows([]table.Row{ - {"Total Users", len(usersLength)}, - {"Total Users Repositories", len(usersReposLength)}, - }) - - tbl.AppendSeparator() - tbl.Render() - -} - -func init() { - Summery = map[string]interface{}{} -} diff --git a/gh/get_new_repos.go b/gh/get_new_repos.go deleted file mode 100644 index c3f45d6..0000000 --- a/gh/get_new_repos.go +++ /dev/null @@ -1,45 +0,0 @@ -package gh - -import ( - "encoding/json" - "os" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" -) - -type Repos struct { - URL string `json:"html_url"` -} - -var ( - newRepos []Repos - oldRepos []Repos - allNewRepos []string - allOldRepos []string -) - -func GetNewRepos() (diff []string) { - newFile := common.GetJsonFileContent(config.GhFileNames()[1]) - oldFile := common.GetJsonFileContent(os.Getenv("org") + "_users_repos_scan") - - json.Unmarshal(newFile, &newRepos) - json.Unmarshal(oldFile, &oldRepos) - - for _, value := range newRepos { - allNewRepos = append(allNewRepos, value.URL) - } - - for _, value := range oldRepos { - allOldRepos = append(allOldRepos, value.URL) - } - - diff = common.SliceDiff(allNewRepos, allOldRepos) - - if len(diff) == 0 { - return nil - } - - os.Rename(config.GhFilePaths()[0], config.GhFilePaths()[1]) - return diff -} diff --git a/gh/get_org_users.go b/gh/get_org_users.go deleted file mode 100644 index 19960b6..0000000 --- a/gh/get_org_users.go +++ /dev/null @@ -1,64 +0,0 @@ -package gh - -import ( - "encoding/json" - "os" - "strconv" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" - "github.com/boringtools/git-alerts/logger" -) - -type Users struct { - Username string `json:"login"` - Url string `json:"url"` - ProfileUrl string `json:"html_url"` - ReposUrl string `json:"repos_url"` - EventsUrl string `json:"events_url"` - ReceivedEventsUrl string `json:"received_events_url"` - Type string `json:"type"` - Admin bool `json:"site_admin"` -} - -var ( - user []Users - allUsers []Users -) - -func GetUsers() (jsonData []byte) { - logger.Log("Fetching " + os.Getenv("org") + " users") - - url := config.GhUrls()[2] - parameters := map[string]string{ - "per_page": "100", - } - ghResponse, pageLength := GetResponse(url, common.Auth, parameters) - - if pageLength == 0 { - - json.Unmarshal(ghResponse, &user) - allUsers = append(allUsers, user...) - jsonData, err := json.Marshal(allUsers) - - if err != nil { - logger.LogERR("GetUsers - Error in marshalling json data") - } - return jsonData - } else { - for i := 1; i <= pageLength; i++ { - parameters["page"] = strconv.Itoa(i) - ghResponse, _ := GetResponse(url, common.Auth, parameters) - - json.Unmarshal(ghResponse, &user) - allUsers = append(allUsers, user...) - } - - jsonData, err := json.Marshal(allUsers) - - if err != nil { - logger.LogERR("GetUsers - Error in marshalling json data") - } - return jsonData - } -} diff --git a/gh/get_pat_limit.go b/gh/get_pat_limit.go deleted file mode 100644 index 31281cd..0000000 --- a/gh/get_pat_limit.go +++ /dev/null @@ -1,46 +0,0 @@ -package gh - -import ( - "encoding/json" - "os" - "time" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" - "github.com/boringtools/git-alerts/logger" -) - -type Rate struct { - Rate Limits `json:"rate"` -} -type Limits struct { - Total int `json:"limit"` - Used int `json:"used"` - Remaining int `json:"remaining"` - Reset int `json:"reset"` -} - -func CheckPatLimit() { - var limit Rate - url := config.GhUrls()[1] - parameters := map[string]string{} - - ghResponse, _ := GetResponse(url, common.Auth, parameters) - json.Unmarshal(ghResponse, &limit) - - remaining := limit.Rate.Remaining - unixTime := limit.Rate.Reset - time := time.Unix(int64(unixTime), 0) - date := time.Format("Mon Jan 2 15:04:05 MST 2006") - - if remaining <= 10 { - logger.LogERR("GitHub request limit exceeded") - logger.LogERR("Please try again once the limit reset or try with GitHub PAT") - logger.LogERRP("Remaining request limit : ", remaining) - logger.LogERRP("Reset time : ", date) - os.Exit(1) - } - - logger.LogP("Remaining request limit : ", remaining) - -} diff --git a/gh/get_users_repos.go b/gh/get_users_repos.go deleted file mode 100644 index d2ea16d..0000000 --- a/gh/get_users_repos.go +++ /dev/null @@ -1,60 +0,0 @@ -package gh - -import ( - "encoding/json" - "os" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" - "github.com/boringtools/git-alerts/logger" -) - -type Repo struct { - FullName string `json:"full_name"` - Private bool `json:"private"` - HtmlURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - Created string `json:"created_at"` - Updated string `json:"updated_at"` - Push string `json:"pushed_at"` - CloneURL string `json:"clone_url"` - Visiability string `json:"visibility"` -} - -type RepoURL struct { - URL string `json:"repos_url"` -} - -var ( - rURL []RepoURL - repos []Repo - allRepos []Repo -) - -func GetUsersRepos() (jsonData []byte) { - logger.Log("Fetching " + os.Getenv("org") + " users public repositories") - - users := common.GetJsonFileContent(config.GhFileNames()[0]) - - json.Unmarshal(users, &rURL) - - parameters := map[string]string{ - "per_page": "100", - } - for _, value := range rURL { - - usersRepo, _ := GetResponse(value.URL, common.Auth, parameters) - - json.Unmarshal(usersRepo, &repos) - allRepos = append(allRepos, repos...) - - } - - jsonData, err := json.Marshal(allRepos) - - if err != nil { - logger.LogERR("GetUsers - Error in marshalling json data") - } - return jsonData -} diff --git a/go.mod b/go.mod index 461bb36..5a58073 100644 --- a/go.mod +++ b/go.mod @@ -1,39 +1,42 @@ module github.com/boringtools/git-alerts -go 1.21.2 +go 1.22.0 -require github.com/spf13/cobra v1.8.0 +toolchain go1.23.2 + +require github.com/spf13/cobra v1.8.1 require ( - dario.cat/mergo v1.0.0 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.0.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.2 // indirect + github.com/cloudflare/circl v1.5.0 // indirect + github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-billy/v5 v5.6.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.2.2 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/tools v0.27.0 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) require ( github.com/go-git/go-git/v5 v5.12.0 github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jedib0t/go-pretty/v6 v6.4.9 - github.com/sirupsen/logrus v1.9.3 + github.com/jedib0t/go-pretty/v6 v6.6.2 github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 4300050..a383f1f 100644 --- a/go.sum +++ b/go.sum @@ -1,71 +1,116 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= +github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= +github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= +github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc= github.com/jedib0t/go-pretty/v6 v6.4.9/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= +github.com/jedib0t/go-pretty/v6 v6.6.2 h1:27bLj3nRODzaiA7tPIxy9UVWHoPspFfME9XxgwiiNsM= +github.com/jedib0t/go-pretty/v6 v6.6.2/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -76,10 +121,14 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -89,9 +138,14 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -101,7 +155,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -109,11 +162,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -121,15 +179,26 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/logger/log.go b/logger/log.go deleted file mode 100644 index 5c793a6..0000000 --- a/logger/log.go +++ /dev/null @@ -1,23 +0,0 @@ -package logger - -import "github.com/sirupsen/logrus" - -func Log(text string) { - logrus.Info(text) -} - -func LogP(text string, args any) { - logrus.Info(text, args) -} - -func LogWRN(text string) { - logrus.Warn(text) -} - -func LogERR(text string) { - logrus.Error(text) -} - -func LogERRP(text string, args any) { - logrus.Error(text, args) -} diff --git a/main.go b/main.go index e490417..e5e1219 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,8 @@ package main import ( - "fmt" - "github.com/boringtools/git-alerts/cmd" + "github.com/boringtools/git-alerts/pkg/ui" ) var banner string = ` @@ -16,6 +15,6 @@ var banner string = ` ` func main() { - fmt.Println(banner) + ui.PrintBanner(banner) cmd.Execute() } diff --git a/notification/slack.go b/notification/slack.go deleted file mode 100644 index 0b2d731..0000000 --- a/notification/slack.go +++ /dev/null @@ -1,42 +0,0 @@ -package notification - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "os" - - "github.com/boringtools/git-alerts/logger" -) - -func SlackNotification(content string) { - - jsonData := []byte("{'text': '" + content + "'}") - json.Marshal(jsonData) - - slackHook := os.Getenv("SLACK_HOOK") - request, error := http.NewRequest("POST", slackHook, bytes.NewReader(jsonData)) - request.Header.Set("Content-Type", "application/json; charset=UTF-8") - - if error != nil { - logger.LogERR("Error in making HTTP request to slack") - } - - client := &http.Client{} - response, error := client.Do(request) - if error != nil { - panic(error) - } - - defer response.Body.Close() - - body, _ := io.ReadAll(response.Body) - sBody := string(body) - - if sBody == "ok" { - logger.Log("Slack notification sent successfully") - } else { - logger.LogERR("Error in sending slack notification") - } -} diff --git a/pkg/common/constants.go b/pkg/common/constants.go new file mode 100644 index 0000000..7a7f776 --- /dev/null +++ b/pkg/common/constants.go @@ -0,0 +1,6 @@ +package common + +const ( + GitHubAPIBaseURL = "https://api.github.com" + GitHubAPIRateLimit = "https://api.github.com/rate_limit" +) diff --git a/common/slice_diff.go b/pkg/common/slice_diff.go similarity index 100% rename from common/slice_diff.go rename to pkg/common/slice_diff.go diff --git a/pkg/common/variables.go b/pkg/common/variables.go new file mode 100644 index 0000000..046945e --- /dev/null +++ b/pkg/common/variables.go @@ -0,0 +1,17 @@ +package common + +var ( + Command string + GitHubOrg string + ReportPath string + SlackNotification bool + TrufflehogScan bool + TrufflehogVerifiedScan bool + GitleaksScan bool + AuthenticatedScan bool +) + +var ( + NumberOfGitHubUsers int + NumberOfPublicRepositories int +) diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..26bfdba --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,28 @@ +package config + +import ( + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/models" +) + +func GetGitHubAPIEndPoints() *models.GitHubAPIEndPoints { + return &models.GitHubAPIEndPoints{ + GetUsers: common.GitHubAPIBaseURL + "/orgs/" + common.GitHubOrg + "/members", + } +} + +func GetReportFileNames() *models.ReportFileNames { + return &models.ReportFileNames{ + GitHubOrgUsers: common.GitHubOrg + "_github_users.json", + GitHubOrgPublicRepos: common.GitHubOrg + "_public_repositories.json", + GitHubOrgPublicReposNew: common.GitHubOrg + "_public_repositories_new.json", + } +} + +func GetReportFilePaths() *models.ReportFileNames { + return &models.ReportFileNames{ + GitHubOrgUsers: common.ReportPath + GetReportFileNames().GitHubOrgUsers, + GitHubOrgPublicRepos: common.ReportPath + GetReportFileNames().GitHubOrgPublicRepos, + GitHubOrgPublicReposNew: common.ReportPath + GetReportFileNames().GitHubOrgPublicReposNew, + } +} diff --git a/pkg/github/api_limits.go b/pkg/github/api_limits.go new file mode 100644 index 0000000..4ca45c2 --- /dev/null +++ b/pkg/github/api_limits.go @@ -0,0 +1,29 @@ +package github + +import ( + "encoding/json" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/models" +) + +func GetGitHubPATLimits() (*models.Limits, error) { + url := common.GitHubAPIRateLimit + + parameters := map[string]string{} + var limit models.GitHubPATLimits + + ghResponse, _, _ := GetGitHubResponse(url, common.AuthenticatedScan, parameters) + err := json.Unmarshal(ghResponse, &limit) + + if err != nil { + return nil, err + } + + return &models.Limits{ + Total: limit.Rate.Total, + Used: limit.Rate.Used, + Remaining: limit.Rate.Remaining, + Reset: limit.Rate.Reset, + }, nil +} diff --git a/pkg/github/connecter.go b/pkg/github/connecter.go new file mode 100644 index 0000000..8896523 --- /dev/null +++ b/pkg/github/connecter.go @@ -0,0 +1,63 @@ +package github + +import ( + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" +) + +func Connecter() { + utils.CheckAuthenticatedScan() + ui.PrintSuccess("Scan started : %s", utils.GetCurrentTime()) + + gitHubAPILimit, errLimit := GetGitHubPATLimits() + + if errLimit != nil { + ui.PrintError("error fetching GitHub API limits : %s", errLimit) + } + ui.PrintMsg("Remaining PAT limit : %v", gitHubAPILimit.Remaining) + + if gitHubAPILimit.Remaining <= 1 { + ui.PrintError("GitHub PAT limit exceeded") + } + + users, errGetGitHubUsers := GetGitHubUsers() + if errGetGitHubUsers != nil { + ui.PrintError("error fetching GitHub users : %s", errGetGitHubUsers) + } + + errSaveToJson := utils.SaveToJson(users, config.GetReportFilePaths().GitHubOrgUsers) + + if errSaveToJson != nil { + ui.PrintError("error saving data to JSON : %s", errSaveToJson) + } else { + ui.PrintSuccess("%s users fetched successfully %s", common.GitHubOrg, config.GetReportFilePaths().GitHubOrgUsers) + } + + usersRepo, errGetGitHubUsers := GetGitHubUsersRepos() + + if errGetGitHubUsers != nil { + ui.PrintError("%s", errGetGitHubUsers) + } + + if common.Command == "monitor" { + errSaveToJson := utils.SaveToJson(usersRepo, config.GetReportFilePaths().GitHubOrgPublicReposNew) + + if errSaveToJson != nil { + ui.PrintError("error saving data to JSON : %s", errSaveToJson) + } else { + ui.PrintSuccess("%s public repositories fetched successfully %s", common.GitHubOrg, config.GetReportFilePaths().GitHubOrgPublicRepos) + } + } else { + errSaveToJson := utils.SaveToJson(usersRepo, config.GetReportFilePaths().GitHubOrgPublicRepos) + + if errSaveToJson != nil { + ui.PrintError("error saving data to JSON : %s", errSaveToJson) + } else { + ui.PrintSuccess("%s public repositories fetched successfully %s", common.GitHubOrg, config.GetReportFilePaths().GitHubOrgPublicRepos) + } + } + + utils.PrintSummery() +} diff --git a/pkg/github/get_new_repos.go b/pkg/github/get_new_repos.go new file mode 100644 index 0000000..60515ee --- /dev/null +++ b/pkg/github/get_new_repos.go @@ -0,0 +1,48 @@ +package github + +import ( + "encoding/json" + "os" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/boringtools/git-alerts/pkg/models" + "github.com/boringtools/git-alerts/pkg/utils" +) + +var ( + newRepos []models.GitHubRepository + oldRepos []models.GitHubRepository + allNewRepos []string + allOldRepos []string +) + +func GetNewPublicRepositories() ([]string, error) { + newFile, _ := utils.GetJSONFileContent(config.GetReportFilePaths().GitHubOrgPublicReposNew) + oldFile, _ := utils.GetJSONFileContent(config.GetReportFilePaths().GitHubOrgPublicRepos) + + errNewFile := json.Unmarshal(newFile, &newRepos) + + if errNewFile != nil { + return nil, errNewFile + } + + errOldFile := json.Unmarshal(oldFile, &oldRepos) + + if errOldFile != nil { + return nil, errOldFile + } + + for _, value := range newRepos { + allNewRepos = append(allNewRepos, value.HtmlURL) + } + + for _, value := range oldRepos { + allOldRepos = append(allOldRepos, value.HtmlURL) + } + + lisOfNewPublicRepos := common.SliceDiff(allNewRepos, allOldRepos) + + os.Rename(config.GetReportFilePaths().GitHubOrgPublicReposNew, config.GetReportFilePaths().GitHubOrgPublicRepos) + return lisOfNewPublicRepos, nil +} diff --git a/pkg/github/get_org_users.go b/pkg/github/get_org_users.go new file mode 100644 index 0000000..975af4c --- /dev/null +++ b/pkg/github/get_org_users.go @@ -0,0 +1,56 @@ +package github + +import ( + "encoding/json" + "strconv" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/boringtools/git-alerts/pkg/models" + "github.com/boringtools/git-alerts/pkg/ui" +) + +var ( + user []models.GitHubUser + allUsers []models.GitHubUser +) + +func GetGitHubUsers() ([]byte, error) { + ui.PrintMsg("Fetching %s users", common.GitHubOrg) + + url := config.GetGitHubAPIEndPoints().GetUsers + parameters := map[string]string{ + "per_page": "100", + } + + ghResponse, pageLength, _ := GetGitHubResponse(url, common.AuthenticatedScan, parameters) + + if pageLength == 0 { + + json.Unmarshal(ghResponse, &user) + allUsers = append(allUsers, user...) + jsonData, err := json.Marshal(allUsers) + + if err != nil { + return nil, err + } + common.NumberOfGitHubUsers = len(allUsers) + return jsonData, nil + } else { + for i := 1; i <= pageLength; i++ { + parameters["page"] = strconv.Itoa(i) + ghResponse, _, _ := GetGitHubResponse(url, common.AuthenticatedScan, parameters) + + json.Unmarshal(ghResponse, &user) + allUsers = append(allUsers, user...) + } + + jsonData, err := json.Marshal(allUsers) + + if err != nil { + return nil, err + } + common.NumberOfGitHubUsers = len(allUsers) + return jsonData, nil + } +} diff --git a/gh/get_response.go b/pkg/github/get_response.go similarity index 56% rename from gh/get_response.go rename to pkg/github/get_response.go index ef671e3..699923a 100644 --- a/gh/get_response.go +++ b/pkg/github/get_response.go @@ -1,4 +1,4 @@ -package gh +package github import ( "fmt" @@ -8,15 +8,13 @@ import ( "os" "strconv" "strings" - - "github.com/boringtools/git-alerts/logger" ) var ( pageLength int ) -func GetResponse(ghURL string, auth bool, params map[string]string) ([]byte, int) { +func GetGitHubResponse(URL string, auth bool, params map[string]string) ([]byte, int, error) { parameters := url.Values{} @@ -24,31 +22,28 @@ func GetResponse(ghURL string, auth bool, params map[string]string) ([]byte, int parameters.Add(key, value) } - fullURL := fmt.Sprintf("%s?%s", ghURL, parameters.Encode()) + fullURL := fmt.Sprintf("%s?%s", URL, parameters.Encode()) client := &http.Client{} request, errRequest := http.NewRequest("GET", fullURL, nil) if errRequest != nil { - logger.LogERR("GetResponse - Error in making HTTP calls to GitHub") - panic(errRequest) + return nil, 0, errRequest } if auth { - pat := "token " + os.Getenv("GITHUB_PAT") - request.Header.Add("Authorization", pat) + token := "Bearer " + os.Getenv("GITHUB_PAT") + request.Header.Add("Authorization", token) } response, errResponse := client.Do(request) if errResponse != nil { - logger.LogERR("GetResponse - Error in fetching HTTP Response") - panic(errResponse) + return nil, 0, errResponse } - if response.StatusCode != 200 { - logger.LogERR("Please provide a valid GitHub PAT") - os.Exit(1) + if response.StatusCode != http.StatusOK { + return nil, 0, fmt.Errorf("unable to fetch response, status code : %s", response.Status) } getLinkAttr := response.Header.Get("Link") @@ -71,10 +66,9 @@ func GetResponse(ghURL string, auth bool, params map[string]string) ([]byte, int rowResponse, errRead := io.ReadAll(response.Body) if errRead != nil { - logger.LogERR("GetResponse - Error in reading response body") - panic(errRead) + return nil, 0, errRead } defer response.Body.Close() - return rowResponse, pageLength + return rowResponse, pageLength, nil } diff --git a/pkg/github/get_users_repos.go b/pkg/github/get_users_repos.go new file mode 100644 index 0000000..ea391b9 --- /dev/null +++ b/pkg/github/get_users_repos.go @@ -0,0 +1,46 @@ +package github + +import ( + "encoding/json" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/boringtools/git-alerts/pkg/models" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" +) + +type RepoURL struct { + URL string `json:"repos_url"` +} + +var ( + rURL []RepoURL + repos []models.GitHubRepository + allRepos []models.GitHubRepository +) + +func GetGitHubUsersRepos() ([]byte, error) { + ui.PrintMsg("Fetching " + common.GitHubOrg + " users public repositories") + + users, _ := utils.GetJSONFileContent(config.GetReportFilePaths().GitHubOrgUsers) + json.Unmarshal(users, &rURL) + + parameters := map[string]string{ + "per_page": "100", + } + for _, value := range rURL { + usersRepo, _, _ := GetGitHubResponse(value.URL, common.AuthenticatedScan, parameters) + + json.Unmarshal(usersRepo, &repos) + allRepos = append(allRepos, repos...) + } + + jsonData, err := json.Marshal(allRepos) + + if err != nil { + return nil, err + } + common.NumberOfPublicRepositories = len(allRepos) + return jsonData, nil +} diff --git a/pkg/models/models.go b/pkg/models/models.go new file mode 100644 index 0000000..f867859 --- /dev/null +++ b/pkg/models/models.go @@ -0,0 +1,50 @@ +package models + +type GitHubUser struct { + Username string `json:"login"` + Url string `json:"url"` + ProfileUrl string `json:"html_url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + ReceivedEventsUrl string `json:"received_events_url"` + Type string `json:"type"` + Admin bool `json:"site_admin"` +} + +type GitHubRepository struct { + FullName string `json:"full_name"` + Private bool `json:"private"` + HtmlURL string `json:"html_url"` + Description string `json:"description"` + Fork bool `json:"fork"` + Created string `json:"created_at"` + Updated string `json:"updated_at"` + Push string `json:"pushed_at"` + CloneURL string `json:"clone_url"` + Visiability string `json:"visibility"` +} + +type GitHubPATLimits struct { + Rate Limits `json:"rate"` +} + +type Limits struct { + Total int `json:"limit"` + Used int `json:"used"` + Remaining int `json:"remaining"` + Reset int `json:"reset"` +} + +type GitHubAPIEndPoints struct { + GetUsers string +} + +type ReportFileNames struct { + GitHubOrgUsers string + GitHubOrgPublicRepos string + GitHubOrgPublicReposNew string +} + +type SlackPayload struct { + Text string `json:"text"` +} diff --git a/pkg/notification/notify.go b/pkg/notification/notify.go new file mode 100644 index 0000000..e7b5765 --- /dev/null +++ b/pkg/notification/notify.go @@ -0,0 +1,38 @@ +package notification + +import ( + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/github" + "github.com/boringtools/git-alerts/pkg/ui" +) + +var ( + slackMessage string +) + +func Notify() { + newRepos, _ := github.GetNewPublicRepositories() + + if len(newRepos) == 0 { + ui.PrintMsg("No new public repositories detected") + } else { + ui.PrintTable(newRepos, "New Public Repositories Detected") + + if common.SlackNotification { + slackMessage = slackMessage + ":eyes: *New Public Repositories Detected* \n" + for _, value := range newRepos { + repo := ":point_right: " + value + "\n" + slackMessage = slackMessage + repo + } + + errNotificaiton := SendSlackNotification(slackMessage) + + if errNotificaiton != nil { + ui.PrintError("error in sending slack notificaiton : %s", errNotificaiton) + } else { + ui.PrintSuccess("slack notificaiton sent successfully!") + } + + } + } +} diff --git a/pkg/notification/slack.go b/pkg/notification/slack.go new file mode 100644 index 0000000..1b84d12 --- /dev/null +++ b/pkg/notification/slack.go @@ -0,0 +1,47 @@ +package notification + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + "github.com/boringtools/git-alerts/pkg/models" +) + +func SendSlackNotification(content string) error { + + payload := models.SlackPayload{Text: content} + byteData, err := json.Marshal(payload) + + if err != nil { + return err + } + + request, errorRequest := http.NewRequest("POST", os.Getenv("SLACK_HOOK"), bytes.NewBuffer(byteData)) + request.Header.Set("Content-Type", "application/json; charset=UTF-8") + + if errorRequest != nil { + return errorRequest + } + + client := &http.Client{} + response, errorResponse := client.Do(request) + + if errorResponse != nil { + return errorResponse + } + + defer response.Body.Close() + + body, _ := io.ReadAll(response.Body) + stringBody := string(body) + + if stringBody == "ok" { + return nil + } else { + return fmt.Errorf("error in sending slack notifcation") + } +} diff --git a/pkg/secrets/get_secrets.go b/pkg/secrets/get_secrets.go new file mode 100644 index 0000000..b2e1939 --- /dev/null +++ b/pkg/secrets/get_secrets.go @@ -0,0 +1,58 @@ +package secrets + +import ( + "encoding/json" + "os" + "strconv" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/boringtools/git-alerts/pkg/models" + "github.com/boringtools/git-alerts/pkg/ui" + "github.com/boringtools/git-alerts/pkg/utils" +) + +var ( + repo []models.GitHubRepository +) + +func RunSecretsScan() { + fileContent, _ := utils.GetJSONFileContent(config.GetReportFilePaths().GitHubOrgPublicRepos) + json.Unmarshal(fileContent, &repo) + + if common.GitleaksScan { + + directoryName := "cloned_repo" + directoryPath := common.ReportPath + directoryName + + _, errDirExists := os.Stat(directoryPath) + + if os.IsNotExist(errDirExists) { + CreateDirectory(directoryPath) + } else { + RemoveDirectory(directoryPath) + CreateDirectory(directoryPath) + } + + for key, value := range repo { + if !value.Fork { + cloneDirectory := directoryPath + "/" + strconv.Itoa(key) + CloneRepo(value.CloneURL, cloneDirectory) + ui.PrintMsg("Scanning repository : %s", value.CloneURL) + RunGitleaks(cloneDirectory) + RemoveDirectory(cloneDirectory) + } else { + ui.PrintWarning("Skipping forked repository : %s", value.CloneURL) + } + } + } else { + for _, value := range repo { + if !value.Fork { + ui.PrintMsg("Scanning repository : %s", value.CloneURL) + RunTruffleHog(value.CloneURL) + } else { + ui.PrintWarning("Skipping forked repository : %s", value.CloneURL) + } + } + } +} diff --git a/secrets/utils.go b/pkg/secrets/utils.go similarity index 60% rename from secrets/utils.go rename to pkg/secrets/utils.go index 2a6278c..9617182 100644 --- a/secrets/utils.go +++ b/pkg/secrets/utils.go @@ -5,47 +5,49 @@ import ( "os" "os/exec" - "github.com/boringtools/git-alerts/logger" "github.com/go-git/go-git/v5" ) -func CreateDirectory(dirPath string) { +func CreateDirectory(dirPath string) error { cmd := exec.Command("mkdir", dirPath) _, err := cmd.Output() if err != nil { - logger.LogERRP("Error in running the command : ", err.Error()) + return err } + return nil } -func RemoveDirectory(dirPath string) { +func RemoveDirectory(dirPath string) error { cmd := exec.Command("rm", "-rf", dirPath) _, err := cmd.Output() if err != nil { - logger.LogERRP("Error in running the command : ", err.Error()) + return err } + + return nil } -func CloneRepo(url, dir string) { +func CloneRepo(url, dir string) error { _, errClone := git.PlainClone(dir, false, &git.CloneOptions{ URL: url, Progress: nil, }) if errClone != nil { - logger.LogERRP("Error in cloning repository : ", errClone) + return errClone } + return nil } -func RunTruffleHog(repoURL string) { +func RunTruffleHog(repoURL string) error { var tf *exec.Cmd _, checkTf := exec.LookPath("trufflehog") if checkTf != nil { - logger.LogERR("Trufflehog is not installed in your machine") - os.Exit(1) + return fmt.Errorf("trufflehog is not installed in your machine") } if os.Getenv("trufflehog") == "true" { @@ -57,20 +59,21 @@ func RunTruffleHog(repoURL string) { op, errTf := tf.Output() if errTf != nil { - logger.LogERRP("Error in running the command", errTf.Error()) + return errTf } else { if string(op) != "" { fmt.Println(string(op)) } } + + return nil } -func RunGitleaks(repoPath string) { +func RunGitleaks(repoPath string) error { _, checkTf := exec.LookPath("gitleaks") if checkTf != nil { - logger.LogERR("Gitleaks is not installed in your machine") - os.Exit(1) + return fmt.Errorf("gitleaks is not installed in your machine") } gl := exec.Command("gitleaks", "git", repoPath, "-v") @@ -78,10 +81,11 @@ func RunGitleaks(repoPath string) { if exitError, ok := errGl.(*exec.ExitError); ok && exitError.ExitCode() == 1 { fmt.Println(string(op)) - return } if errGl != nil { - logger.LogERRP("Error in running the command : ", errGl) + return errGl } + + return nil } diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go new file mode 100644 index 0000000..f7860ed --- /dev/null +++ b/pkg/ui/ui.go @@ -0,0 +1,47 @@ +package ui + +import ( + "fmt" + "os" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" +) + +func PrintBanner(s string) { + fmt.Fprint(os.Stderr, s, "\n") +} + +func PrintSuccess(s string, args ...any) { + msg := fmt.Sprintf(s, args...) + fmt.Fprint(os.Stderr, text.FgGreen.Sprint(msg), "\n") +} + +func PrintMsg(s string, args ...any) { + msg := fmt.Sprintf(s, args...) + fmt.Fprint(os.Stderr, text.Bold.Sprint(msg), "\n") +} + +func PrintWarning(s string, args ...any) { + msg := fmt.Sprintf(s, args...) + fmt.Fprint(os.Stdout, text.FgYellow.Sprint(msg), "\n") +} + +func PrintError(s string, args ...any) { + msg := fmt.Sprintf(s, args...) + fmt.Fprint(os.Stderr, text.FgRed.Sprint(msg), "\n") +} + +func PrintTable(data []string, header string) { + tbl := table.NewWriter() + tbl.SetOutputMirror(os.Stdout) + tbl.AppendHeader(table.Row{header}) + + for _, value := range data { + tbl.AppendRows([]table.Row{ + {value}, + }) + } + tbl.AppendSeparator() + tbl.Render() +} diff --git a/pkg/utils/read_data.go b/pkg/utils/read_data.go new file mode 100644 index 0000000..1585ff1 --- /dev/null +++ b/pkg/utils/read_data.go @@ -0,0 +1,24 @@ +package utils + +import ( + "io" + "os" +) + +func GetJSONFileContent(filePath string) ([]byte, error) { + openFile, errOpenFile := os.Open(filePath) + + if errOpenFile != nil { + return nil, errOpenFile + } + + defer openFile.Close() + + byteData, errRead := io.ReadAll(openFile) + + if errRead != nil { + return nil, errRead + } + + return byteData, nil +} diff --git a/pkg/utils/save_data.go b/pkg/utils/save_data.go new file mode 100644 index 0000000..2bb2a69 --- /dev/null +++ b/pkg/utils/save_data.go @@ -0,0 +1,16 @@ +package utils + +import ( + "os" +) + +func SaveToJson(content []byte, filePath string) error { + + errWriteFile := os.WriteFile(filePath, content, 0644) + + if errWriteFile != nil { + return errWriteFile + } + + return nil +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..a70b121 --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,48 @@ +package utils + +import ( + "os" + "time" + + "github.com/boringtools/git-alerts/pkg/common" + "github.com/boringtools/git-alerts/pkg/config" + "github.com/jedib0t/go-pretty/v6/table" +) + +func GetCurrentTime() (formattedTime string) { + currentTime := time.Now() + formattedTime = currentTime.Format("Mon Jan 2 15:04:05 MST 2006") + return formattedTime +} + +func CheckAuthenticatedScan() { + _, isGitHubPATExists := os.LookupEnv("GITHUB_PAT") + + if isGitHubPATExists { + common.AuthenticatedScan = true + } else { + common.AuthenticatedScan = false + } +} + +func IsPreviousScanFileExists() bool { + _, errScanFile := os.Stat(config.GetReportFilePaths().GitHubOrgPublicRepos) + + if errScanFile != nil { + return false + } + + return true +} + +func PrintSummery() { + tbl := table.NewWriter() + tbl.SetOutputMirror(os.Stdout) + tbl.AppendHeader(table.Row{"Scan Summery", "Data"}) + tbl.AppendRows([]table.Row{ + {"Total Users", common.NumberOfGitHubUsers}, + {"Total Users Repositories", common.NumberOfPublicRepositories}, + }) + tbl.AppendSeparator() + tbl.Render() +} diff --git a/reporter/notify.go b/reporter/notify.go deleted file mode 100644 index c1d488b..0000000 --- a/reporter/notify.go +++ /dev/null @@ -1,45 +0,0 @@ -package reporter - -import ( - "os" - - "github.com/boringtools/git-alerts/gh" - "github.com/boringtools/git-alerts/logger" - "github.com/boringtools/git-alerts/notification" - "github.com/jedib0t/go-pretty/v6/table" -) - -var ( - slackMessage string -) - -func Notify() { - newRepos := gh.GetNewRepos() - - if len(newRepos) == 0 { - logger.Log("No new public repositories detected") - } else { - - tbl := table.NewWriter() - tbl.SetOutputMirror(os.Stdout) - tbl.AppendHeader(table.Row{"New Public Repositories Detected"}) - - for _, value := range newRepos { - tbl.AppendRows([]table.Row{ - {value}, - }) - } - tbl.AppendSeparator() - tbl.Render() - - if os.Getenv("slack") == "true" { - slackMessage = slackMessage + ":eyes: *New Public Repositories Detected* \n" - for _, value := range newRepos { - repo := ":point_right: " + value + "\n" - slackMessage = slackMessage + repo - } - - notification.SlackNotification(slackMessage) - } - } -} diff --git a/secrets/get_secrets.go b/secrets/get_secrets.go deleted file mode 100644 index ed3c05a..0000000 --- a/secrets/get_secrets.go +++ /dev/null @@ -1,58 +0,0 @@ -package secrets - -import ( - "encoding/json" - "os" - "strconv" - - "github.com/boringtools/git-alerts/common" - "github.com/boringtools/git-alerts/config" - "github.com/boringtools/git-alerts/logger" -) - -type Repos struct { - Name string `json:"full_name"` - CloneUrl string `json:"clone_url"` - Fork bool `json:"fork"` -} - -var ( - repo []Repos -) - -func GetSecrets() { - fileContent := common.GetJsonFileContent(config.GhFileNames()[1]) - json.Unmarshal(fileContent, &repo) - - if os.Getenv("gitleaks") == "true" { - - directoryName := "cloned_repo" - directoryPath := os.Getenv("rfp") + directoryName - - _, errDirExists := os.Stat(directoryPath) - - if errDirExists != nil { - CreateDirectory(directoryPath) - } else { - RemoveDirectory(directoryPath) - CreateDirectory(directoryPath) - } - - for key, value := range repo { - if !value.Fork { - cloneDirectory := directoryPath + "/" + strconv.Itoa(key) - CloneRepo(value.CloneUrl, cloneDirectory) - logger.LogP("Scanning repository : ", value.CloneUrl) - RunGitleaks(cloneDirectory) - RemoveDirectory(cloneDirectory) - } - } - } else { - for _, value := range repo { - if !value.Fork { - logger.LogP("Scanning repository : ", value.CloneUrl) - RunTruffleHog(value.CloneUrl) - } - } - } -}