Skip to content

Commit 64467bb

Browse files
committed
feat(github): add graphql client
1 parent ef3a714 commit 64467bb

File tree

8 files changed

+158
-55
lines changed

8 files changed

+158
-55
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ require (
283283
github.com/sendgrid/rest v2.6.9+incompatible // indirect
284284
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
285285
github.com/shoenig/go-m1cpu v0.1.6 // indirect
286+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 // indirect
287+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
286288
github.com/sirupsen/logrus v1.9.3 // indirect
287289
github.com/skeema/knownhosts v1.3.0 // indirect
288290
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
@@ -701,6 +701,10 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
701701
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
702702
github.com/shuheiktgw/go-travis v0.3.1 h1:SAT16mi77ccqogOslnXxBXzXbpeyChaIYUwi2aJpVZY=
703703
github.com/shuheiktgw/go-travis v0.3.1/go.mod h1:avnFFDqJDdRHwlF9tgqvYi3asQCm/HGL8aLxYiKa4Yg=
704+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
705+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
706+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
707+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
704708
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
705709
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
706710
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=

pkg/sources/github/connector.go

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,91 @@
11
package github
22

33
import (
4+
"fmt"
5+
"net/http"
6+
"net/url"
7+
"strings"
8+
49
gogit "github.com/go-git/go-git/v5"
510
"github.com/google/go-github/v67/github"
11+
"github.com/shurcooL/githubv4"
12+
613
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
14+
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
15+
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
716
)
817

9-
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+
)
1022

1123
// Connector abstracts over the authenticated ways to interact with GitHub: cloning and API operations.
1224
type Connector interface {
1325
// APIClient returns a configured GitHub client that can be used for GitHub API operations.
1426
APIClient() *github.Client
27+
// GraphQLClient returns a client that can be used for GraphQL operations.
28+
GraphQLClient() *githubv4.Client
1529
// Clone clones a repository using the configured authentication information.
1630
Clone(ctx context.Context, repoURL string, args ...string) (string, *gogit.Repository, error)
1731
}
32+
33+
func newConnector(ctx context.Context, source *Source) (Connector, error) {
34+
apiEndpoint := source.conn.Endpoint
35+
if apiEndpoint == "" || endsWithGithub.MatchString(apiEndpoint) {
36+
apiEndpoint = cloudV3Endpoint
37+
}
38+
39+
switch cred := source.conn.GetCredential().(type) {
40+
case *sourcespb.GitHub_GithubApp:
41+
log.RedactGlobally(cred.GithubApp.GetPrivateKey())
42+
return NewAppConnector(ctx, apiEndpoint, cred.GithubApp)
43+
case *sourcespb.GitHub_BasicAuth:
44+
log.RedactGlobally(cred.BasicAuth.GetPassword())
45+
return NewBasicAuthConnector(ctx, apiEndpoint, cred.BasicAuth)
46+
case *sourcespb.GitHub_Token:
47+
log.RedactGlobally(cred.Token)
48+
return NewTokenConnector(ctx, apiEndpoint, cred.Token, func(c context.Context, err error) bool {
49+
return source.handleRateLimit(c, err)
50+
})
51+
case *sourcespb.GitHub_Unauthenticated:
52+
return NewUnauthenticatedConnector(ctx, apiEndpoint)
53+
default:
54+
return nil, fmt.Errorf("unknown connection type %T", source.conn.GetCredential())
55+
}
56+
}
57+
58+
func createAPIClient(ctx context.Context, httpClient *http.Client, apiEndpoint string) (*github.Client, error) {
59+
getLogger(ctx).V(2).Info("Creating API client", "url", apiEndpoint)
60+
61+
// If we're using public GitHub, make a regular client.
62+
// Otherwise, make an enterprise client.
63+
if strings.EqualFold(apiEndpoint, cloudV3Endpoint) {
64+
return github.NewClient(httpClient), nil
65+
}
66+
67+
return github.NewClient(httpClient).WithEnterpriseURLs(apiEndpoint, apiEndpoint)
68+
}
69+
70+
func createGraphqlClient(ctx context.Context, client *http.Client, apiEndpoint string) (*githubv4.Client, error) {
71+
var graphqlEndpoint string
72+
if apiEndpoint == cloudV3Endpoint {
73+
graphqlEndpoint = cloudGraphqlEndpoint
74+
} else {
75+
// Use the root endpoint for the host.
76+
// https://docs.github.com/en/[email protected]/graphql/guides/introduction-to-graphql
77+
parsedURL, err := url.Parse(apiEndpoint)
78+
if err != nil {
79+
return nil, fmt.Errorf("could not create GraphQL client: %w", err)
80+
}
81+
82+
// GitHub Enterprise uses `/api/v3` for the base. (https://github.com/google/go-github/issues/958)
83+
// Swap it, and anything before `/api`, with GraphQL.
84+
before, _ := strings.CutSuffix(parsedURL.Path, "/api/v3")
85+
parsedURL.Path = before + "/api/graphql"
86+
graphqlEndpoint = parsedURL.String()
87+
}
88+
getLogger(ctx).V(2).Info("Creating GraphQL client", "url", graphqlEndpoint)
89+
90+
return githubv4.NewEnterpriseClient(graphqlEndpoint, client), nil
91+
}

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) (Connector, error) {
27+
func NewAppConnector(ctx context.Context, apiEndpoint string, app *credentialspb.GitHubApp) (Connector, 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) (Connecto
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
@@ -91,6 +100,10 @@ func (c *appConnector) Clone(ctx context.Context, repoURL string, args ...string
91100
return git.CloneRepoUsingToken(ctx, token.GetToken(), repoURL, "x-access-token", args...)
92101
}
93102

103+
func (c *appConnector) GraphQLClient() *githubv4.Client {
104+
return c.graphqlClient
105+
}
106+
94107
func (c *appConnector) InstallationClient() *github.Client {
95108
return c.installationClient
96109
}

pkg/sources/github/connector_basicauth.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,46 @@ 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) (Connector, error) {
25+
func NewBasicAuthConnector(ctx context.Context, apiEndpoint string, cred *credentialspb.BasicAuth) (Connector, 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

@@ -46,3 +55,7 @@ func (c *basicAuthConnector) APIClient() *github.Client {
4655
func (c *basicAuthConnector) Clone(ctx context.Context, repoURL string, args ...string) (string, *gogit.Repository, error) {
4756
return git.CloneRepoUsingToken(ctx, c.password, repoURL, c.username, args...)
4857
}
58+
59+
func (c *basicAuthConnector) GraphQLClient() *githubv4.Client {
60+
return c.graphqlClient
61+
}

pkg/sources/github/connector_token.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ 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
2024
handleRateLimit func(context.Context, error) bool
2125
user string
@@ -24,7 +28,7 @@ type tokenConnector struct {
2428

2529
var _ Connector = (*tokenConnector)(nil)
2630

27-
func NewTokenConnector(apiEndpoint string, token string, handleRateLimit func(context.Context, error) bool) (Connector, error) {
31+
func NewTokenConnector(ctx context.Context, apiEndpoint string, token string, handleRateLimit func(context.Context, error) bool) (Connector, error) {
2832
const httpTimeoutSeconds = 60
2933
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
3034
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
@@ -33,15 +37,21 @@ func NewTokenConnector(apiEndpoint string, token string, handleRateLimit func(co
3337
Source: tokenSource,
3438
}
3539

36-
apiClient, err := createGitHubClient(httpClient, apiEndpoint)
40+
apiClient, err := createAPIClient(ctx, httpClient, apiEndpoint)
3741
if err != nil {
3842
return nil, fmt.Errorf("could not create API client: %w", err)
3943
}
4044

45+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
46+
if err != nil {
47+
return nil, err
48+
}
49+
4150
return &tokenConnector{
4251
apiClient: apiClient,
52+
graphqlClient: graphqlClient,
4353
token: token,
44-
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudEndpoint),
54+
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudV3Endpoint),
4555
handleRateLimit: handleRateLimit,
4656
}, nil
4757
}
@@ -57,6 +67,10 @@ func (c *tokenConnector) Clone(ctx context.Context, repoURL string, args ...stri
5767
return git.CloneRepoUsingToken(ctx, c.token, repoURL, c.user, args...)
5868
}
5969

70+
func (c *tokenConnector) GraphQLClient() *githubv4.Client {
71+
return c.graphqlClient
72+
}
73+
6074
func (c *tokenConnector) IsGithubEnterprise() bool {
6175
return c.isGitHubEnterprise
6276
}

pkg/sources/github/connector_unauthenticated.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,36 @@ 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) (Connector, error) {
22+
func NewUnauthenticatedConnector(ctx context.Context, apiEndpoint string) (Connector, 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

@@ -36,3 +45,7 @@ func (c *unauthenticatedConnector) APIClient() *github.Client {
3645
func (c *unauthenticatedConnector) Clone(ctx context.Context, repoURL string, args ...string) (string, *gogit.Repository, error) {
3746
return git.CloneRepoUsingUnauthenticated(ctx, repoURL, args...)
3847
}
48+
49+
func (c *unauthenticatedConnector) GraphQLClient() *githubv4.Client {
50+
return c.graphqlClient
51+
}

0 commit comments

Comments
 (0)