Skip to content

Commit bb899f2

Browse files
rgmzkashifkhan0771
andauthored
[GitHub] Add a GraphQL client to the connector (#3837)
* feat(github): add graphql client * Update connector.go * Update connector.go * Update connector_basicauth.go * Update connector_token.go * Update connector_unauthenticated.go * Update connector_app.go --------- Co-authored-by: Kashif Khan <[email protected]>
1 parent 44c672b commit bb899f2

File tree

8 files changed

+159
-60
lines changed

8 files changed

+159
-60
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ require (
271271
github.com/sendgrid/rest v2.6.9+incompatible // indirect
272272
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
273273
github.com/shoenig/go-m1cpu v0.1.6 // indirect
274+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 // indirect
275+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
274276
github.com/sirupsen/logrus v1.9.3 // indirect
275277
github.com/skeema/knownhosts v1.3.0 // indirect
276278
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
@@ -736,6 +736,10 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
736736
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
737737
github.com/shuheiktgw/go-travis v0.3.1 h1:SAT16mi77ccqogOslnXxBXzXbpeyChaIYUwi2aJpVZY=
738738
github.com/shuheiktgw/go-travis v0.3.1/go.mod h1:avnFFDqJDdRHwlF9tgqvYi3asQCm/HGL8aLxYiKa4Yg=
739+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
740+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
741+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
742+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
739743
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
740744
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
741745
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=

pkg/sources/github/connector.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,92 @@
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, source.conn.GetClonePath(), cred.BasicAuth)
46+
case *sourcespb.GitHub_Token:
47+
log.RedactGlobally(cred.Token)
48+
return NewTokenConnector(ctx, apiEndpoint, cred.Token, source.conn.GetClonePath(), source.useAuthInUrl, func(c context.Context, err error) bool {
49+
return source.handleRateLimit(c, err)
50+
})
51+
case *sourcespb.GitHub_Unauthenticated:
52+
return NewUnauthenticatedConnector(ctx, apiEndpoint, source.conn.GetClonePath())
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+
ctx.Logger().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("error parsing URL: %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+
89+
ctx.Logger().V(2).Info("Creating GraphQL client", "url", graphqlEndpoint)
90+
91+
return githubv4.NewEnterpriseClient(graphqlEndpoint, client), nil
92+
}

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, fmt.Errorf("error creating GraphQL client: %w", 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", true, 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: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,48 @@ 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
18-
clonePath string
17+
apiClient *github.Client
18+
graphqlClient *githubv4.Client
19+
username string
20+
password string
21+
clonePath string
1922
}
2023

2124
var _ Connector = (*basicAuthConnector)(nil)
2225

23-
func NewBasicAuthConnector(apiEndpoint, clonePath string, cred *credentialspb.BasicAuth) (Connector, error) {
26+
func NewBasicAuthConnector(ctx context.Context, apiEndpoint, clonePath string, cred *credentialspb.BasicAuth) (Connector, error) {
2427
const httpTimeoutSeconds = 60
2528
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
2629
httpClient.Transport = &github.BasicAuthTransport{
2730
Username: cred.Username,
2831
Password: cred.Password,
2932
}
3033

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

39+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
40+
if err != nil {
41+
return nil, fmt.Errorf("error creating GraphQL client: %w", err)
42+
}
43+
3644
return &basicAuthConnector{
37-
apiClient: apiClient,
38-
username: cred.Username,
39-
password: cred.Password,
40-
clonePath: clonePath,
45+
apiClient: apiClient,
46+
graphqlClient: graphqlClient,
47+
username: cred.Username,
48+
password: cred.Password,
49+
clonePath: clonePath,
4150
}, nil
4251
}
4352

@@ -48,3 +57,7 @@ func (c *basicAuthConnector) APIClient() *github.Client {
4857
func (c *basicAuthConnector) Clone(ctx context.Context, repoURL string, args ...string) (string, *gogit.Repository, error) {
4958
return git.CloneRepoUsingToken(ctx, c.password, repoURL, c.clonePath, c.username, true, args...)
5059
}
60+
61+
func (c *basicAuthConnector) GraphQLClient() *githubv4.Client {
62+
return c.graphqlClient
63+
}

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
@@ -26,7 +30,7 @@ type tokenConnector struct {
2630

2731
var _ Connector = (*tokenConnector)(nil)
2832

29-
func NewTokenConnector(apiEndpoint, token, clonePath string, authInUrl bool, handleRateLimit func(context.Context, error) bool) (Connector, error) {
33+
func NewTokenConnector(ctx context.Context, apiEndpoint, token, clonePath string, authInUrl bool, handleRateLimit func(context.Context, error) bool) (Connector, error) {
3034
const httpTimeoutSeconds = 60
3135
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
3236
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
@@ -35,15 +39,21 @@ func NewTokenConnector(apiEndpoint, token, clonePath string, authInUrl bool, han
3539
Source: tokenSource,
3640
}
3741

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

47+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
48+
if err != nil {
49+
return nil, fmt.Errorf("error creating GraphQL client: %w", err)
50+
}
51+
4352
return &tokenConnector{
4453
apiClient: apiClient,
54+
graphqlClient: graphqlClient,
4555
token: token,
46-
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudEndpoint),
56+
isGitHubEnterprise: !strings.EqualFold(apiEndpoint, cloudV3Endpoint),
4757
handleRateLimit: handleRateLimit,
4858
authInUrl: authInUrl,
4959
clonePath: clonePath,
@@ -62,6 +72,10 @@ func (c *tokenConnector) Clone(ctx context.Context, repoURL string, args ...stri
6272
return git.CloneRepoUsingToken(ctx, c.token, repoURL, c.clonePath, c.user, c.authInUrl, args...)
6373
}
6474

75+
func (c *tokenConnector) GraphQLClient() *githubv4.Client {
76+
return c.graphqlClient
77+
}
78+
6579
func (c *tokenConnector) IsGithubEnterprise() bool {
6680
return c.isGitHubEnterprise
6781
}

pkg/sources/github/connector_unauthenticated.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,39 @@ 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
18+
1619
clonePath string
1720
}
1821

1922
var _ Connector = (*unauthenticatedConnector)(nil)
2023

21-
func NewUnauthenticatedConnector(apiEndpoint, clonePath string) (Connector, error) {
24+
func NewUnauthenticatedConnector(ctx context.Context, apiEndpoint, clonePath string) (Connector, error) {
2225
const httpTimeoutSeconds = 60
2326
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
24-
apiClient, err := createGitHubClient(httpClient, apiEndpoint)
27+
apiClient, err := createAPIClient(ctx, httpClient, apiEndpoint)
2528
if err != nil {
2629
return nil, fmt.Errorf("could not create API client: %w", err)
2730
}
31+
32+
graphqlClient, err := createGraphqlClient(ctx, httpClient, apiEndpoint)
33+
if err != nil {
34+
return nil, fmt.Errorf("error creating GraphQL client: %w", err)
35+
}
36+
2837
return &unauthenticatedConnector{
29-
apiClient: apiClient,
30-
clonePath: clonePath,
38+
apiClient: apiClient,
39+
graphqlClient: graphqlClient,
40+
clonePath: clonePath,
3141
}, nil
3242
}
3343

@@ -38,3 +48,7 @@ func (c *unauthenticatedConnector) APIClient() *github.Client {
3848
func (c *unauthenticatedConnector) Clone(ctx context.Context, repoURL string, args ...string) (string, *gogit.Repository, error) {
3949
return git.CloneRepoUsingUnauthenticated(ctx, repoURL, c.clonePath, args...)
4050
}
51+
52+
func (c *unauthenticatedConnector) GraphQLClient() *githubv4.Client {
53+
return c.graphqlClient
54+
}

0 commit comments

Comments
 (0)