diff --git a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth.go b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth.go index 87fed3374d01..777c1fdc7468 100644 --- a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth.go +++ b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth.go @@ -11,20 +11,22 @@ import ( regexp "github.com/wasilibs/go-re2" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct { client *http.Client + detectors.DefaultMultiPartCredentialProvider + detectors.EndpointSetter } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) +var _ detectors.EndpointCustomizer = (*Scanner)(nil) var ( - defaultClient = common.SaneHttpClient() + defaultClient = detectors.DetectorHttpClientWithNoLocalAddresses roleIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"role"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`) @@ -61,15 +63,19 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result uniqueVaultUrls[url] = struct{}{} } - // If no names or secrets found, return empty results - if len(uniqueRoleIds) == 0 || len(uniqueSecretIds) == 0 || len(uniqueVaultUrls) == 0 { + // If no roleIds or secrets found, return empty results + if len(uniqueRoleIds) == 0 || len(uniqueSecretIds) == 0 { return results, nil } + endpoints := make([]string, 0, len(uniqueVaultUrls)) + for endpoint := range uniqueVaultUrls { + endpoints = append(endpoints, endpoint) + } // create combination results that can be verified for roleId := range uniqueRoleIds { for secretId := range uniqueSecretIds { - for vaultUrl := range uniqueVaultUrls { + for _, vaultUrl := range s.Endpoints(endpoints...) { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HashiCorpVaultAuth, Raw: []byte(secretId), diff --git a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_integration_test.go b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_integration_test.go index 2b071bbde90a..4761577988d0 100644 --- a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_integration_test.go +++ b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_integration_test.go @@ -142,6 +142,8 @@ func TestHashiCorpVaultAuth_FromChunk(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.s.UseCloudEndpoint(true) + tt.s.UseFoundEndpoints(true) got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("HashiCorpVaultAuth.FromData() error = %v, wantErr %v", err, tt.wantErr) @@ -164,6 +166,58 @@ func TestHashiCorpVaultAuth_FromChunk(t *testing.T) { } } +func TestHashiCorpVaultAuth_WithCustomEndpoint(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors6") + if err != nil { + t.Fatalf("could not get test secrets from GCP: %s", err) + } + roleId := testSecrets.MustGetField("HASHICORPVAULTAUTH_ROLE_ID") + secretId := testSecrets.MustGetField("HASHICORPVAULTAUTH_SECRET_ID") + vaultUrl := testSecrets.MustGetField("HASHICORPVAULTAUTH_URL") + s := Scanner{} + s.UseCloudEndpoint(true) + s.UseFoundEndpoints(true) + s.SetConfiguredEndpoints(vaultUrl) + data := fmt.Appendf(nil, "hashicorp config:\nrole_id: %s\nsecret_id: %s", roleId, secretId) + + results, err := s.FromData(context.Background(), true, data) + if err != nil { + t.Fatalf("unexpected error from FromData: %v", err) + } + if len(results) == 0 { + t.Fatal("expected at least one result from FromData, got 0") + } + for _, result := range results { + endpoint, ok := result.ExtraData["URL"] + if !ok { + t.Fatalf("expected URL in ExtraData, got: %v", result.ExtraData) + } + if endpoint != vaultUrl { + t.Fatalf("expected endpoint %s, got %s", vaultUrl, endpoint) + } + } + want := []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_HashiCorpVaultAuth, + Verified: true, + VerificationFromCache: false, + Raw: []byte(secretId), + RawV2: []byte(fmt.Sprintf("%s:%s", roleId, secretId)), + ExtraData: map[string]string{ + "URL": vaultUrl, + }, + StructuredData: nil, + }, + } + + ignoreOpts := cmpopts.IgnoreUnexported(detectors.Result{}) + if diff := cmp.Diff(results, want, ignoreOpts); diff != "" { + t.Errorf("HashiCorpVaultAuth.FromData() %s diff: (-got +want)\n%s", "TestHashiCorpVaultAuth_WithCustomEndpoint", diff) + } +} + func BenchmarkFromData(benchmark *testing.B) { ctx := context.Background() s := Scanner{} diff --git a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_test.go b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_test.go index a06869dfc3a0..dbfefda4a49f 100644 --- a/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_test.go +++ b/pkg/detectors/hashicorpvaultauth/hashicorpvaultauth_test.go @@ -126,7 +126,8 @@ func TestHashiCorpVaultAppRoleAuth_Pattern(t *testing.T) { t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input) return } - + d.UseCloudEndpoint(true) + d.UseFoundEndpoints(true) results, err := d.FromData(context.Background(), false, []byte(test.input)) if err != nil { t.Errorf("error = %v", err) diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index c6d4bdec57a5..31918ed4d28b 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -1377,6 +1377,7 @@ func TestEngineInitializesCloudProviderDetectors(t *testing.T) { detectorspb.DetectorType_ArtifactoryAccessToken: {}, detectorspb.DetectorType_ArtifactoryReferenceToken: {}, detectorspb.DetectorType_TableauPersonalAccessToken: {}, + detectorspb.DetectorType_HashiCorpVaultAuth: {}, // these do not have any cloud endpoint }