Files
trufflehog/pkg/sources/github/connector_app.go
Cody Rose f26b502c2e Auth GitHub in Init (#3131)
The GitHub source currently applies its authentication configuration as the first step of enumeration. This is incompatible with both targeted scans and scan job reports, and also means that authentication logic has to be duplicated into the validation flow. This PR moves it into Init so that it's available to targeted scans and, eventually, unit-specific scans. This also allows us to remove the copy of the old logic that was in Validate.

As part of the work I've also cleaned up the integration test suite. (Several of them were apparently disabled back when they ran on every push, but now that we're not doing that, we can re-enable them.)
2024-08-05 15:13:29 -04:00

97 lines
2.9 KiB
Go

package github
import (
"fmt"
"strconv"
"github.com/bradleyfalzon/ghinstallation/v2"
gogit "github.com/go-git/go-git/v5"
"github.com/google/go-github/v63/github"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/git"
)
type appConnector struct {
apiClient *github.Client
installationClient *github.Client
installationID int64
}
var _ connector = (*appConnector)(nil)
func newAppConnector(apiEndpoint string, app *credentialspb.GitHubApp) (*appConnector, error) {
installationID, err := strconv.ParseInt(app.InstallationId, 10, 64)
if err != nil {
return nil, fmt.Errorf("could not parse app installation ID %q: %w", app.InstallationId, err)
}
appID, err := strconv.ParseInt(app.AppId, 10, 64)
if err != nil {
return nil, fmt.Errorf("could not parse app ID %q: %w", appID, err)
}
const httpTimeoutSeconds = 60
httpClient := common.RetryableHTTPClientTimeout(int64(httpTimeoutSeconds))
installationTransport, err := ghinstallation.NewAppsTransport(
httpClient.Transport,
appID,
[]byte(app.PrivateKey))
if err != nil {
return nil, fmt.Errorf("could not create installation client transport: %w", err)
}
installationTransport.BaseURL = apiEndpoint
installationHttpClient := common.RetryableHTTPClientTimeout(60)
installationHttpClient.Transport = installationTransport
installationClient, err := github.NewClient(installationHttpClient).WithEnterpriseURLs(apiEndpoint, apiEndpoint)
if err != nil {
return nil, fmt.Errorf("could not create installation client: %w", err)
}
apiTransport, err := ghinstallation.New(
httpClient.Transport,
appID,
installationID,
[]byte(app.PrivateKey))
if err != nil {
return nil, fmt.Errorf("could not create API client transport: %w", err)
}
apiTransport.BaseURL = apiEndpoint
httpClient.Transport = apiTransport
apiClient, err := github.NewClient(httpClient).WithEnterpriseURLs(apiEndpoint, apiEndpoint)
if err != nil {
return nil, fmt.Errorf("could not create API client: %w", err)
}
return &appConnector{
apiClient: apiClient,
installationClient: installationClient,
installationID: installationID,
}, nil
}
func (c *appConnector) APIClient() *github.Client {
return c.apiClient
}
func (c *appConnector) Clone(ctx context.Context, repoURL string) (string, *gogit.Repository, error) {
// TODO: Check rate limit for this call.
token, _, err := c.installationClient.Apps.CreateInstallationToken(
ctx,
c.installationID,
&github.InstallationTokenOptions{})
if err != nil {
return "", nil, fmt.Errorf("could not create installation token: %w", err)
}
return git.CloneRepoUsingToken(ctx, token.GetToken(), repoURL, "x-access-token")
}
func (c *appConnector) InstallationClient() *github.Client {
return c.installationClient
}