Skip to content

Commit

Permalink
resolved the http client issue using with aws sdk.
Browse files Browse the repository at this point in the history
passing the user-agent via middleware in aws sdk.
remove the hash logic since it was making the AWS Access key test as flaky by randomly generating the incorrect values.
  • Loading branch information
abmussani committed Feb 20, 2025
1 parent 239f965 commit b906a35
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 23 deletions.
4 changes: 2 additions & 2 deletions pkg/common/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ type CustomTransport struct {
T http.RoundTripper
}

func userAgent() string {
func UserAgent() string {
if len(feature.UserAgentSuffix.Load()) > 0 {
return "TruffleHog " + feature.UserAgentSuffix.Load()
}
return "TruffleHog"
}

func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Add("User-Agent", userAgent())
req.Header.Add("User-Agent", UserAgent())
return t.T.RoundTrip(req)
}

Expand Down
36 changes: 30 additions & 6 deletions pkg/detectors/aws/access_keys/accesskey.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package access_keys
import (
"context"
"fmt"
"net"
"net/http"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws/middleware"
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sts"
Expand All @@ -19,7 +23,7 @@ import (
)

type scanner struct {
verificationClient *http.Client
verificationClient config.HTTPClient
skipIDs map[string]struct{}
detectors.DefaultMultiPartCredentialProvider
}
Expand Down Expand Up @@ -54,7 +58,6 @@ var _ interface {
} = (*scanner)(nil)

var (
defaultVerificationClient = common.SaneHttpClient()

// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
// Key types are from this list https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids
Expand All @@ -71,9 +74,28 @@ func (s scanner) Keywords() []string {
}
}

func (s scanner) getClient() *http.Client {
// The recommended way by AWS is to use the SDK's http client.
// https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-http.html
// Note: Using default http.Client causes SignatureInvalid error in response. therefore, based on http default client implementation, we are using the same configuration.
func getDefaultBuildableClient() *awshttp.BuildableClient {
return awshttp.NewBuildableClient().
WithTimeout(common.DefaultResponseTimeout).
WithDialerOptions(func(dialer *net.Dialer) {
dialer.Timeout = 2 * time.Second
dialer.KeepAlive = 5 * time.Second
}).
WithTransportOptions(func(tr *http.Transport) {
tr.Proxy = http.ProxyFromEnvironment
tr.MaxIdleConns = 5
tr.IdleConnTimeout = 5 * time.Second
tr.TLSHandshakeTimeout = 3 * time.Second
tr.ExpectContinueTimeout = 1 * time.Second
})
}

func (s scanner) getAWSBuilableClient() config.HTTPClient {
if s.verificationClient == nil {
s.verificationClient = defaultVerificationClient
s.verificationClient = getDefaultBuildableClient()
}
return s.verificationClient
}
Expand Down Expand Up @@ -208,7 +230,7 @@ func (s scanner) verifyMatch(ctx context.Context, resIDMatch, resSecretMatch str
// Prep AWS Creds for STS
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion(region),
config.WithHTTPClient(s.getClient()),
config.WithHTTPClient(s.getAWSBuilableClient()),
config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(resIDMatch, resSecretMatch, ""),
),
Expand All @@ -217,7 +239,9 @@ func (s scanner) verifyMatch(ctx context.Context, resIDMatch, resSecretMatch str
return false, nil, err
}
// Create STS client
stsClient := sts.NewFromConfig(cfg)
stsClient := sts.NewFromConfig(cfg, func(o *sts.Options) {
o.APIOptions = append(o.APIOptions, middleware.AddUserAgentKeyValue("User-Agent", common.UserAgent()))
})

// Make the GetCallerIdentity API call
resp, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
Expand Down
35 changes: 22 additions & 13 deletions pkg/detectors/aws/access_keys/accesskey_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ package access_keys

import (
"context"
"crypto/sha256"
"fmt"
"sort"
"testing"
"time"

"github.com/brianvoe/gofakeit/v7"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -95,9 +96,8 @@ func TestAWS_FromChunk(t *testing.T) {
id := testSecrets.MustGetField("AWS_ID")
inactiveSecret := testSecrets.MustGetField("AWS_INACTIVE")
inactiveID := id[:len(id)-3] + "XYZ"
hasher := sha256.New()
hasher.Write([]byte(inactiveSecret))
hash := string(hasher.Sum(nil))

hash := gofakeit.Password(true, true, true, false, false, 10)

type args struct {
ctx context.Context
Expand Down Expand Up @@ -233,15 +233,6 @@ func TestAWS_FromChunk(t *testing.T) {
"user_id": "AIDAZAVB57H5V3Q4ACRGM",
},
},
{
DetectorType: detectorspb.DetectorType_AWS,
Verified: false,
Redacted: inactiveID,
ExtraData: map[string]string{
"account": "619888638459",
"resource_type": "Access key",
},
},
},
wantErr: false,
},
Expand Down Expand Up @@ -387,6 +378,15 @@ func TestAWS_FromChunk(t *testing.T) {
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_AWS,
Verified: false,
Redacted: "AKIAZAVB57H55F3T4BKH",
ExtraData: map[string]string{
"resource_type": "Access key",
"account": "619888638459",
},
},
{
DetectorType: detectorspb.DetectorType_AWS,
Verified: true,
Expand Down Expand Up @@ -425,13 +425,22 @@ func TestAWS_FromChunk(t *testing.T) {
return x.Redacted < y.Redacted
}),
}

sortResults(tt.want)
if diff := cmp.Diff(got, tt.want, ignoreOpts...); diff != "" {
t.Errorf("AWS.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
}
})
}
}

// Helper function to sort results due to the order of the redacted
func sortResults(results []detectors.Result) {
sort.SliceStable(results, func(i, j int) bool {
return results[i].Redacted < results[j].Redacted
})
}

func BenchmarkFromData(benchmark *testing.B) {
ctx := context.Background()
s := scanner{}
Expand Down
8 changes: 6 additions & 2 deletions pkg/detectors/aws/access_keys/canary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/middleware"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
)

const thinkstMessage = "This is an AWS canary token generated at canarytokens.org, and was not set off; learn more here: https://trufflesecurity.com/canaries"
Expand Down Expand Up @@ -49,15 +51,17 @@ func (s scanner) verifyCanary(ctx context.Context, resIDMatch, resSecretMatch st
// Prep AWS Creds for SNS
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion(region),
config.WithHTTPClient(s.getClient()),
config.WithHTTPClient(s.getAWSBuilableClient()),
config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(resIDMatch, resSecretMatch, ""),
),
)
if err != nil {
return false, "", err
}
svc := sns.NewFromConfig(cfg)
svc := sns.NewFromConfig(cfg, func(o *sns.Options) {
o.APIOptions = append(o.APIOptions, middleware.AddUserAgentKeyValue("User-Agent", common.UserAgent()))
})

// Prep vars and Publish to SNS
_, err = svc.Publish(ctx, &sns.PublishInput{
Expand Down

0 comments on commit b906a35

Please sign in to comment.