go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/github/github.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package github 5 6 import ( 7 "context" 8 "errors" 9 "strings" 10 11 "github.com/google/go-github/v49/github" 12 "github.com/rs/zerolog/log" 13 "go.mondoo.com/cnquery/motor/asset" 14 "go.mondoo.com/cnquery/motor/discovery/common" 15 "go.mondoo.com/cnquery/motor/providers" 16 github_provider "go.mondoo.com/cnquery/motor/providers/github" 17 "go.mondoo.com/cnquery/motor/providers/resolver" 18 "go.mondoo.com/cnquery/motor/vault" 19 ) 20 21 const ( 22 DiscoveryRepository = "repository" 23 DiscoveryUser = "user" 24 DiscoveryOrganization = "organization" 25 ) 26 27 type Resolver struct{} 28 29 func (r *Resolver) Name() string { 30 return "GitHub Resolver" 31 } 32 33 func (r *Resolver) AvailableDiscoveryTargets() []string { 34 return []string{common.DiscoveryAuto, common.DiscoveryAll, DiscoveryRepository, DiscoveryUser} 35 } 36 37 func (r *Resolver) Resolve(ctx context.Context, root *asset.Asset, pCfg *providers.Config, credsResolver vault.Resolver, sfn common.QuerySecretFn, userIdDetectors ...providers.PlatformIdDetector) ([]*asset.Asset, error) { 38 // establish connection to GitHub 39 m, err := resolver.NewMotorConnection(ctx, pCfg, credsResolver) 40 if err != nil { 41 return nil, err 42 } 43 defer m.Close() 44 45 p, ok := m.Provider.(*github_provider.Provider) 46 if !ok { 47 return nil, errors.New("could not initialize github provider") 48 } 49 50 identifier, err := p.Identifier() 51 if err != nil { 52 return nil, err 53 } 54 55 pf, err := m.Platform() 56 if err != nil { 57 return nil, err 58 } 59 60 defaultName := root.Name 61 list := []*asset.Asset{} 62 63 switch pf.Name { 64 case "github-repo": 65 if pCfg.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryRepository) { 66 name := defaultName 67 if name == "" { 68 repo, _ := p.Repository() 69 if repo != nil && repo.GetOwner() != nil { 70 name = repo.GetOwner().GetLogin() + "/" + repo.GetName() 71 } 72 } 73 74 list = append(list, &asset.Asset{ 75 PlatformIds: []string{identifier}, 76 Name: name, 77 Platform: pf, 78 Connections: []*providers.Config{pCfg}, // pass-in the current config 79 State: asset.State_STATE_ONLINE, 80 }) 81 } 82 case "github-user": 83 if pCfg.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryUser) { 84 name := defaultName 85 if name == "" { 86 user, _ := p.User() 87 if user != nil { 88 name = user.GetName() 89 } 90 } 91 92 list = append(list, &asset.Asset{ 93 PlatformIds: []string{identifier}, 94 Name: name, 95 Platform: pf, 96 Connections: []*providers.Config{pCfg}, // pass-in the current config 97 State: asset.State_STATE_ONLINE, 98 }) 99 } 100 case "github-org": 101 if pCfg.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryOrganization) { 102 name := defaultName 103 if name == "" { 104 org, _ := p.Organization() 105 if org != nil { 106 name = org.GetName() 107 } 108 } 109 list = append(list, &asset.Asset{ 110 PlatformIds: []string{identifier}, 111 Name: name, 112 Platform: pf, 113 Connections: []*providers.Config{pCfg}, // pass-in the current config 114 State: asset.State_STATE_ONLINE, 115 }) 116 } 117 118 if pCfg.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryRepository) { 119 log.Debug().Msg("Discovering repositories for organization") 120 org, err := p.Organization() 121 if err != nil { 122 return nil, err 123 } 124 125 listOpts := &github.RepositoryListOptions{ 126 ListOptions: github.ListOptions{PerPage: 100}, 127 Type: "all", 128 } 129 allRepos := []*github.Repository{} 130 for { 131 repos, resp, err := p.Client().Repositories.List(context.Background(), org.GetLogin(), listOpts) 132 if err != nil { 133 if strings.Contains(err.Error(), "404") { 134 return nil, nil 135 } 136 return nil, err 137 } 138 allRepos = append(allRepos, repos...) 139 if resp.NextPage == 0 { 140 break 141 } 142 listOpts.Page = resp.NextPage 143 } 144 145 for _, repo := range allRepos { 146 clonedConfig := pCfg.Clone() 147 if clonedConfig.Options == nil { 148 clonedConfig.Options = map[string]string{} 149 } 150 151 owner := repo.GetOwner().GetLogin() 152 repoName := repo.GetName() 153 clonedConfig.Options["owner"] = owner 154 clonedConfig.Options["repository"] = repoName 155 delete(clonedConfig.Options, "organization") 156 delete(clonedConfig.Options, "user") 157 158 list = append(list, &asset.Asset{ 159 PlatformIds: []string{github_provider.NewGitHubRepoIdentifier(owner, repoName)}, 160 Name: owner + "/" + repoName, 161 Platform: github_provider.GithubRepoPlatform, 162 Connections: []*providers.Config{clonedConfig}, // pass-in the current config 163 State: asset.State_STATE_ONLINE, 164 }) 165 } 166 } 167 } 168 169 return list, nil 170 }