Skip to content

Commit b3ede0c

Browse files
committed
feat(github): add graphql client
1 parent 6d3ba1f commit b3ede0c

File tree

8 files changed

+142
-41
lines changed

8 files changed

+142
-41
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ require (
275275
github.com/sendgrid/rest v2.6.9+incompatible // indirect
276276
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
277277
github.com/shoenig/go-m1cpu v0.1.6 // indirect
278+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 // indirect
279+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
278280
github.com/sirupsen/logrus v1.9.3 // indirect
279281
github.com/skeema/knownhosts v1.3.0 // indirect
280282
github.com/sorairolake/lzip-go v0.3.5 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
677677
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
678678
github.com/shuheiktgw/go-travis v0.3.1 h1:SAT16mi77ccqogOslnXxBXzXbpeyChaIYUwi2aJpVZY=
679679
github.com/shuheiktgw/go-travis v0.3.1/go.mod h1:avnFFDqJDdRHwlF9tgqvYi3asQCm/HGL8aLxYiKa4Yg=
680+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
681+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
682+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
683+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
680684
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
681685
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
682686
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=

pkg/sources/github/connector.go

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,88 @@ package github
22

33
import (
44
"fmt"
5+
"net/http"
6+
"net/url"
7+
"strings"
58

69
gogit "github.com/go-git/go-git/v5"
710
"github.com/google/go-github/v67/github"
8-
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
11+
"github.com/shurcooL/githubv4"
912

1013
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
14+
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
1115
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
1216
)
1317

14-
const cloudEndpoint = "https://api.github.com"
18+
const (
19+
cloudV3Endpoint = "https://api.github.com"
20+
cloudGraphqlEndpoint = "https://api.github.com/graphql" // https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#the-graphql-endpoint
21+
)
1522

1623
type connector interface {
1724
// APIClient returns a configured GitHub client that can be used for GitHub API operations.
1825
APIClient() *github.Client
26+
// GraphQLClient returns a client that can be used for GraphQL operations.
27+
GraphQLClient() *githubv4.Client
1928
// Clone clones a repository using the configured authentication information.
2029
Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error)
2130
}
2231

23-
func newConnector(source *Source) (connector, error) {
32+
func newConnector(ctx context.Context, source *Source) (connector, error) {
33+
// Construct the URLs.
2434
apiEndpoint := source.conn.Endpoint
2535
if apiEndpoint == "" || endsWithGithub.MatchString(apiEndpoint) {
26-
apiEndpoint = cloudEndpoint
36+
apiEndpoint = cloudV3Endpoint
2737
}
2838

2939
switch cred := source.conn.GetCredential().(type) {
3040
case *sourcespb.GitHub_GithubApp:
3141
log.RedactGlobally(cred.GithubApp.GetPrivateKey())
32-
return newAppConnector(apiEndpoint, cred.GithubApp)
42+
return newAppConnector(ctx, apiEndpoint, cred.GithubApp)
3343
case *sourcespb.GitHub_BasicAuth:
3444
log.RedactGlobally(cred.BasicAuth.GetPassword())
35-
return newBasicAuthConnector(apiEndpoint, cred.BasicAuth)
45+
return newBasicAuthConnector(ctx, apiEndpoint, cred.BasicAuth)
3646
case *sourcespb.GitHub_Token:
3747
log.RedactGlobally(cred.Token)
38-
return newTokenConnector(apiEndpoint, cred.Token, source.handleRateLimit)
48+
return newTokenConnector(ctx, apiEndpoint, cred.Token, source.handleRateLimit)
3949
case *sourcespb.GitHub_Unauthenticated:
40-
return newUnauthenticatedConnector(apiEndpoint)
50+
return newUnauthenticatedConnector(ctx, apiEndpoint)
4151
default:
4252
return nil, fmt.Errorf("unknown connection type")
4353
}
4454
}
55+
56+
func createAPIClient(ctx context.Context, httpClient *http.Client, apiEndpoint string) (*github.Client, error) {
57+
getLogger(ctx).V(2).Info("Creating API client", "url", apiEndpoint)
58+
59+
// If we're using public GitHub, make a regular client.
60+
// Otherwise, make an enterprise client.
61+
if strings.EqualFold(apiEndpoint, cloudV3Endpoint) {
62+
return github.NewClient(httpClient), nil
63+
}
64+
65+
return github.NewClient(httpClient).WithEnterpriseURLs(apiEndpoint, apiEndpoint)
66+
}
67+
68+
func createGraphqlClient(ctx context.Context, client *http.Client, apiEndpoint string) (*githubv4.Client, error) {
69+
var graphqlEndpoint string
70+
if apiEndpoint == cloudV3Endpoint {
71+
graphqlEndpoint = cloudGraphqlEndpoint
72+
} else {
73+
// Use the root endpoint for the host.
74+
// https://docs.github.com/en/[email protected]/graphql/guides/introduction-to-graphql
75+
parsedURL, err := url.Parse(apiEndpoint)
76+
if err != nil {
77+
return nil, fmt.Errorf("could not create GraphQL client: %w", err)
78+
}
79+
80+
// GitHub Enterprise uses `/api/v3` for the base. (https://github.com/google/go-github/issues/958)
81+
// Swap it, and anything before `/api`, with GraphQL.
82+
before, _ := strings.CutSuffix(parsedURL.Path, "/api/v3")
83+
parsedURL.Path = before + "/api/graphql"
84+
graphqlEndpoint = parsedURL.String()
85+
}
86+
getLogger(ctx).V(2).Info("Creating GraphQL client", "url", graphqlEndpoint)
87+
88+
return githubv4.NewEnterpriseClient(graphqlEndpoint, client), nil
89+
}

pkg/sources/github/connector_app.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"github.com/bradleyfalzon/ghinstallation/v2"
88
gogit "github.com/go-git/go-git/v5"
99
"github.com/google/go-github/v67/github"
10+
"github.com/shurcooL/githubv4"
11+
1012
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1113
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
1214
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
@@ -15,13 +17,14 @@ import (
1517

1618
type appConnector struct {
1719
apiClient *github.Client
20+
graphqlClient *githubv4.Client
1821
installationClient *github.Client
1922
installationID int64
2023
}
2124

2225
var _ connector = (*appConnector)(nil)
2326

24-
func newAppConnector(apiEndpoint string, app *credentialspb.GitHubApp) (*appConnector, error) {
27+
func newAppConnector(ctx context.Context, apiEndpoint string, app *credentialspb.GitHubApp) (*appConnector, error) {
2528
installationID, err := strconv.ParseInt(app.InstallationId, 10, 64)
2629
if err != nil {
2730
return nil, fmt.Errorf("could not parse app installation ID %q: %w", app.InstallationId, err)
@@ -67,8 +70,14 @@ func newAppConnector(apiEndpoint string, app *credentialspb.GitHubApp) (*appConn
6770
return nil, fmt.Errorf("could not create API client: %w", err)
6871
}
6972

73+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
74+
if err != nil {
75+
return nil, err
76+
}
77+
7078
return &appConnector{
7179
apiClient: apiClient,
80+
graphqlClient: graphqlClient,
7281
installationClient: installationClient,
7382
installationID: installationID,
7483
}, nil
@@ -78,6 +87,10 @@ func (c *appConnector) APIClient() *github.Client {
7887
return c.apiClient
7988
}
8089

90+
func (c *appConnector) GraphQLClient() *githubv4.Client {
91+
return c.graphqlClient
92+
}
93+
8194
func (c *appConnector) Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error) {
8295
// TODO: Check rate limit for this call.
8396
token, _, err := c.installationClient.Apps.CreateInstallationToken(

pkg/sources/github/connector_basicauth.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,57 @@ import (
55

66
gogit "github.com/go-git/go-git/v5"
77
"github.com/google/go-github/v67/github"
8+
"github.com/shurcooL/githubv4"
9+
810
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
911
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
1012
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
1113
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
1214
)
1315

1416
type basicAuthConnector struct {
15-
apiClient *github.Client
16-
username string
17-
password string
17+
apiClient *github.Client
18+
graphqlClient *githubv4.Client
19+
username string
20+
password string
1821
}
1922

2023
var _ connector = (*basicAuthConnector)(nil)
2124

22-
func newBasicAuthConnector(apiEndpoint string, cred *credentialspb.BasicAuth) (*basicAuthConnector, error) {
25+
func newBasicAuthConnector(ctx context.Context, apiEndpoint string, cred *credentialspb.BasicAuth) (*basicAuthConnector, error) {
2326
const httpTimeoutSeconds = 60
2427
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
2528
httpClient.Transport = &github.BasicAuthTransport{
2629
Username: cred.Username,
2730
Password: cred.Password,
2831
}
2932

30-
apiClient, err := createGitHubClient(httpClient, apiEndpoint)
33+
apiClient, err := createAPIClient(ctx, httpClient, apiEndpoint)
3134
if err != nil {
3235
return nil, fmt.Errorf("could not create API client: %w", err)
3336
}
3437

38+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
39+
if err != nil {
40+
return nil, err
41+
}
42+
3543
return &basicAuthConnector{
36-
apiClient: apiClient,
37-
username: cred.Username,
38-
password: cred.Password,
44+
apiClient: apiClient,
45+
graphqlClient: graphqlClient,
46+
username: cred.Username,
47+
password: cred.Password,
3948
}, nil
4049
}
4150

4251
func (c *basicAuthConnector) APIClient() *github.Client {
4352
return c.apiClient
4453
}
4554

55+
func (c *basicAuthConnector) GraphQLClient() *githubv4.Client {
56+
return c.graphqlClient
57+
}
58+
4659
func (c *basicAuthConnector) Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error) {
4760
return git.CloneRepoUsingToken(ctx, c.password, repoURL, c.username)
4861
}

pkg/sources/github/connector_token.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,29 @@ import (
77

88
gogit "github.com/go-git/go-git/v5"
99
"github.com/google/go-github/v67/github"
10+
"github.com/shurcooL/githubv4"
11+
"golang.org/x/oauth2"
12+
1013
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1114
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
1215
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
13-
"golang.org/x/oauth2"
1416
)
1517

1618
type tokenConnector struct {
17-
apiClient *github.Client
18-
token string
19+
token string
20+
apiClient *github.Client
21+
graphqlClient *githubv4.Client
22+
1923
isGitHubEnterprise bool
20-
handleRateLimit func(context.Context, error) bool
21-
user string
22-
userMu sync.Mutex
24+
25+
handleRateLimit func(context.Context, error) bool
26+
user string
27+
userMu sync.Mutex
2328
}
2429

2530
var _ connector = (*tokenConnector)(nil)
2631

27-
func newTokenConnector(apiEndpoint string, token string, handleRateLimit func(context.Context, error) bool) (*tokenConnector, error) {
32+
func newTokenConnector(ctx context.Context, apiEndpoint string, token string, handleRateLimit func(context.Context, error) bool) (*tokenConnector, error) {
2833
const httpTimeoutSeconds = 60
2934
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
3035
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
@@ -33,15 +38,21 @@ func newTokenConnector(apiEndpoint string, token string, handleRateLimit func(co
3338
Source: tokenSource,
3439
}
3540

36-
apiClient, err := createGitHubClient(httpClient, apiEndpoint)
41+
apiClient, err := createAPIClient(ctx, httpClient, apiEndpoint)
3742
if err != nil {
3843
return nil, fmt.Errorf("could not create API client: %w", err)
3944
}
4045

46+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
47+
if err != nil {
48+
return nil, err
49+
}
50+
4151
return &tokenConnector{
4252
apiClient: apiClient,
53+
graphqlClient: graphqlClient,
4354
token: token,
44-
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudEndpoint),
55+
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudV3Endpoint),
4556
handleRateLimit: handleRateLimit,
4657
}, nil
4758
}
@@ -50,6 +61,10 @@ func (c *tokenConnector) APIClient() *github.Client {
5061
return c.apiClient
5162
}
5263

64+
func (c *tokenConnector) GraphQLClient() *githubv4.Client {
65+
return c.graphqlClient
66+
}
67+
5368
func (c *tokenConnector) Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error) {
5469
if err := c.setUserIfUnset(ctx); err != nil {
5570
return "", nil, err

pkg/sources/github/connector_unauthenticated.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,47 @@ import (
55

66
gogit "github.com/go-git/go-git/v5"
77
"github.com/google/go-github/v67/github"
8+
"github.com/shurcooL/githubv4"
89

910
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1011
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
1112
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
1213
)
1314

1415
type unauthenticatedConnector struct {
15-
apiClient *github.Client
16+
apiClient *github.Client
17+
graphqlClient *githubv4.Client
1618
}
1719

1820
var _ connector = (*unauthenticatedConnector)(nil)
1921

20-
func newUnauthenticatedConnector(apiEndpoint string) (*unauthenticatedConnector, error) {
22+
func newUnauthenticatedConnector(ctx context.Context, apiEndpoint string) (*unauthenticatedConnector, error) {
2123
const httpTimeoutSeconds = 60
2224
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
23-
apiClient, err := createGitHubClient(httpClient, apiEndpoint)
25+
apiClient, err := createAPIClient(ctx, httpClient, apiEndpoint)
2426
if err != nil {
2527
return nil, fmt.Errorf("could not create API client: %w", err)
2628
}
29+
30+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
31+
if err != nil {
32+
return nil, err
33+
}
34+
2735
return &unauthenticatedConnector{
28-
apiClient: apiClient,
36+
apiClient: apiClient,
37+
graphqlClient: graphqlClient,
2938
}, nil
3039
}
3140

3241
func (c *unauthenticatedConnector) APIClient() *github.Client {
3342
return c.apiClient
3443
}
3544

45+
func (c *unauthenticatedConnector) GraphQLClient() *githubv4.Client {
46+
return c.graphqlClient
47+
}
48+
3649
func (c *unauthenticatedConnector) Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error) {
3750
return git.CloneRepoUsingUnauthenticated(ctx, repoURL)
3851
}

0 commit comments

Comments
 (0)