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  }