Skip to content

Commit

Permalink
Merge pull request #45 from crashappsec/nettrino/nilfixes
Browse files Browse the repository at this point in the history
Handle null responses in case of TPS throttling
  • Loading branch information
nettrino authored Oct 21, 2022
2 parents ec552af + 8e4f423 commit d1ab083
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 37 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ You can see available options via the `--help` flag.
$GOPATH/bin/github-analyzer \
--organization crashappsec \
--token "$GH_SECURITY_AUDITOR_TOKEN" \
--enableStats
```

### Running using Docker
Expand All @@ -121,7 +120,6 @@ You can see available options via the `--help` flag.
--organization crashappsec \
--output output \
--token "$GH_SECURITY_AUDITOR_TOKEN" \
--enableStats
```

## Permissions
Expand All @@ -147,7 +145,7 @@ needed. Example usage:
github-analyzer \
--organization crashappsec \
--token "$GH_SECURITY_AUDITOR_TOKEN" \
--enableStats \
--userPermissionStats \
--enableScraping \
--username "$GH_SECURITY_AUDITOR_USERNAME" \
--password "$GH_SECURITY_AUDITOR_PASSWORD" \
Expand Down
7 changes: 3 additions & 4 deletions cmd/github-analyzer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ func runCmd() {
config.ViperEnv.Username,
config.ViperEnv.Password,
config.ViperEnv.OtpSeed,
config.ViperEnv.Organization,
config.ViperEnv.EnableStats)
config.ViperEnv.Organization)
if err != nil {
log.Logger.Error(err)
}
Expand All @@ -68,7 +67,7 @@ func runCmd() {
log.Logger.Error(err)
return
}
results, execStatus, err := auditor.AuditOrg(config.ViperEnv.Organization, config.ViperEnv.EnableStats)
results, execStatus, err := auditor.AuditOrg(config.ViperEnv.Organization, config.ViperEnv.UserPermissionStats)
if err != nil {
log.Logger.Error(err)
}
Expand Down Expand Up @@ -172,7 +171,7 @@ func NewRootCommand() *cobra.Command {
rootCmd.Flags().
BoolVarP(&config.ViperEnv.Version, "version", "", false, "print version and exit")
rootCmd.Flags().
BoolVarP(&config.ViperEnv.EnableStats, "enableStats", "", false, "enable user permission statistics (might be slow due to throttling limits)")
BoolVarP(&config.ViperEnv.UserPermissionStats, "userPermissionStats", "", false, "enable user permission statistics (might be slow in large orgs due to throttling limits)")

rootCmd.Flags().
BoolVarP(&config.ViperEnv.EnableScraping, "enableScraping", "", false, "enable experimental checks that rely on screen scraping")
Expand Down
24 changes: 12 additions & 12 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ const (
)

type ViperEnvVars struct {
CfgFile string `mapstructure:"CFG_FILE"`
EnableScraping bool `mapstructure:"ENABLE_SCRAPING"`
EnableStats bool `mapstructure:"ENABLE_STATS"`
Version bool `mapstructure:"VERSION"`
Organization string `mapstructure:"ORGANIZATION"`
OtpSeed string `mapstructure:"OTP_SEED"`
OutputDir string `mapstructure:"OUTPUT_DIR"`
Password string `mapstructure:"PASSWORD"`
Port int `mapstructure:"PORT"`
ScmURL string `mapstructure:"SCM_URL"`
Token string `mapstructure:"TOKEN"`
Username string `mapstructure:"USERNAME"`
CfgFile string `mapstructure:"CFG_FILE"`
EnableScraping bool `mapstructure:"ENABLE_SCRAPING"`
UserPermissionStats bool `mapstructure:"USER_PERMISSION_STATS"`
Version bool `mapstructure:"VERSION"`
Organization string `mapstructure:"ORGANIZATION"`
OtpSeed string `mapstructure:"OTP_SEED"`
OutputDir string `mapstructure:"OUTPUT_DIR"`
Password string `mapstructure:"PASSWORD"`
Port int `mapstructure:"PORT"`
ScmURL string `mapstructure:"SCM_URL"`
Token string `mapstructure:"TOKEN"`
Username string `mapstructure:"USERNAME"`
}

var ViperEnv ViperEnvVars
8 changes: 4 additions & 4 deletions pkg/github/auditor/auditor.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ func NewGithubAuditor(token string) (*GithubAuditor, error) {
// or yielded in an error), and a generic overall error if audit fails overall
func (gs GithubAuditor) AuditOrg(
name string,
enableStats bool,
enableUserPermissionStats bool,
) ([]issue.Issue, map[issue.IssueID]error, error) {
ctx := context.Background()
back := &backoff.Backoff{
Min: 10 * time.Second,
Max: 1 * time.Hour,
Min: 30 * time.Second,
Max: 30 * time.Minute,
Jitter: true,
}
org, err := org.NewOrganization(ctx, gs.client, back, name)
Expand All @@ -63,5 +63,5 @@ func (gs GithubAuditor) AuditOrg(
return nil, nil, err
}

return org.Audit(ctx, enableStats)
return org.Audit(ctx, enableUserPermissionStats)
}
6 changes: 4 additions & 2 deletions pkg/github/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,8 @@ func (org *Organization) AuditMemberPermissions(
var issues []issue.Issue
execStatus := make(map[issue.IssueID]error, 1)

log.Logger.Infof("[slow] Fetching user permissions for %s", *org.info.Login)

// userRepoPermissions holds a list of all repos with a given permission for a given user
type userRepoPermissions map[string]([]types.RepoName)
// permission summary is the list of different permissions for a user
Expand Down Expand Up @@ -620,7 +622,7 @@ func (org *Organization) AuditMemberPermissions(

func (org *Organization) Audit(
ctx context.Context,
enableStats bool,
enableUserPermissionStats bool,
) ([]issue.Issue, map[issue.IssueID]error, error) {
var allIssues []issue.Issue
execStatus := map[issue.IssueID]error{}
Expand All @@ -634,7 +636,7 @@ func (org *Organization) Audit(
org.GetInstalls(ctx)
org.GetActionRunners(ctx)

if enableStats {
if enableUserPermissionStats {
auditHooks = append(auditHooks, org.AuditMemberPermissions)
}
for _, hook := range auditHooks {
Expand Down
1 change: 1 addition & 0 deletions pkg/github/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func (repo *Repository) GetCollaborators(ctx context.Context) (
log.Logger.Error(err)
}

repo.backoff.Reset()
collaborators := make(map[types.UserLogin]types.User, len(users))
for _, u := range users {
collaborators[types.UserLogin(*u.Login)] = u
Expand Down
39 changes: 33 additions & 6 deletions pkg/github/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,51 @@ func WorkflowsAggregator(workflows *github.Workflows) []types.Workflow {

func GetPaginatedResult[T any, K any](
ctx context.Context,
backoff *backoff.Backoff,
globalBackoff *backoff.Backoff,
callOpts *github.ListOptions,
githubCall func(opts *github.ListOptions) (K, *github.Response, error),
aggregator func(K) []T,
) ([]T, error) {

var results []T
retries := 0

var back *backoff.Backoff
if globalBackoff != nil {
back = &backoff.Backoff{
Min: globalBackoff.Min,
Max: globalBackoff.Max,
Jitter: globalBackoff.Jitter,
}
} else {
back = &backoff.Backoff{
Min: 30 * time.Second,
Max: 30 * time.Minute,
Jitter: true,
}
}

for {
raw, resp, err := githubCall(callOpts)

if _, ok := err.(*github.RateLimitError); ok {
d := backoff.Duration()
log.Logger.Infoln("Hit rate limit, sleeping for %d", d)
_, ok := err.(*github.RateLimitError)
if ok || resp == nil {
d := back.Duration()
log.Logger.Infof("Hit rate limit, sleeping for %v", d)
time.Sleep(d)
if resp == nil {
retries += 1
if retries > 10 {
return results, fmt.Errorf(
"Aborting after 5 failed retries",
)
}
}
continue
}

if err != nil {
retries = 0
if err != nil && resp != nil {
if resp.StatusCode == 403 {
log.Logger.Debugf(
"It appears the token being used doesn't have access to call %v",
Expand All @@ -123,7 +150,7 @@ func GetPaginatedResult[T any, K any](
return results, err
}

backoff.Reset()
back.Reset()
for _, res := range aggregator(raw) {
results = append(results, res)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/output/html/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ func Serve(
"Server with HTML summary starting at 0.0.0.0:%d\n",
port,
)
fmt.Println(
"\n\n\t Analysis complete! Visit localhost:3000 using your browser to see results",
)
err = http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
if err != nil {
log.Logger.Error(err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/output/html/templates/index.html.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
</div>
</div>
<div>
The following checks had errors - does token you used have the appropriate permissions?
The following checks had errors - does the token you used have the appropriate permissions?
<ul>
{{range $issue := $failed}}
<li>
Expand Down
6 changes: 1 addition & 5 deletions pkg/scraping/scraping.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func AuditScraping(
username, password, otpseed, org string, enableStats bool,
username, password, otpseed, org string,
) ([]issue.Issue, map[issue.IssueID]error, error) {
var issues []issue.Issue
execStatus := make(map[issue.IssueID]error, 1)
Expand All @@ -33,10 +33,6 @@ func AuditScraping(
issues = append(issues, issue.ApplicationRestrictionsDisabled(org))
}

if !enableStats {
return issues, execStatus, nil
}

apps, err := client.ListOAuthApps(org)
execStatus[issue.STATS_OAUTH_PERMS] = err
if err != nil {
Expand Down

0 comments on commit d1ab083

Please sign in to comment.