From 11394ea318a12a5bf99c1b0cb7ba5e44b3c7a79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81h=CC=95=CC=B3m=CD=AD=CD=AD=CD=A8=CD=A9=CC=90e=CD=AC?= =?UTF-8?q?=CC=81=CD=8B=CD=AC=CC=8A=CC=93=CD=82=CC=98d?= <13666360+0x1@users.noreply.github.com> Date: Tue, 5 Dec 2023 08:57:52 -0500 Subject: [PATCH] [thog-1548] add auto redaction for verification errors (#2106) * Updating VerificationError to have auto redaction logic * find/replace error --- .../airtableapikey/airtableapikey_test.go | 2 +- pkg/detectors/alchemy/alchemy.go | 5 +- pkg/detectors/alchemy/alchemy_test.go | 11 +- pkg/detectors/anthropic/anthropic.go | 8 +- pkg/detectors/anthropic/anthropic_test.go | 11 +- pkg/detectors/appoptics/appoptics.go | 5 +- pkg/detectors/appoptics/appoptics_test.go | 8 +- pkg/detectors/aws/aws.go | 10 +- pkg/detectors/aws/aws_test.go | 6 +- pkg/detectors/awssessionkeys/awssessionkey.go | 6 +- pkg/detectors/azurebatch/azurebatch_test.go | 6 +- .../azurecontainerregistry.go | 5 +- .../azurecontainerregistry_test.go | 12 +- pkg/detectors/azurestorage/azurestorage.go | 5 +- .../azurestorage/azurestorage_test.go | 6 +- pkg/detectors/betterstack/betterstack.go | 5 +- pkg/detectors/betterstack/betterstack_test.go | 6 +- .../bitcoinaverage/bitcoinaverage.go | 2 +- .../braintreepayments/braintreepayments.go | 2 +- .../braintreepayments_test.go | 6 +- pkg/detectors/browserstack/browserstack.go | 2 +- .../browserstack/browserstack_test.go | 6 +- pkg/detectors/budibase/budibase.go | 8 +- pkg/detectors/budibase/budibase_test.go | 20 +-- pkg/detectors/cloudsmith/cloudsmith.go | 2 +- pkg/detectors/coda/coda.go | 5 +- pkg/detectors/coda/coda_test.go | 11 +- pkg/detectors/codeclimate/codeclimate.go | 2 +- pkg/detectors/coinbase_waas/coinbase_waas.go | 9 +- .../coinbase_waas/coinbase_waas_test.go | 6 +- .../databrickstoken/databrickstoken.go | 17 +-- .../databrickstoken/databrickstoken_test.go | 11 +- pkg/detectors/deno/denodeploy.go | 16 ++- pkg/detectors/deno/denodeploy_test.go | 6 +- pkg/detectors/detectors.go | 40 +++++- pkg/detectors/eventbrite/eventbrite.go | 5 +- pkg/detectors/eventbrite/eventbrite_test.go | 11 +- pkg/detectors/falsepositives.go | 2 +- .../v1/figmapersonalaccesstoken.go | 5 +- .../v1/figmapersonalaccesstoken_test.go | 6 +- .../v2/figmapersonalaccesstoken_v2.go | 5 +- .../v2/figmapersonalaccesstoken_v2_test.go | 6 +- pkg/detectors/formsite/formsite_test.go | 8 ++ pkg/detectors/ftp/ftp.go | 12 +- pkg/detectors/ftp/ftp_test.go | 11 +- pkg/detectors/github/github.go | 2 +- pkg/detectors/gitlab/gitlab.go | 2 +- pkg/detectors/gitlab/gitlab_v1_test.go | 6 +- pkg/detectors/gitlab/gitlab_v2_test.go | 6 +- pkg/detectors/gitlabv2/gitlab_v1_test.go | 6 +- pkg/detectors/gitlabv2/gitlab_v2.go | 2 +- pkg/detectors/gitlabv2/gitlab_v2_test.go | 6 +- pkg/detectors/grafana/grafana.go | 5 +- pkg/detectors/grafana/grafana_test.go | 11 +- .../grafanaserviceaccount.go | 17 +-- .../grafanaserviceaccount_test.go | 11 +- pkg/detectors/huggingface/huggingface.go | 5 +- pkg/detectors/huggingface/huggingface_test.go | 11 +- pkg/detectors/instamojo/instamojo.go | 7 +- pkg/detectors/instamojo/instamojo_test.go | 3 +- pkg/detectors/ip2location/ip2location.go | 14 +- pkg/detectors/ip2location/ip2location_test.go | 6 +- pkg/detectors/ipinfo/ipinfo.go | 5 +- pkg/detectors/ipinfo/ipinfo_test.go | 11 +- pkg/detectors/jdbc/jdbc.go | 3 +- pkg/detectors/jiratoken/jiratoken.go | 2 +- pkg/detectors/jiratoken/jiratoken_test.go | 6 +- pkg/detectors/jiratoken_v2/jiratoken_v2.go | 2 +- .../jiratoken_v2/jiratoken_v2_test.go | 6 +- pkg/detectors/klaviyo/klaviyo.go | 9 +- pkg/detectors/klaviyo/klaviyo_test.go | 11 +- pkg/detectors/launchdarkly/launchdarkly.go | 7 +- pkg/detectors/ldap/ldap.go | 4 +- pkg/detectors/ldap/ldap_integration_test.go | 11 +- pkg/detectors/lemonsqueezy/lemonsqueezy.go | 5 +- .../lemonsqueezy/lemonsqueezy_test.go | 6 +- pkg/detectors/liveagent/liveagent.go | 2 +- pkg/detectors/loggly/loggly.go | 130 +++++++++--------- pkg/detectors/loggly/loggly_test.go | 11 +- pkg/detectors/logzio/logzio.go | 5 +- pkg/detectors/logzio/logzio_test.go | 11 +- pkg/detectors/meaningcloud/meaningcloud.go | 2 +- .../microsoftteamswebhook.go | 2 +- .../microsoftteamswebhook_test.go | 6 +- pkg/detectors/mongodb/mongodb.go | 2 +- pkg/detectors/mongodb/mongodb_test.go | 6 +- pkg/detectors/myfreshworks/myfreshworks.go | 2 +- .../myfreshworks/myfreshworks_test.go | 2 +- pkg/detectors/ngrok/ngrok.go | 8 +- pkg/detectors/ngrok/ngrok_test.go | 6 +- pkg/detectors/notion/notion.go | 2 +- pkg/detectors/openvpn/openvpn.go | 14 +- pkg/detectors/openvpn/openvpn_test.go | 6 +- pkg/detectors/overloop/overloop.go | 5 +- pkg/detectors/overloop/overloop_test.go | 11 +- .../pagerdutyapikey/pagerdutyapikey.go | 2 +- .../pagerdutyapikey/pagerdutyapikey_test.go | 6 +- pkg/detectors/planetscale/planetscale.go | 5 +- pkg/detectors/planetscale/planetscale_test.go | 6 +- pkg/detectors/planetscaledb/planetscaledb.go | 4 +- .../planetscaledb/planetscaledb_test.go | 6 +- pkg/detectors/portainer/portainer.go | 11 +- pkg/detectors/portainer/portainer_test.go | 6 +- .../portainertoken/portainertoken.go | 11 +- .../portainertoken/portainertoken_test.go | 6 +- pkg/detectors/postman/postman.go | 2 +- pkg/detectors/postman/postman_test.go | 6 +- pkg/detectors/privacy/privacy.go | 5 +- pkg/detectors/privacy/privacy_test.go | 11 +- pkg/detectors/privatekey/privatekey.go | 33 ++--- .../pubnubpublishkey/pubnubpublishkey.go | 2 +- .../pubnubpublishkey/pubnubpublishkey_test.go | 6 +- pkg/detectors/ramp/ramp.go | 12 +- pkg/detectors/ramp/ramp_test.go | 11 +- pkg/detectors/replicate/replicate.go | 7 +- pkg/detectors/replicate/replicate_test.go | 6 +- pkg/detectors/replyio/replyio.go | 5 +- pkg/detectors/replyio/replyio_test.go | 6 +- .../requestfinance/requestfinance.go | 7 +- .../requestfinance/requestfinance_test.go | 6 +- pkg/detectors/salesforce/salesforce.go | 5 +- pkg/detectors/salesforce/salesforce_test.go | 11 +- pkg/detectors/screenshotapi/screenshotapi.go | 2 +- pkg/detectors/sendbird/sendbird.go | 11 +- pkg/detectors/sendbird/sendbird_test.go | 6 +- .../sendbirdorganizationapi.go | 7 +- .../sendbirdorganizationapi_test.go | 6 +- pkg/detectors/sendgrid/sendgrid.go | 9 +- pkg/detectors/sendgrid/sendgrid_test.go | 2 +- pkg/detectors/sentrytoken/sentrytoken.go | 2 +- pkg/detectors/sentrytoken/sentrytoken_test.go | 6 +- pkg/detectors/slack/slack.go | 8 +- pkg/detectors/slack/slack_test.go | 6 +- pkg/detectors/slackwebhook/slackwebhook.go | 5 +- .../slackwebhook/slackwebhook_test.go | 6 +- pkg/detectors/snowflake/snowflake.go | 11 +- pkg/detectors/snowflake/snowflake_test.go | 11 +- pkg/detectors/sourcegraph/sourcegraph.go | 5 +- pkg/detectors/sourcegraph/sourcegraph_test.go | 11 +- .../sourcegraphcody/sourcegraphcody.go | 5 +- .../sourcegraphcody/sourcegraphcody_test.go | 6 +- pkg/detectors/sqlserver/sqlserver.go | 10 +- .../sqlserver/sqlserver_integration_test.go | 6 +- pkg/detectors/stripo/stripo.go | 5 +- pkg/detectors/stripo/stripo_test.go | 6 +- pkg/detectors/surveybot/surveybot.go | 2 +- pkg/detectors/tailscale/tailscale.go | 5 +- pkg/detectors/twilio/twilio.go | 5 +- pkg/detectors/twilio/twilio_test.go | 11 +- pkg/detectors/twitch/twitch.go | 2 +- pkg/detectors/twitch/twitch_test.go | 6 +- pkg/detectors/uri/uri.go | 4 +- pkg/detectors/uri/uri_test.go | 15 +- .../vagrantcloudpersonaltoken.go | 9 +- .../vagrantcloudpersonaltoken_test.go | 6 +- pkg/detectors/voiceflow/voiceflow.go | 5 +- pkg/detectors/voiceflow/voiceflow_test.go | 11 +- pkg/detectors/web3storage/web3storage.go | 5 +- pkg/detectors/web3storage/web3storage_test.go | 11 +- pkg/detectors/zerotier/zerotier.go | 5 +- pkg/detectors/zerotier/zerotier_test.go | 6 +- pkg/detectors/zulipchat/zulipchat.go | 20 +-- pkg/detectors/zulipchat/zulipchat_test.go | 11 +- 163 files changed, 710 insertions(+), 585 deletions(-) diff --git a/pkg/detectors/airtableapikey/airtableapikey_test.go b/pkg/detectors/airtableapikey/airtableapikey_test.go index cef78a00d..6c3e962ed 100644 --- a/pkg/detectors/airtableapikey/airtableapikey_test.go +++ b/pkg/detectors/airtableapikey/airtableapikey_test.go @@ -100,7 +100,7 @@ func TestAirtableApiKey_FromChunk(t *testing.T) { } got[i].Raw = nil } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Airtable.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/alchemy/alchemy.go b/pkg/detectors/alchemy/alchemy.go index 86cd69549..2e806bdbc 100644 --- a/pkg/detectors/alchemy/alchemy.go +++ b/pkg/detectors/alchemy/alchemy.go @@ -65,10 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/alchemy/alchemy_test.go b/pkg/detectors/alchemy/alchemy_test.go index eb460d50f..6bf24a625 100644 --- a/pkg/detectors/alchemy/alchemy_test.go +++ b/pkg/detectors/alchemy/alchemy_test.go @@ -6,11 +6,12 @@ package alchemy import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestAlchemy_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Alchemy.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/anthropic/anthropic.go b/pkg/detectors/anthropic/anthropic.go index 9168bf8f3..bf4d74d57 100644 --- a/pkg/detectors/anthropic/anthropic.go +++ b/pkg/detectors/anthropic/anthropic.go @@ -82,7 +82,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode == http.StatusBadRequest { var resp response if err = json.NewDecoder(res.Body).Decode(&resp); err != nil { - s1.VerificationError = fmt.Errorf("unexpected HTTP response body: %w", err) + err = fmt.Errorf("unexpected HTTP response body: %w", err) + s1.SetVerificationError(err, resMatch) continue } if resp.Error.Message == "max_tokens_to_sample: field required" { @@ -95,10 +96,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result // The secret is determinately not verified (nothing to do) // Anthropic returns 401 on all requests not containing a valid x-api-key header } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/anthropic/anthropic_test.go b/pkg/detectors/anthropic/anthropic_test.go index 7b4ec5155..64713de61 100644 --- a/pkg/detectors/anthropic/anthropic_test.go +++ b/pkg/detectors/anthropic/anthropic_test.go @@ -6,11 +6,12 @@ package anthropic import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestAnthropic_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Anthropic.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/appoptics/appoptics.go b/pkg/detectors/appoptics/appoptics.go index f2bf45497..90229a97c 100644 --- a/pkg/detectors/appoptics/appoptics.go +++ b/pkg/detectors/appoptics/appoptics.go @@ -73,10 +73,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/appoptics/appoptics_test.go b/pkg/detectors/appoptics/appoptics_test.go index 736bf738e..a50a583b0 100644 --- a/pkg/detectors/appoptics/appoptics_test.go +++ b/pkg/detectors/appoptics/appoptics_test.go @@ -86,7 +86,7 @@ func TestAppoptics_FromChunk(t *testing.T) { want: nil, wantErr: false, wantVerificationErr: false, - }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -99,11 +99,11 @@ func TestAppoptics_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Appoptics.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/aws/aws.go b/pkg/detectors/aws/aws.go index c74f60609..b29cd8380 100644 --- a/pkg/detectors/aws/aws.go +++ b/pkg/detectors/aws/aws.go @@ -137,8 +137,8 @@ func (s scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - verified, extraData, verificationErr := s.verifyMatch(ctx, resIDMatch, resSecretMatch, true) - s1.Verified = verified + isVerified, extraData, verificationErr := s.verifyMatch(ctx, resIDMatch, resSecretMatch, true) + s1.Verified = isVerified // It'd be good to log when calculated account value does not match // the account value from verification. Should only be edge cases at most. // if extraData["account"] != s1.ExtraData["account"] && extraData["account"] != "" {//log here} @@ -148,7 +148,9 @@ func (s scanner) FromData(ctx context.Context, verify bool, data []byte) (result for k, v := range extraData { s1.ExtraData[k] = v } - s1.VerificationError = verificationErr + if verificationErr != nil { + s1.SetVerificationError(verificationErr, resSecretMatch) + } } if !s1.Verified { @@ -286,7 +288,7 @@ func (s scanner) verifyMatch(ctx context.Context, resIDMatch, resSecretMatch str if strings.EqualFold(body.Error.Code, "InvalidClientTokenId") { return false, nil, nil } else { - return false, nil, fmt.Errorf("request to %v returned status %d with an unexpected reason (%s: %s)", res.Request.URL, res.StatusCode, body.Error.Code, body.Error.Message) + return false, nil, fmt.Errorf("request returned status %d with an unexpected reason (%s: %s)", res.StatusCode, body.Error.Code, body.Error.Message) } } else { return false, nil, fmt.Errorf("couldn't parse the sts response body (%v)", err) diff --git a/pkg/detectors/aws/aws_test.go b/pkg/detectors/aws/aws_test.go index 0d1ac08b5..8ab69552e 100644 --- a/pkg/detectors/aws/aws_test.go +++ b/pkg/detectors/aws/aws_test.go @@ -314,11 +314,11 @@ func TestAWS_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationError { - t.Fatalf("wantVerificationError %v, verification error = %v", tt.wantVerificationError, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationError { + t.Fatalf("wantVerificationError %v, verification error = %v", tt.wantVerificationError, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("AWS.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/awssessionkeys/awssessionkey.go b/pkg/detectors/awssessionkeys/awssessionkey.go index ba60843a8..16e87bc7a 100644 --- a/pkg/detectors/awssessionkeys/awssessionkey.go +++ b/pkg/detectors/awssessionkeys/awssessionkey.go @@ -134,12 +134,12 @@ func (s scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - verified, extraData, verificationErr := s.verifyMatch(ctx, resIDMatch, resSecretMatch, resSessionMatch, true) - s1.Verified = verified + isVerified, extraData, verificationErr := s.verifyMatch(ctx, resIDMatch, resSecretMatch, resSessionMatch, true) + s1.Verified = isVerified if extraData != nil { s1.ExtraData = extraData } - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resSecretMatch) } if !s1.Verified { diff --git a/pkg/detectors/azurebatch/azurebatch_test.go b/pkg/detectors/azurebatch/azurebatch_test.go index b7a356275..dc92a401b 100644 --- a/pkg/detectors/azurebatch/azurebatch_test.go +++ b/pkg/detectors/azurebatch/azurebatch_test.go @@ -100,11 +100,11 @@ func TestAzurebatch_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "Redacted", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "Redacted", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("AzureBatch.FromData() %s diff: (-got +want)\n%s", tt.name, diff) diff --git a/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go b/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go index 6698c338c..d29e81572 100644 --- a/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go +++ b/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go @@ -75,10 +75,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, password) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, username, password) } } diff --git a/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go b/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go index ede6611f7..8ed032f97 100644 --- a/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go +++ b/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go @@ -6,11 +6,12 @@ package azurecontainerregistry import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -28,7 +29,6 @@ func TestAzureContainerRegistry_FromChunk(t *testing.T) { password := testSecrets.MustGetField("AZURE_CR_PASSWORD") passwordInactive := testSecrets.MustGetField("AZURE_CR_PASSWORD_INACTIVE") - type args struct { ctx context.Context data []byte @@ -100,11 +100,11 @@ func TestAzureContainerRegistry_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw","Redacted", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "Redacted", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("AzureContainerRegistry.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/azurestorage/azurestorage.go b/pkg/detectors/azurestorage/azurestorage.go index d6f221529..4954208b4 100644 --- a/pkg/detectors/azurestorage/azurestorage.go +++ b/pkg/detectors/azurestorage/azurestorage.go @@ -79,10 +79,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result s1.Verified = true } else if res.StatusCode == 403 { } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, accountKey) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, accountKey) } } diff --git a/pkg/detectors/azurestorage/azurestorage_test.go b/pkg/detectors/azurestorage/azurestorage_test.go index 5ee8c9e0f..0b3c537b4 100644 --- a/pkg/detectors/azurestorage/azurestorage_test.go +++ b/pkg/detectors/azurestorage/azurestorage_test.go @@ -133,11 +133,11 @@ func TestAzurestorage_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Azurestorage.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/betterstack/betterstack.go b/pkg/detectors/betterstack/betterstack.go index 8036ec935..a95552b03 100644 --- a/pkg/detectors/betterstack/betterstack.go +++ b/pkg/detectors/betterstack/betterstack.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/betterstack/betterstack_test.go b/pkg/detectors/betterstack/betterstack_test.go index 8615bdf5f..4273df1f2 100644 --- a/pkg/detectors/betterstack/betterstack_test.go +++ b/pkg/detectors/betterstack/betterstack_test.go @@ -99,11 +99,11 @@ func TestBetterstack_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Betterstack.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/bitcoinaverage/bitcoinaverage.go b/pkg/detectors/bitcoinaverage/bitcoinaverage.go index bcbe1ce15..c278292a7 100644 --- a/pkg/detectors/bitcoinaverage/bitcoinaverage.go +++ b/pkg/detectors/bitcoinaverage/bitcoinaverage.go @@ -63,7 +63,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { resp := &response{} if err = json.NewDecoder(res.Body).Decode(resp); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if resp.Success { diff --git a/pkg/detectors/braintreepayments/braintreepayments.go b/pkg/detectors/braintreepayments/braintreepayments.go index 160094530..8c79d4284 100644 --- a/pkg/detectors/braintreepayments/braintreepayments.go +++ b/pkg/detectors/braintreepayments/braintreepayments.go @@ -68,7 +68,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result url := s.getBraintreeURL() isVerified, verificationErr := verifyBraintree(ctx, client, url, resIdMatch, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key diff --git a/pkg/detectors/braintreepayments/braintreepayments_test.go b/pkg/detectors/braintreepayments/braintreepayments_test.go index 0713f124b..db4c346dc 100644 --- a/pkg/detectors/braintreepayments/braintreepayments_test.go +++ b/pkg/detectors/braintreepayments/braintreepayments_test.go @@ -131,12 +131,12 @@ func TestBraintreePayments_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("BraintreePayments.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/browserstack/browserstack.go b/pkg/detectors/browserstack/browserstack.go index 54cdf1aa8..28d1f5d1c 100644 --- a/pkg/detectors/browserstack/browserstack.go +++ b/pkg/detectors/browserstack/browserstack.go @@ -68,7 +68,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result isVerified, verificationErr := verifyBrowserStackCredentials(ctx, client, resUserMatch, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. diff --git a/pkg/detectors/browserstack/browserstack_test.go b/pkg/detectors/browserstack/browserstack_test.go index e32b377f8..fbb3074ec 100644 --- a/pkg/detectors/browserstack/browserstack_test.go +++ b/pkg/detectors/browserstack/browserstack_test.go @@ -134,11 +134,11 @@ func TestBrowserStack_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("BrowserStack.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/budibase/budibase.go b/pkg/detectors/budibase/budibase.go index 47f06242a..22186b03e 100644 --- a/pkg/detectors/budibase/budibase.go +++ b/pkg/detectors/budibase/budibase.go @@ -21,7 +21,7 @@ var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() - + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"budibase"}) + `\b([a-f0-9]{32}-[a-f0-9]{78,80})\b`) ) @@ -54,7 +54,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client = defaultClient } - // URL: https://docs.budibase.com/reference/appsearch // API searches for the app with given name, since we only need to check api key, sending any appname will work. payload := strings.NewReader(`{"name":"qwerty"}`) @@ -74,10 +73,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/budibase/budibase_test.go b/pkg/detectors/budibase/budibase_test.go index 3cb2da696..ebd9ce6c8 100644 --- a/pkg/detectors/budibase/budibase_test.go +++ b/pkg/detectors/budibase/budibase_test.go @@ -66,13 +66,14 @@ func TestBudibase_FromChunk(t *testing.T) { data: []byte(fmt.Sprintf("You can find a budibase secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, - want: []detectors.Result{ - { + want: func() []detectors.Result { + r := detectors.Result{ DetectorType: detectorspb.DetectorType_Budibase, - Verified: false, - VerificationError: fmt.Errorf("unexpected HTTP response status 403"), - }, - }, + Verified: true, + } + r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 403")) + return []detectors.Result{r} + }(), wantErr: false, wantVerificationErr: true, }, @@ -88,7 +89,6 @@ func TestBudibase_FromChunk(t *testing.T) { wantErr: false, wantVerificationErr: false, }, - } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -101,11 +101,11 @@ func TestBudibase_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.want[i].VerificationError(), got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Budibase.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/cloudsmith/cloudsmith.go b/pkg/detectors/cloudsmith/cloudsmith.go index b7aab4f6d..44d012555 100644 --- a/pkg/detectors/cloudsmith/cloudsmith.go +++ b/pkg/detectors/cloudsmith/cloudsmith.go @@ -64,7 +64,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if r.Authenticated { diff --git a/pkg/detectors/coda/coda.go b/pkg/detectors/coda/coda.go index bec893080..109e7f56d 100644 --- a/pkg/detectors/coda/coda.go +++ b/pkg/detectors/coda/coda.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/coda/coda_test.go b/pkg/detectors/coda/coda_test.go index 0503f9e6b..e260351d3 100644 --- a/pkg/detectors/coda/coda_test.go +++ b/pkg/detectors/coda/coda_test.go @@ -6,11 +6,12 @@ package coda import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestCoda_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Coda.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/codeclimate/codeclimate.go b/pkg/detectors/codeclimate/codeclimate.go index aaf23de10..f5ee30fc5 100644 --- a/pkg/detectors/codeclimate/codeclimate.go +++ b/pkg/detectors/codeclimate/codeclimate.go @@ -67,7 +67,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if r.Data.Id != "" { diff --git a/pkg/detectors/coinbase_waas/coinbase_waas.go b/pkg/detectors/coinbase_waas/coinbase_waas.go index 7f31d754c..383ed8325 100644 --- a/pkg/detectors/coinbase_waas/coinbase_waas.go +++ b/pkg/detectors/coinbase_waas/coinbase_waas.go @@ -6,15 +6,16 @@ import ( "crypto/x509" "encoding/pem" "errors" + "net/http" + "regexp" + "strings" + "github.com/coinbase/waas-client-library-go/auth" "github.com/coinbase/waas-client-library-go/clients" v1clients "github.com/coinbase/waas-client-library-go/clients/v1" v1 "github.com/coinbase/waas-client-library-go/gen/go/coinbase/cloud/pools/v1" "github.com/google/uuid" "google.golang.org/api/googleapi" - "net/http" - "regexp" - "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" @@ -73,7 +74,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if verify { isVerified, verificationErr := s.verifyMatch(ctx, resKeyNameMatch, resPrivKeyMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resPrivKeyMatch) } results = append(results, s1) diff --git a/pkg/detectors/coinbase_waas/coinbase_waas_test.go b/pkg/detectors/coinbase_waas/coinbase_waas_test.go index 03c7c8542..2a493be5e 100644 --- a/pkg/detectors/coinbase_waas/coinbase_waas_test.go +++ b/pkg/detectors/coinbase_waas/coinbase_waas_test.go @@ -275,11 +275,11 @@ func TestCoinbaseWaaS_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Coinbasewaas.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/databrickstoken/databrickstoken.go b/pkg/detectors/databrickstoken/databrickstoken.go index 72e48e6b0..0429bdc74 100644 --- a/pkg/detectors/databrickstoken/databrickstoken.go +++ b/pkg/detectors/databrickstoken/databrickstoken.go @@ -12,7 +12,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct{ +type Scanner struct { client *http.Client } @@ -24,7 +24,7 @@ var ( // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. domain = regexp.MustCompile(`\b([a-z0-9-]+(?:\.[a-z0-9-]+)*\.(cloud\.databricks\.com|gcp\.databricks\.com|azurewebsites\.net))\b`) - keyPat = regexp.MustCompile(`\b(dapi[0-9a-f]{32})(-\d)?\b`) + keyPat = regexp.MustCompile(`\b(dapi[0-9a-f]{32})(-\d)?\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -54,10 +54,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if verify { client := s.client - if client == nil { - client = defaultClient - } - req, err := http.NewRequestWithContext(ctx, "GET", "https://" + resDomainMatch + "/api/2.0/clusters/list", nil) + if client == nil { + client = defaultClient + } + req, err := http.NewRequestWithContext(ctx, "GET", "https://"+resDomainMatch+"/api/2.0/clusters/list", nil) if err != nil { continue } @@ -70,10 +70,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 403 { // nothing to do here } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/databrickstoken/databrickstoken_test.go b/pkg/detectors/databrickstoken/databrickstoken_test.go index 92911f9d0..80274bdd8 100644 --- a/pkg/detectors/databrickstoken/databrickstoken_test.go +++ b/pkg/detectors/databrickstoken/databrickstoken_test.go @@ -6,11 +6,12 @@ package databrickstoken import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -133,11 +134,11 @@ func TestDatabricksToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("DatabricksToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/deno/denodeploy.go b/pkg/detectors/deno/denodeploy.go index 213752553..5fc20bb45 100644 --- a/pkg/detectors/deno/denodeploy.go +++ b/pkg/detectors/deno/denodeploy.go @@ -4,12 +4,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "io" "net/http" "regexp" + + "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 { @@ -68,12 +69,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result body, err := io.ReadAll(res.Body) if err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, token) } else { var user userResponse if err := json.Unmarshal(body, &user); err != nil { fmt.Printf("Unmarshal error: %v\n", err) - s1.VerificationError = err + s1.SetVerificationError(err, token) } else { s1.ExtraData = map[string]string{ "login": user.Login, @@ -83,10 +84,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, token) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, token) } } diff --git a/pkg/detectors/deno/denodeploy_test.go b/pkg/detectors/deno/denodeploy_test.go index ba8197bb8..4b6189ba1 100644 --- a/pkg/detectors/deno/denodeploy_test.go +++ b/pkg/detectors/deno/denodeploy_test.go @@ -226,11 +226,11 @@ func TestDenoDeploy_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError", "ExtraData") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError", "ExtraData") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Denodeploy.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/detectors.go b/pkg/detectors/detectors.go index 9f7cd04b3..ba50c9fbc 100644 --- a/pkg/detectors/detectors.go +++ b/pkg/detectors/detectors.go @@ -8,6 +8,8 @@ import ( "strings" "unicode" + "errors" + "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb" @@ -59,7 +61,43 @@ type Result struct { // This field should only be populated if the verification process itself failed in a way that provides no // information about the verification status of the candidate secret, such as if the verification request timed out. - VerificationError error + verificationError error +} + +// SetVerificationError is the only way to set a verification error. Any sensetive values should be passed-in as secrets to be redacted. +func (r *Result) SetVerificationError(err error, secrets ...string) { + if err != nil { + r.verificationError = redactSecrets(err, secrets...) + } +} + +// Public accessors for the fields could also be provided if needed. +func (r *Result) VerificationError() error { + return r.verificationError +} + +// redactSecrets replaces all instances of the given secrets with [REDACTED] in the error message. +func redactSecrets(err error, secrets ...string) error { + lastErr := unwrapToLast(err) + errStr := lastErr.Error() + for _, secret := range secrets { + errStr = strings.Replace(errStr, secret, "[REDACTED]", -1) + } + return errors.New(errStr) +} + +// unwrapToLast returns the last error in the chain of errors. +// This is added to exclude non-essential details (like URLs) for brevity and security. +// Also helps us optimize performance in redaction and enhance log clarity. +func unwrapToLast(err error) error { + for { + unwrapped := errors.Unwrap(err) + if unwrapped == nil { + // We've reached the last error in the chain + return err + } + err = unwrapped + } } type ResultWithMetadata struct { diff --git a/pkg/detectors/eventbrite/eventbrite.go b/pkg/detectors/eventbrite/eventbrite.go index 821596371..43f2decc1 100644 --- a/pkg/detectors/eventbrite/eventbrite.go +++ b/pkg/detectors/eventbrite/eventbrite.go @@ -65,10 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/eventbrite/eventbrite_test.go b/pkg/detectors/eventbrite/eventbrite_test.go index d5eddbd5e..5ac973112 100644 --- a/pkg/detectors/eventbrite/eventbrite_test.go +++ b/pkg/detectors/eventbrite/eventbrite_test.go @@ -6,11 +6,12 @@ package eventbrite import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestEventbrite_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Eventbrite.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/falsepositives.go b/pkg/detectors/falsepositives.go index 2a0b37f7b..0d4b31367 100644 --- a/pkg/detectors/falsepositives.go +++ b/pkg/detectors/falsepositives.go @@ -107,7 +107,7 @@ func StringShannonEntropy(input string) float64 { func FilterResultsWithEntropy(results []Result, entropy float64) []Result { var filteredResults []Result for _, result := range results { - if !result.Verified && result.VerificationError == nil { + if !result.Verified && result.VerificationError() == nil { if result.RawV2 != nil { if StringShannonEntropy(string(result.RawV2)) >= entropy { filteredResults = append(filteredResults, result) diff --git a/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken.go b/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken.go index 59dda2dfd..4968b1e1e 100644 --- a/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken.go +++ b/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken.go @@ -68,10 +68,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode != 403 { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken_test.go b/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken_test.go index 22453c032..1162ad3b8 100644 --- a/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken_test.go +++ b/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken_test.go @@ -128,11 +128,11 @@ func TestFigmaPersonalAccessToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("FigmaPersonalAccessToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2.go b/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2.go index ec7c765e2..0fa7b4ecc 100644 --- a/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2.go +++ b/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2.go @@ -67,10 +67,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode != 403 { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2_test.go b/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2_test.go index 7c8bc4e34..a3604ce1b 100644 --- a/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2_test.go +++ b/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2_test.go @@ -128,11 +128,11 @@ func TestFigmaPersonalAccessToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("FigmaPersonalAccessToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/formsite/formsite_test.go b/pkg/detectors/formsite/formsite_test.go index 376b0a9af..4f8bdf634 100644 --- a/pkg/detectors/formsite/formsite_test.go +++ b/pkg/detectors/formsite/formsite_test.go @@ -50,6 +50,10 @@ func TestFormsite_FromChunk(t *testing.T) { DetectorType: detectorspb.DetectorType_Formsite, Verified: true, }, + { + DetectorType: detectorspb.DetectorType_Formsite, + Verified: false, + }, }, wantErr: false, }, @@ -66,6 +70,10 @@ func TestFormsite_FromChunk(t *testing.T) { DetectorType: detectorspb.DetectorType_Formsite, Verified: false, }, + { + DetectorType: detectorspb.DetectorType_Formsite, + Verified: false, + }, }, wantErr: false, }, diff --git a/pkg/detectors/ftp/ftp.go b/pkg/detectors/ftp/ftp.go index c55cd541d..8d77f12f1 100644 --- a/pkg/detectors/ftp/ftp.go +++ b/pkg/detectors/ftp/ftp.go @@ -70,7 +70,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result rawURL.Path = "" redact := strings.TrimSpace(strings.Replace(rawURL.String(), password, "********", -1)) - r := detectors.Result{ + s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FTP, Raw: []byte(rawURL.String()), Redacted: redact, @@ -82,24 +82,24 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result timeout = defaultVerificationTimeout } verificationErr := verifyFTP(timeout, parsedURL) - r.Verified = verificationErr == nil + s1.Verified = verificationErr == nil if !isErrDeterminate(verificationErr) { - r.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, password) } } - if !r.Verified { + if !s1.Verified { // Skip unverified findings where the password starts with a `$` - it's almost certainly a variable. if strings.HasPrefix(password, "$") { continue } } - if detectors.IsKnownFalsePositive(string(r.Raw), []detectors.FalsePositive{"@ftp.freebsd.org"}, false) { + if detectors.IsKnownFalsePositive(string(s1.Raw), []detectors.FalsePositive{"@ftp.freebsd.org"}, false) { continue } - results = append(results, r) + results = append(results, s1) } return results, nil diff --git a/pkg/detectors/ftp/ftp_test.go b/pkg/detectors/ftp/ftp_test.go index 434f6fb7b..80982dc07 100644 --- a/pkg/detectors/ftp/ftp_test.go +++ b/pkg/detectors/ftp/ftp_test.go @@ -5,11 +5,12 @@ package ftp import ( "context" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) @@ -136,11 +137,11 @@ func TestFTP_FromChunk(t *testing.T) { for i := range got { got[i].Raw = nil - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("FTP.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/github/github.go b/pkg/detectors/github/github.go index c0d07e72d..ad8346cd1 100644 --- a/pkg/detectors/github/github.go +++ b/pkg/detectors/github/github.go @@ -101,7 +101,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } } } else { - s1.VerificationError = err + s1.SetVerificationError(err, token) } } } diff --git a/pkg/detectors/gitlab/gitlab.go b/pkg/detectors/gitlab/gitlab.go index 1188f3df4..e679959e2 100644 --- a/pkg/detectors/gitlab/gitlab.go +++ b/pkg/detectors/gitlab/gitlab.go @@ -62,7 +62,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if verify { isVerified, verificationErr := s.verifyGitlab(ctx, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/gitlab/gitlab_v1_test.go b/pkg/detectors/gitlab/gitlab_v1_test.go index 60ea59516..05c2f8ffa 100644 --- a/pkg/detectors/gitlab/gitlab_v1_test.go +++ b/pkg/detectors/gitlab/gitlab_v1_test.go @@ -160,11 +160,11 @@ func TestGitlab_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/gitlab/gitlab_v2_test.go b/pkg/detectors/gitlab/gitlab_v2_test.go index 1c5f3a030..13063e1a5 100644 --- a/pkg/detectors/gitlab/gitlab_v2_test.go +++ b/pkg/detectors/gitlab/gitlab_v2_test.go @@ -84,11 +84,11 @@ func TestGitlab_FromChunk_WithV2Secrets(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/gitlabv2/gitlab_v1_test.go b/pkg/detectors/gitlabv2/gitlab_v1_test.go index 2c91a85f3..930d6a848 100644 --- a/pkg/detectors/gitlabv2/gitlab_v1_test.go +++ b/pkg/detectors/gitlabv2/gitlab_v1_test.go @@ -95,11 +95,11 @@ func TestGitlabV2_FromChunk_WithV1Secrets(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/gitlabv2/gitlab_v2.go b/pkg/detectors/gitlabv2/gitlab_v2.go index 3084efa62..4c8c26150 100644 --- a/pkg/detectors/gitlabv2/gitlab_v2.go +++ b/pkg/detectors/gitlabv2/gitlab_v2.go @@ -58,7 +58,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if verify { isVerified, verificationErr := s.verifyGitlab(ctx, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/gitlabv2/gitlab_v2_test.go b/pkg/detectors/gitlabv2/gitlab_v2_test.go index d074c6fb0..ee6b8591e 100644 --- a/pkg/detectors/gitlabv2/gitlab_v2_test.go +++ b/pkg/detectors/gitlabv2/gitlab_v2_test.go @@ -144,11 +144,11 @@ func TestGitlabV2_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/grafana/grafana.go b/pkg/detectors/grafana/grafana.go index 696ede60b..5bd7676ba 100644 --- a/pkg/detectors/grafana/grafana.go +++ b/pkg/detectors/grafana/grafana.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/grafana/grafana_test.go b/pkg/detectors/grafana/grafana_test.go index 3079553ce..4e6002a83 100644 --- a/pkg/detectors/grafana/grafana_test.go +++ b/pkg/detectors/grafana/grafana_test.go @@ -6,11 +6,12 @@ package grafana import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestGrafana_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Grafana.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount.go b/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount.go index 0d002fb40..94c1771cf 100644 --- a/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount.go +++ b/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount.go @@ -22,7 +22,7 @@ var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. - keyPat = regexp.MustCompile(`\b(glsa_[0-9a-zA-Z_]{41})\b`) + keyPat = regexp.MustCompile(`\b(glsa_[0-9a-zA-Z_]{41})\b`) domainPat = regexp.MustCompile(`\b([a-zA-Z0-9-]+\.grafana\.net)\b`) ) @@ -45,16 +45,16 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } key := strings.TrimSpace(match[1]) - for _, domainMatch := range domainMatches { - if len(domainMatch) != 2 { - continue - } + for _, domainMatch := range domainMatches { + if len(domainMatch) != 2 { + continue + } domainRes := strings.TrimSpace(domainMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GrafanaServiceAccount, Raw: []byte(key), - RawV2: []byte(fmt.Sprintf("%s:%s", domainRes, key)), + RawV2: []byte(fmt.Sprintf("%s:%s", domainRes, key)), } if verify { @@ -75,10 +75,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, key) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, key) } } diff --git a/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount_test.go b/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount_test.go index 48818f580..c3572d809 100644 --- a/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount_test.go +++ b/pkg/detectors/grafanaserviceaccount/grafanaserviceaccount_test.go @@ -6,11 +6,12 @@ package grafanaserviceaccount import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -133,11 +134,11 @@ func TestGrafanaServiceAccount_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("GrafanaServiceAccount.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/huggingface/huggingface.go b/pkg/detectors/huggingface/huggingface.go index 694da79f9..a464e0098 100644 --- a/pkg/detectors/huggingface/huggingface.go +++ b/pkg/detectors/huggingface/huggingface.go @@ -68,10 +68,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/huggingface/huggingface_test.go b/pkg/detectors/huggingface/huggingface_test.go index fbdb94244..08bcf1f31 100644 --- a/pkg/detectors/huggingface/huggingface_test.go +++ b/pkg/detectors/huggingface/huggingface_test.go @@ -6,11 +6,12 @@ package huggingface import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestHuggingface_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Huggingface.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/instamojo/instamojo.go b/pkg/detectors/instamojo/instamojo.go index 4ada2694a..42ba954b5 100644 --- a/pkg/detectors/instamojo/instamojo.go +++ b/pkg/detectors/instamojo/instamojo.go @@ -24,7 +24,7 @@ var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. //KeyPat is client_id - keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"instamojo"}) + `\b([0-9a-zA-Z]{40})\b`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"instamojo"}) + `\b([0-9a-zA-Z]{40})\b`) //Secretpat is Client_secret secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"instamojo"}) + `\b([0-9a-zA-Z]{128})\b`) ) @@ -82,10 +82,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if (res.StatusCode >= 200 && res.StatusCode < 300) && strings.Contains(body, "access_token") { s1.Verified = true } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resSecret) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resSecret) } } diff --git a/pkg/detectors/instamojo/instamojo_test.go b/pkg/detectors/instamojo/instamojo_test.go index d6e6fdb36..81bacaaac 100644 --- a/pkg/detectors/instamojo/instamojo_test.go +++ b/pkg/detectors/instamojo/instamojo_test.go @@ -68,7 +68,6 @@ func TestInstamojo_FromChunk(t *testing.T) { want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Instamojo, - VerificationError: fmt.Errorf("unexpected HTTP response status 401"), }, }, wantErr: false, @@ -82,7 +81,7 @@ func TestInstamojo_FromChunk(t *testing.T) { data: []byte("You cannot find the secret within"), verify: true, }, - want: nil, + want: nil, wantErr: false, wantVerificationErr: true, }, diff --git a/pkg/detectors/ip2location/ip2location.go b/pkg/detectors/ip2location/ip2location.go index 9503b51e0..04537e43a 100644 --- a/pkg/detectors/ip2location/ip2location.go +++ b/pkg/detectors/ip2location/ip2location.go @@ -12,7 +12,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct {} +type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) @@ -47,8 +47,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ip2location.io/?key=" + resMatch, nil) - + req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ip2location.io/?key="+resMatch, nil) + if err != nil { continue } @@ -60,10 +60,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { @@ -79,6 +80,3 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ip2location } - - - diff --git a/pkg/detectors/ip2location/ip2location_test.go b/pkg/detectors/ip2location/ip2location_test.go index caf61edbd..2e40cdefd 100644 --- a/pkg/detectors/ip2location/ip2location_test.go +++ b/pkg/detectors/ip2location/ip2location_test.go @@ -99,11 +99,11 @@ func TestIp2location_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.want[i].VerificationError(), got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ip2location.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/ipinfo/ipinfo.go b/pkg/detectors/ipinfo/ipinfo.go index 4b7024b33..3b1761c9c 100644 --- a/pkg/detectors/ipinfo/ipinfo.go +++ b/pkg/detectors/ipinfo/ipinfo.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 403 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/ipinfo/ipinfo_test.go b/pkg/detectors/ipinfo/ipinfo_test.go index d1c523fbe..abdcac517 100644 --- a/pkg/detectors/ipinfo/ipinfo_test.go +++ b/pkg/detectors/ipinfo/ipinfo_test.go @@ -6,11 +6,12 @@ package ipinfo import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestIpinfo_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ipinfo.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/jdbc/jdbc.go b/pkg/detectors/jdbc/jdbc.go index 88b1e07f7..cc69851d4 100644 --- a/pkg/detectors/jdbc/jdbc.go +++ b/pkg/detectors/jdbc/jdbc.go @@ -92,7 +92,8 @@ matchLoop: // behavior before tri-state verification was introduced and preserving it allows us to gradually migrate // detectors to use tri-state verification. if pingRes.err != nil && !pingRes.determinate { - s.VerificationError = pingRes.err + err = pingRes.err + s.SetVerificationError(err, jdbcConn) } // TODO: specialized redaction } diff --git a/pkg/detectors/jiratoken/jiratoken.go b/pkg/detectors/jiratoken/jiratoken.go index 3e495773c..ddae96f46 100644 --- a/pkg/detectors/jiratoken/jiratoken.go +++ b/pkg/detectors/jiratoken/jiratoken.go @@ -81,7 +81,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyJiratoken(ctx, client, resEmail, resDomain, resToken) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resToken) } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/jiratoken/jiratoken_test.go b/pkg/detectors/jiratoken/jiratoken_test.go index d733b0b92..b9f6579f4 100644 --- a/pkg/detectors/jiratoken/jiratoken_test.go +++ b/pkg/detectors/jiratoken/jiratoken_test.go @@ -134,11 +134,11 @@ func TestJiraToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("JiraToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/jiratoken_v2/jiratoken_v2.go b/pkg/detectors/jiratoken_v2/jiratoken_v2.go index 57233b666..5753dc10a 100644 --- a/pkg/detectors/jiratoken_v2/jiratoken_v2.go +++ b/pkg/detectors/jiratoken_v2/jiratoken_v2.go @@ -79,7 +79,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyJiratoken(ctx, client, resEmail, resDomain, resToken) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resToken) } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/jiratoken_v2/jiratoken_v2_test.go b/pkg/detectors/jiratoken_v2/jiratoken_v2_test.go index bdfd2454c..3fec014b7 100644 --- a/pkg/detectors/jiratoken_v2/jiratoken_v2_test.go +++ b/pkg/detectors/jiratoken_v2/jiratoken_v2_test.go @@ -134,11 +134,11 @@ func TestJiraToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("JiraToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/klaviyo/klaviyo.go b/pkg/detectors/klaviyo/klaviyo.go index 2cbd94e77..51f17fc03 100644 --- a/pkg/detectors/klaviyo/klaviyo.go +++ b/pkg/detectors/klaviyo/klaviyo.go @@ -88,18 +88,19 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result // Thus, the key is verified, but it is up to the user to determine what scopes the key has. s1.Verified = true } else { - s1.VerificationError = fmt.Errorf("errors expected") + s1.SetVerificationError(fmt.Errorf("errors expected"), resMatch) } } else { - s1.VerificationError = fmt.Errorf("unexpected API JSON response") + s1.SetVerificationError(fmt.Errorf("unexpected API JSON response"), resMatch) } } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/klaviyo/klaviyo_test.go b/pkg/detectors/klaviyo/klaviyo_test.go index effeabcf6..1ade9e751 100644 --- a/pkg/detectors/klaviyo/klaviyo_test.go +++ b/pkg/detectors/klaviyo/klaviyo_test.go @@ -6,11 +6,12 @@ package klaviyo import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestKlaviyo_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Klaviyo.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/launchdarkly/launchdarkly.go b/pkg/detectors/launchdarkly/launchdarkly.go index debded7dc..bcb2c95f9 100644 --- a/pkg/detectors/launchdarkly/launchdarkly.go +++ b/pkg/detectors/launchdarkly/launchdarkly.go @@ -79,10 +79,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // 401 is expected for an invalid token, so there is nothing to do here. } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } else { // This is a server SDK key. Try to initialize using the SDK. @@ -94,7 +95,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else { // If the error isn't nil or known, then this is likely a timeout error: ldclient.ErrInitializationTimeout // But any other error here means we don't know if this key is valid. - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } } diff --git a/pkg/detectors/ldap/ldap.go b/pkg/detectors/ldap/ldap.go index fb5fd15df..996ed0532 100644 --- a/pkg/detectors/ldap/ldap.go +++ b/pkg/detectors/ldap/ldap.go @@ -68,7 +68,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result verificationErr := verifyLDAP(username[1], password[1], ldapURL) s1.Verified = verificationErr == nil if !isErrDeterminate(verificationErr) { - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, password[1]) } } @@ -99,7 +99,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result s1.Verified = verificationError == nil if !isErrDeterminate(verificationError) { - s1.VerificationError = verificationError + s1.SetVerificationError(verificationError, password) } } diff --git a/pkg/detectors/ldap/ldap_integration_test.go b/pkg/detectors/ldap/ldap_integration_test.go index 8b85d382b..0927fcce5 100644 --- a/pkg/detectors/ldap/ldap_integration_test.go +++ b/pkg/detectors/ldap/ldap_integration_test.go @@ -7,14 +7,15 @@ import ( "bytes" "context" "errors" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "os" "os/exec" "strings" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" @@ -198,11 +199,11 @@ func TestLdap_Integration_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ldap.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/lemonsqueezy/lemonsqueezy.go b/pkg/detectors/lemonsqueezy/lemonsqueezy.go index b1cd1da4a..c6914ffa9 100644 --- a/pkg/detectors/lemonsqueezy/lemonsqueezy.go +++ b/pkg/detectors/lemonsqueezy/lemonsqueezy.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/lemonsqueezy/lemonsqueezy_test.go b/pkg/detectors/lemonsqueezy/lemonsqueezy_test.go index 058e069b9..4152f7e1f 100644 --- a/pkg/detectors/lemonsqueezy/lemonsqueezy_test.go +++ b/pkg/detectors/lemonsqueezy/lemonsqueezy_test.go @@ -99,11 +99,11 @@ func TestLemonsqueezy_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Lemonsqueezy.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/liveagent/liveagent.go b/pkg/detectors/liveagent/liveagent.go index 9dfc04df8..e12ca56a3 100644 --- a/pkg/detectors/liveagent/liveagent.go +++ b/pkg/detectors/liveagent/liveagent.go @@ -76,7 +76,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 403 { var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } diff --git a/pkg/detectors/loggly/loggly.go b/pkg/detectors/loggly/loggly.go index 1c7bcd606..3ef5bf012 100644 --- a/pkg/detectors/loggly/loggly.go +++ b/pkg/detectors/loggly/loggly.go @@ -1,15 +1,15 @@ package loggly import ( - "context" - "fmt" - "net/http" - "regexp" - "strings" + "context" + "fmt" + "net/http" + "regexp" + "strings" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" + "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 { @@ -20,82 +20,82 @@ type Scanner struct { var _ detectors.Detector = (*Scanner)(nil) var ( - defaultClient = common.SaneHttpClient() - // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. - domainPat = regexp.MustCompile(`\b([a-zA-Z0-9-]+\.loggly\.com)\b`) - keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"loggly"}) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\b`) + defaultClient = common.SaneHttpClient() + // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. + domainPat = regexp.MustCompile(`\b([a-zA-Z0-9-]+\.loggly\.com)\b`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"loggly"}) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\b`) ) // Keywords are used for efficiently pre-filtering chunks. // Use identifiers in the secret preferably, or the provider name. func (s Scanner) Keywords() []string { - return []string{"loggly"} + return []string{"loggly"} } // FromData will find and optionally verify Loggly secrets in a given set of bytes. func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { - dataStr := string(data) + dataStr := string(data) - keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) - domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) + keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) + domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) - for _, match := range keyMatches { - if len(match) != 2 { - continue - } - key := strings.TrimSpace(match[1]) + for _, match := range keyMatches { + if len(match) != 2 { + continue + } + key := strings.TrimSpace(match[1]) - for _, domainMatch := range domainMatches { - if len(domainMatch) != 2 { - continue - } + for _, domainMatch := range domainMatches { + if len(domainMatch) != 2 { + continue + } - domainRes := strings.TrimSpace(domainMatch[1]) + domainRes := strings.TrimSpace(domainMatch[1]) - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_Loggly, - Raw: []byte(key), - RawV2: []byte(fmt.Sprintf("%s:%s", domainRes, key)), + s1 := detectors.Result{ + DetectorType: detectorspb.DetectorType_Loggly, + Raw: []byte(key), + RawV2: []byte(fmt.Sprintf("%s:%s", domainRes, key)), + } - } + if verify { + client := s.client + if client == nil { + client = defaultClient + } + req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domainRes+"/apiv2/customer", nil) + if err != nil { + continue + } + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key)) + res, err := client.Do(req) + if err == nil { + defer res.Body.Close() + if res.StatusCode >= 200 && res.StatusCode < 300 { + s1.Verified = true + } else if res.StatusCode == 401 { + // The secret is determinately not verified (nothing to do) + } else { + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, key) + } + } else { + s1.SetVerificationError(err, key) + } + } - if verify { - client := s.client - if client == nil { - client = defaultClient - } - req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domainRes+"/apiv2/customer", nil) - if err != nil { - continue - } - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key)) - res, err := client.Do(req) - if err == nil { - defer res.Body.Close() - if res.StatusCode >= 200 && res.StatusCode < 300 { - s1.Verified = true - } else if res.StatusCode == 401 { - // The secret is determinately not verified (nothing to do) - } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) - } - } else { - s1.VerificationError = err - } - } + // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. + if !s1.Verified && detectors.IsKnownFalsePositive(key, detectors.DefaultFalsePositives, true) { + continue + } - // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. - if !s1.Verified && detectors.IsKnownFalsePositive(key, detectors.DefaultFalsePositives, true) { - continue - } + results = append(results, s1) + } + } - results = append(results, s1) - } - } - - return results, nil + return results, nil } func (s Scanner) Type() detectorspb.DetectorType { - return detectorspb.DetectorType_Loggly + return detectorspb.DetectorType_Loggly } diff --git a/pkg/detectors/loggly/loggly_test.go b/pkg/detectors/loggly/loggly_test.go index aa1977482..7c37d6b2f 100644 --- a/pkg/detectors/loggly/loggly_test.go +++ b/pkg/detectors/loggly/loggly_test.go @@ -6,11 +6,12 @@ package loggly import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -133,11 +134,11 @@ func TestLoggly_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Loggly.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/logzio/logzio.go b/pkg/detectors/logzio/logzio.go index 572fe5269..cc77dd4cf 100644 --- a/pkg/detectors/logzio/logzio.go +++ b/pkg/detectors/logzio/logzio.go @@ -67,10 +67,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/logzio/logzio_test.go b/pkg/detectors/logzio/logzio_test.go index d754d7f07..a4f6c53c3 100644 --- a/pkg/detectors/logzio/logzio_test.go +++ b/pkg/detectors/logzio/logzio_test.go @@ -6,11 +6,12 @@ package logzio import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestLogzIO_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("LogzIO.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/meaningcloud/meaningcloud.go b/pkg/detectors/meaningcloud/meaningcloud.go index ca1814de5..a9eb84e4a 100644 --- a/pkg/detectors/meaningcloud/meaningcloud.go +++ b/pkg/detectors/meaningcloud/meaningcloud.go @@ -85,7 +85,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if r.DeepTime > 0 { diff --git a/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook.go b/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook.go index 5047d0bb0..fd5f5c726 100644 --- a/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook.go +++ b/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook.go @@ -62,7 +62,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result isVerified, verificationErr := verifyWebhook(ctx, client, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, false) { diff --git a/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook_test.go b/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook_test.go index 0db3ff2b1..6aaa243f3 100644 --- a/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook_test.go +++ b/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook_test.go @@ -146,12 +146,12 @@ func TestMicrosoftTeamsWebhook_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Errorf("MicrosoftTeamsWebhook.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantVerificationErr) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Errorf("MicrosoftTeamsWebhook.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError(), tt.wantVerificationErr) return } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("MicrosoftTeamsWebhook.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/mongodb/mongodb.go b/pkg/detectors/mongodb/mongodb.go index a98d4dbfe..346db1998 100644 --- a/pkg/detectors/mongodb/mongodb.go +++ b/pkg/detectors/mongodb/mongodb.go @@ -61,7 +61,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result err := verifyUri(resMatch, timeout) s1.Verified = err == nil if !isErrDeterminate(err) { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } results = append(results, s1) diff --git a/pkg/detectors/mongodb/mongodb_test.go b/pkg/detectors/mongodb/mongodb_test.go index cf5178755..530558f9b 100644 --- a/pkg/detectors/mongodb/mongodb_test.go +++ b/pkg/detectors/mongodb/mongodb_test.go @@ -132,11 +132,11 @@ func TestMongoDB_FromChunk(t *testing.T) { t.Fatalf("no raw secret present: \n %+v", got[i]) } got[i].Raw = nil - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationErr = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationErr = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "verificationError") if diff := cmp.Diff(tt.want, got, ignoreOpts); diff != "" { t.Errorf("MongoDB.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/myfreshworks/myfreshworks.go b/pkg/detectors/myfreshworks/myfreshworks.go index e5cc2bcd7..2673dfcb4 100644 --- a/pkg/detectors/myfreshworks/myfreshworks.go +++ b/pkg/detectors/myfreshworks/myfreshworks.go @@ -59,7 +59,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyMyfreshworks(ctx, client, resMatch, resIdMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. diff --git a/pkg/detectors/myfreshworks/myfreshworks_test.go b/pkg/detectors/myfreshworks/myfreshworks_test.go index f260a76fc..f3e2528e0 100644 --- a/pkg/detectors/myfreshworks/myfreshworks_test.go +++ b/pkg/detectors/myfreshworks/myfreshworks_test.go @@ -131,7 +131,7 @@ func TestMyfreshworks_FromChunk(t *testing.T) { t.Fatalf("no raw secret present: \n %+v", got[i]) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Myfreshworks.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/ngrok/ngrok.go b/pkg/detectors/ngrok/ngrok.go index eb599f3e0..5cd3941d3 100644 --- a/pkg/detectors/ngrok/ngrok.go +++ b/pkg/detectors/ngrok/ngrok.go @@ -16,12 +16,11 @@ type Scanner struct { client *http.Client } - var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() - + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ngrok"}) + `\b2[a-zA-Z0-9]{26}_\d[a-zA-Z0-9]{20}\b`) ) @@ -64,10 +63,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/ngrok/ngrok_test.go b/pkg/detectors/ngrok/ngrok_test.go index 9e6faebb6..234785e80 100644 --- a/pkg/detectors/ngrok/ngrok_test.go +++ b/pkg/detectors/ngrok/ngrok_test.go @@ -99,11 +99,11 @@ func TestNgrok_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ngrok.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/notion/notion.go b/pkg/detectors/notion/notion.go index 9edb7a84e..2496c55ac 100644 --- a/pkg/detectors/notion/notion.go +++ b/pkg/detectors/notion/notion.go @@ -73,7 +73,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/openvpn/openvpn.go b/pkg/detectors/openvpn/openvpn.go index 7a16304b2..ecb602af2 100644 --- a/pkg/detectors/openvpn/openvpn.go +++ b/pkg/detectors/openvpn/openvpn.go @@ -25,8 +25,7 @@ var ( clientIDPat = regexp.MustCompile(detectors.PrefixRegex([]string{"openvpn"}) + `\b([A-Za-z0-9-]{3,40}\.[A-Za-z0-9-]{3,40})\b`) clientSecretPat = regexp.MustCompile(`\b([a-zA-Z0-9_-]{64,})\b`) - domainPat = regexp.MustCompile(`\b(https?://[A-Za-z0-9-]+\.api\.openvpn\.com)\b`) - + domainPat = regexp.MustCompile(`\b(https?://[A-Za-z0-9-]+\.api\.openvpn\.com)\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -42,7 +41,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) clientIdMatches := clientIDPat.FindAllStringSubmatch(dataStr, -1) clientSecretMatches := clientSecretPat.FindAllStringSubmatch(dataStr, -1) - + for _, clientIdMatch := range clientIdMatches { clientIDRes := strings.TrimSpace(clientIdMatch[1]) for _, clientSecretMatch := range clientSecretMatches { @@ -65,7 +64,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result payload := strings.NewReader("grant_type=client_credentials") // OpenVPN API is in beta, We'll have to update the API endpoint once // Docs: https://openvpn.net/cloud-docs/developer/creating-api-credentials.html - req, err := http.NewRequestWithContext(ctx, "POST", domainRes + "/api/beta/oauth/token", payload) + req, err := http.NewRequestWithContext(ctx, "POST", domainRes+"/api/beta/oauth/token", payload) if err != nil { continue } @@ -83,10 +82,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, clientSecretRes) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, clientSecretRes) } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. @@ -97,8 +97,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } } - - } return results, nil diff --git a/pkg/detectors/openvpn/openvpn_test.go b/pkg/detectors/openvpn/openvpn_test.go index 03a3547bb..b1521d2cf 100644 --- a/pkg/detectors/openvpn/openvpn_test.go +++ b/pkg/detectors/openvpn/openvpn_test.go @@ -104,11 +104,11 @@ func TestOpenvpn_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Openvpn.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/overloop/overloop.go b/pkg/detectors/overloop/overloop.go index 2b39fc38f..e7b82a2c6 100644 --- a/pkg/detectors/overloop/overloop.go +++ b/pkg/detectors/overloop/overloop.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/overloop/overloop_test.go b/pkg/detectors/overloop/overloop_test.go index e4b2b3893..7b5d499fd 100644 --- a/pkg/detectors/overloop/overloop_test.go +++ b/pkg/detectors/overloop/overloop_test.go @@ -6,11 +6,12 @@ package overloop import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestOverloop_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Overloop.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/pagerdutyapikey/pagerdutyapikey.go b/pkg/detectors/pagerdutyapikey/pagerdutyapikey.go index f3dbcd875..2b5c2b709 100644 --- a/pkg/detectors/pagerdutyapikey/pagerdutyapikey.go +++ b/pkg/detectors/pagerdutyapikey/pagerdutyapikey.go @@ -53,7 +53,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyPagerdutyapikey(ctx, client, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/pagerdutyapikey/pagerdutyapikey_test.go b/pkg/detectors/pagerdutyapikey/pagerdutyapikey_test.go index 5f16180ed..f3a1ff55c 100644 --- a/pkg/detectors/pagerdutyapikey/pagerdutyapikey_test.go +++ b/pkg/detectors/pagerdutyapikey/pagerdutyapikey_test.go @@ -128,11 +128,11 @@ func TestPagerDutyApiKey_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Errorf("PagerDutyApiKey.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantVerificationErr) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Errorf("PagerDutyApiKey.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError(), tt.wantVerificationErr) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("PagerDutyApiKey.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/planetscale/planetscale.go b/pkg/detectors/planetscale/planetscale.go index 8fe238fe7..9de06a114 100644 --- a/pkg/detectors/planetscale/planetscale.go +++ b/pkg/detectors/planetscale/planetscale.go @@ -69,10 +69,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result // The secret is determinately not verified s1.Verified = false } else { - s1.VerificationError = fmt.Errorf("unexpected status code %d", res.StatusCode) + err = fmt.Errorf("unexpected status code %d", res.StatusCode) + s1.SetVerificationError(err, password) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, password) } } diff --git a/pkg/detectors/planetscale/planetscale_test.go b/pkg/detectors/planetscale/planetscale_test.go index e1dbdf391..315f8f97b 100644 --- a/pkg/detectors/planetscale/planetscale_test.go +++ b/pkg/detectors/planetscale/planetscale_test.go @@ -135,11 +135,11 @@ func TestPlanetscale_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Planetscale.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/planetscaledb/planetscaledb.go b/pkg/detectors/planetscaledb/planetscaledb.go index 63355c958..57b86828a 100644 --- a/pkg/detectors/planetscaledb/planetscaledb.go +++ b/pkg/detectors/planetscaledb/planetscaledb.go @@ -55,13 +55,13 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } db, err := sql.Open("mysql", cfg.FormatDSN()) if err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, password[0]) } else { err = db.PingContext(ctx) if err == nil { s1.Verified = true } else { - s1.VerificationError = err + s1.SetVerificationError(err, password[0]) } db.Close() } diff --git a/pkg/detectors/planetscaledb/planetscaledb_test.go b/pkg/detectors/planetscaledb/planetscaledb_test.go index e831a9fb0..57a1ae3c4 100644 --- a/pkg/detectors/planetscaledb/planetscaledb_test.go +++ b/pkg/detectors/planetscaledb/planetscaledb_test.go @@ -100,11 +100,11 @@ func TestPlanetscaledb_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Planetscaledb.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/portainer/portainer.go b/pkg/detectors/portainer/portainer.go index 433f90336..ec769fe14 100644 --- a/pkg/detectors/portainer/portainer.go +++ b/pkg/detectors/portainer/portainer.go @@ -23,7 +23,7 @@ var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. endpointPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainer"}) + `\b(https?:\/\/\S+(:[0-9]{4,5})?)\b`) - tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainer"}) + `\b(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[0-9A-Za-z]{50,310}\.[0-9A-Z-a-z\-_]{43})\b`) + tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainer"}) + `\b(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[0-9A-Za-z]{50,310}\.[0-9A-Z-a-z\-_]{43})\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -59,7 +59,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if client == nil { client = defaultClient } - req, err := http.NewRequestWithContext(ctx, "GET", resEndpointMatch + "/api/endpoints", nil) + req, err := http.NewRequestWithContext(ctx, "GET", resEndpointMatch+"/api/endpoints", nil) if err != nil { continue } @@ -72,10 +72,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 || res.StatusCode == 403 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } @@ -86,7 +87,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if len(endpointMatches) > 0 { results = append(results, s1) } - + } } diff --git a/pkg/detectors/portainer/portainer_test.go b/pkg/detectors/portainer/portainer_test.go index 4ce1ec49e..49ca42d4a 100644 --- a/pkg/detectors/portainer/portainer_test.go +++ b/pkg/detectors/portainer/portainer_test.go @@ -102,11 +102,11 @@ func TestPortainer_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Portainer.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/portainertoken/portainertoken.go b/pkg/detectors/portainertoken/portainertoken.go index 9417b5a0d..c2f8e7402 100644 --- a/pkg/detectors/portainertoken/portainertoken.go +++ b/pkg/detectors/portainertoken/portainertoken.go @@ -22,7 +22,7 @@ var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. - keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainertoken"}) + `\b(ptr_[A-Za-z0-9\/_\-+=]{20,60})`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainertoken"}) + `\b(ptr_[A-Za-z0-9\/_\-+=]{20,60})`) endpointPat = regexp.MustCompile(detectors.PrefixRegex([]string{"portainer"}) + `\b(https?:\/\/\S+(:[0-9]{4,5})?)\b`) ) @@ -59,7 +59,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if client == nil { client = defaultClient } - req, err := http.NewRequestWithContext(ctx, "GET", resEndpointMatch + "/api/stacks", nil) + req, err := http.NewRequestWithContext(ctx, "GET", resEndpointMatch+"/api/stacks", nil) if err != nil { continue } @@ -67,7 +67,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result req.Header.Add("X-API-Key", resMatch) res, err := client.Do(req) - + if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { @@ -75,10 +75,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/portainertoken/portainertoken_test.go b/pkg/detectors/portainertoken/portainertoken_test.go index 2f8a61d8b..cf2c35d22 100644 --- a/pkg/detectors/portainertoken/portainertoken_test.go +++ b/pkg/detectors/portainertoken/portainertoken_test.go @@ -102,11 +102,11 @@ func TestPortainertoken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Portainertoken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/postman/postman.go b/pkg/detectors/postman/postman.go index 227071fa3..79276ff9e 100644 --- a/pkg/detectors/postman/postman.go +++ b/pkg/detectors/postman/postman.go @@ -55,7 +55,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyPostman(ctx, client, resMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { diff --git a/pkg/detectors/postman/postman_test.go b/pkg/detectors/postman/postman_test.go index df7a0b402..355b81cd8 100644 --- a/pkg/detectors/postman/postman_test.go +++ b/pkg/detectors/postman/postman_test.go @@ -129,11 +129,11 @@ func TestPostman_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Errorf("Postman.FromData() error = %v, wantErr %v", got[i].VerificationError, tt.wantVerificationErr) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Errorf("Postman.FromData() error = %v, wantErr %v", got[i].VerificationError(), tt.wantVerificationErr) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Postman.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/privacy/privacy.go b/pkg/detectors/privacy/privacy.go index 7b030232b..38df12247 100644 --- a/pkg/detectors/privacy/privacy.go +++ b/pkg/detectors/privacy/privacy.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/privacy/privacy_test.go b/pkg/detectors/privacy/privacy_test.go index b52057749..c17317ea6 100644 --- a/pkg/detectors/privacy/privacy_test.go +++ b/pkg/detectors/privacy/privacy_test.go @@ -6,11 +6,12 @@ package privacy import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestPrivacy_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Privacy.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/privatekey/privatekey.go b/pkg/detectors/privatekey/privatekey.go index 860a66452..e071b146d 100644 --- a/pkg/detectors/privatekey/privatekey.go +++ b/pkg/detectors/privatekey/privatekey.go @@ -51,25 +51,25 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete continue } - secret := detectors.Result{ + s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PrivateKey, Raw: []byte(token), Redacted: token[0:64], } - secret.ExtraData = make(map[string]string) + s1.ExtraData = make(map[string]string) var passphrase string parsedKey, err := ssh.ParseRawPrivateKey([]byte(token)) if err != nil && strings.Contains(err.Error(), "private key is passphrase protected") { - secret.ExtraData["encrypted"] = "true" + s1.ExtraData["encrypted"] = "true" parsedKey, passphrase, err = crack([]byte(token)) if err != nil { - secret.VerificationError = err + s1.SetVerificationError(err, token) continue } if passphrase != "" { - secret.ExtraData["cracked_encryption_passphrase"] = "true" + s1.ExtraData["cracked_encryption_passphrase"] = "true" } } else if err != nil { // couldn't parse key, probably invalid @@ -86,8 +86,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete data, err := lookupFingerprint(fingerprint, s.IncludeExpired) if err == nil { if data != nil { - secret.Verified = true - secret.ExtraData["certificate_urls"] = strings.Join(data.CertificateURLs, ", ") + s1.Verified = true + s1.ExtraData["certificate_urls"] = strings.Join(data.CertificateURLs, ", ") } } else { verificationErrors = append(verificationErrors, err.Error()) @@ -98,8 +98,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete verificationErrors = append(verificationErrors, err.Error()) } if user != nil { - secret.Verified = true - secret.ExtraData["github_user"] = *user + s1.Verified = true + s1.ExtraData["github_user"] = *user } user, err = verifyGitLabUser(parsedKey) @@ -107,20 +107,21 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete verificationErrors = append(verificationErrors, err.Error()) } if user != nil { - secret.Verified = true - secret.ExtraData["gitlab_user"] = *user + s1.Verified = true + s1.ExtraData["gitlab_user"] = *user } - if !secret.Verified && len(verificationErrors) > 0 { - secret.VerificationError = fmt.Errorf("verification failures: %s", strings.Join(verificationErrors, ", ")) + if !s1.Verified && len(verificationErrors) > 0 { + err = fmt.Errorf("verification failures: %s", strings.Join(verificationErrors, ", ")) + s1.SetVerificationError(err, token) } } - if len(secret.ExtraData) == 0 { - secret.ExtraData = nil + if len(s1.ExtraData) == 0 { + s1.ExtraData = nil } - results = append(results, secret) + results = append(results, s1) } return results, nil diff --git a/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go b/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go index f83296d94..99a16acd0 100644 --- a/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go +++ b/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go @@ -63,7 +63,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result isVerified, verificationErr := verifyPubNub(ctx, client, resMatch, ressubMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } // This function will check false positives for common test words, but also it will make sure the key diff --git a/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go b/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go index 7978ae490..9caa04ae1 100644 --- a/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go +++ b/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go @@ -131,11 +131,11 @@ func TestPubNubPublishKey_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("PubNubPublishKey.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/ramp/ramp.go b/pkg/detectors/ramp/ramp.go index 67031c97a..d41a869aa 100644 --- a/pkg/detectors/ramp/ramp.go +++ b/pkg/detectors/ramp/ramp.go @@ -3,13 +3,14 @@ package ramp import ( "context" "fmt" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "net/http" "net/url" "regexp" "strings" + + "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 { @@ -84,10 +85,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/ramp/ramp_test.go b/pkg/detectors/ramp/ramp_test.go index 69bb3ab44..7989a7509 100644 --- a/pkg/detectors/ramp/ramp_test.go +++ b/pkg/detectors/ramp/ramp_test.go @@ -6,11 +6,12 @@ package ramp import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -134,11 +135,11 @@ func TestRamp_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ramp.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/replicate/replicate.go b/pkg/detectors/replicate/replicate.go index f1a05db7b..e8bf5c40d 100644 --- a/pkg/detectors/replicate/replicate.go +++ b/pkg/detectors/replicate/replicate.go @@ -21,7 +21,7 @@ var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() - keyPat = regexp.MustCompile(`\b(r8_[0-9A-Za-z-_]{37})\b`) + keyPat = regexp.MustCompile(`\b(r8_[0-9A-Za-z-_]{37})\b`) ) func (s Scanner) Keywords() []string { @@ -62,10 +62,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/replicate/replicate_test.go b/pkg/detectors/replicate/replicate_test.go index 0795734c5..ab597813d 100644 --- a/pkg/detectors/replicate/replicate_test.go +++ b/pkg/detectors/replicate/replicate_test.go @@ -99,11 +99,11 @@ func TestReplicate_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Replicate.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/replyio/replyio.go b/pkg/detectors/replyio/replyio.go index f9c8f72c9..241405348 100644 --- a/pkg/detectors/replyio/replyio.go +++ b/pkg/detectors/replyio/replyio.go @@ -63,10 +63,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/replyio/replyio_test.go b/pkg/detectors/replyio/replyio_test.go index bbeea658a..9f01fa023 100644 --- a/pkg/detectors/replyio/replyio_test.go +++ b/pkg/detectors/replyio/replyio_test.go @@ -99,11 +99,11 @@ func TestReplyio_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Replyio.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/requestfinance/requestfinance.go b/pkg/detectors/requestfinance/requestfinance.go index c55c64d2e..35d8710d7 100644 --- a/pkg/detectors/requestfinance/requestfinance.go +++ b/pkg/detectors/requestfinance/requestfinance.go @@ -21,7 +21,7 @@ var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() - keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"requestfinance"}) + `\b([0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7})\b`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"requestfinance"}) + `\b([0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7})\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -65,10 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/requestfinance/requestfinance_test.go b/pkg/detectors/requestfinance/requestfinance_test.go index 3c32fca4d..d8dc273f8 100644 --- a/pkg/detectors/requestfinance/requestfinance_test.go +++ b/pkg/detectors/requestfinance/requestfinance_test.go @@ -99,11 +99,11 @@ func TestRequestfinance_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Requestfinance.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/salesforce/salesforce.go b/pkg/detectors/salesforce/salesforce.go index 0623621d4..f886e1334 100644 --- a/pkg/detectors/salesforce/salesforce.go +++ b/pkg/detectors/salesforce/salesforce.go @@ -78,7 +78,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if err != nil { // End execution, append Detector Result if request fails to prevent panic on response body checks - s1.VerificationError = err + s1.SetVerificationError(err, tokenMatch) results = append(results, s1) continue } @@ -93,7 +93,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { s1.Verified = false } else { - s1.VerificationError = fmt.Errorf("request to %v returned status %d with error %+v", res.Request.URL, res.StatusCode, err) + err = fmt.Errorf("request to %v returned status %d with error %+v", res.Request.URL, res.StatusCode, err) + s1.SetVerificationError(err, tokenMatch) } } diff --git a/pkg/detectors/salesforce/salesforce_test.go b/pkg/detectors/salesforce/salesforce_test.go index 77ff55c4b..6c7721e97 100644 --- a/pkg/detectors/salesforce/salesforce_test.go +++ b/pkg/detectors/salesforce/salesforce_test.go @@ -6,11 +6,12 @@ package salesforce import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -139,11 +140,11 @@ func TestSalesforce_FromChunk(t *testing.T) { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Salesforce.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/screenshotapi/screenshotapi.go b/pkg/detectors/screenshotapi/screenshotapi.go index e2b453050..6f2f91b58 100644 --- a/pkg/detectors/screenshotapi/screenshotapi.go +++ b/pkg/detectors/screenshotapi/screenshotapi.go @@ -64,7 +64,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result defer res.Body.Close() var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if res.StatusCode >= 200 && res.StatusCode < 300 && r.Screenshot != "" { diff --git a/pkg/detectors/sendbird/sendbird.go b/pkg/detectors/sendbird/sendbird.go index f632083a6..ee5c253ac 100644 --- a/pkg/detectors/sendbird/sendbird.go +++ b/pkg/detectors/sendbird/sendbird.go @@ -86,18 +86,21 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result var userResp userResp err := json.NewDecoder(res.Body).Decode(&userResp) if err != nil { - s1.VerificationError = fmt.Errorf("error decoding json response body: %w", err) + err = fmt.Errorf("error decoding json response body: %w", err) + s1.SetVerificationError(err, resMatch) } else if userResp.Code != 400401 { // https://sendbird.com/docs/chat/platform-api/v3/error-codes // Sendbird always includes its own error codes with 400 responses // 400401 (InvalidApiToken) is the only one that indicates a bad token - s1.VerificationError = fmt.Errorf("unexpected response code: %d", userResp.Code) + err = fmt.Errorf("unexpected response code: %d", userResp.Code) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/sendbird/sendbird_test.go b/pkg/detectors/sendbird/sendbird_test.go index 6f987718b..b84849869 100644 --- a/pkg/detectors/sendbird/sendbird_test.go +++ b/pkg/detectors/sendbird/sendbird_test.go @@ -166,11 +166,11 @@ func TestSendbird_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Sendbird.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go index 52f22ce6c..aa927cadb 100644 --- a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go +++ b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go @@ -57,18 +57,19 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result req, err := http.NewRequestWithContext(ctx, "GET", "https://gate.sendbird.com/api/v2/applications", nil) if err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } req.Header.Add("SENDBIRDORGANIZATIONAPITOKEN", resMatch) res, err := client.Do(req) if err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } else { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode != http.StatusForbidden { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } } diff --git a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go index 46ea5b339..7d1b856ad 100644 --- a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go +++ b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go @@ -132,11 +132,11 @@ func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Sendbird.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sendgrid/sendgrid.go b/pkg/detectors/sendgrid/sendgrid.go index 9760cffec..eb6fbc5d1 100644 --- a/pkg/detectors/sendgrid/sendgrid.go +++ b/pkg/detectors/sendgrid/sendgrid.go @@ -42,11 +42,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if len(match) != 2 { continue } - res := strings.TrimSpace(match[1]) + resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_SendGrid, - Raw: []byte(res), + Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/sendgrid/", @@ -67,7 +67,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if err != nil { continue } - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", res)) + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { @@ -79,7 +79,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden { s1.Verified = true } else if res.StatusCode != http.StatusUnauthorized { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } } diff --git a/pkg/detectors/sendgrid/sendgrid_test.go b/pkg/detectors/sendgrid/sendgrid_test.go index bd95329da..490636ec2 100644 --- a/pkg/detectors/sendgrid/sendgrid_test.go +++ b/pkg/detectors/sendgrid/sendgrid_test.go @@ -126,7 +126,7 @@ func TestSendgrid_FromChunk(t *testing.T) { t.Errorf("Sendgrid.FromData() error = %v, wantErr %v", err, tt.wantErr) return } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Sendgrid.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sentrytoken/sentrytoken.go b/pkg/detectors/sentrytoken/sentrytoken.go index 0362c52cd..97ac3a153 100644 --- a/pkg/detectors/sentrytoken/sentrytoken.go +++ b/pkg/detectors/sentrytoken/sentrytoken.go @@ -66,7 +66,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result case isVerified: s1.Verified = true default: - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } } diff --git a/pkg/detectors/sentrytoken/sentrytoken_test.go b/pkg/detectors/sentrytoken/sentrytoken_test.go index 5c547a3c5..44a9324f2 100644 --- a/pkg/detectors/sentrytoken/sentrytoken_test.go +++ b/pkg/detectors/sentrytoken/sentrytoken_test.go @@ -181,11 +181,11 @@ func TestSentryToken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError()) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/slack/slack.go b/pkg/detectors/slack/slack.go index 9e105bb86..064c1309b 100644 --- a/pkg/detectors/slack/slack.go +++ b/pkg/detectors/slack/slack.go @@ -82,7 +82,8 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result defer res.Body.Close() var authResponse authRes if err := json.NewDecoder(res.Body).Decode(&authResponse); err != nil { - s1.VerificationError = fmt.Errorf("failed to decode auth response: %w", err) + err = fmt.Errorf("failed to decode auth response: %w", err) + s1.SetVerificationError(err, token) } if authResponse.Ok { @@ -91,10 +92,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if authResponse.Error == "invalid_auth" { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected error auth response %+v", authResponse.Error) + err = fmt.Errorf("unexpected error auth response %+v", authResponse.Error) + s1.SetVerificationError(err, token) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, token) } } diff --git a/pkg/detectors/slack/slack_test.go b/pkg/detectors/slack/slack_test.go index 84c869898..28fd42097 100644 --- a/pkg/detectors/slack/slack_test.go +++ b/pkg/detectors/slack/slack_test.go @@ -120,11 +120,11 @@ func TestSlack_FromChunk(t *testing.T) { } got[i].Raw = nil - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.wantResults, ignoreOpts); diff != "" { t.Errorf("Slack.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/slackwebhook/slackwebhook.go b/pkg/detectors/slackwebhook/slackwebhook.go index 16bf24f0f..a6e1c7e64 100644 --- a/pkg/detectors/slackwebhook/slackwebhook.go +++ b/pkg/detectors/slackwebhook/slackwebhook.go @@ -81,10 +81,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 || res.StatusCode == 403 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/slackwebhook/slackwebhook_test.go b/pkg/detectors/slackwebhook/slackwebhook_test.go index 0d5d5c969..79511430d 100644 --- a/pkg/detectors/slackwebhook/slackwebhook_test.go +++ b/pkg/detectors/slackwebhook/slackwebhook_test.go @@ -183,11 +183,11 @@ func TestSlackWebhook_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("SlackWebhook.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/snowflake/snowflake.go b/pkg/detectors/snowflake/snowflake.go index 2f7db62c7..0b837f9e1 100644 --- a/pkg/detectors/snowflake/snowflake.go +++ b/pkg/detectors/snowflake/snowflake.go @@ -4,13 +4,14 @@ import ( "context" "database/sql" "fmt" + "regexp" + "strings" + "unicode" + _ "github.com/snowflakedb/gosnowflake" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" - "regexp" - "strings" - "unicode" ) type Scanner struct { @@ -120,7 +121,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if strings.Contains(err.Error(), "Incorrect username or password was specified") { s1.Verified = false } else { - s1.VerificationError = err + s1.SetVerificationError(err, resPasswordMatch) } } else { rows, err := db.Query(retrieveAllDatabasesQuery) @@ -141,7 +142,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } s1.ExtraData["databases"] = strings.Join(databases, ", ") - if s1.VerificationError == nil { + if s1.VerificationError() == nil { s1.Verified = true } } diff --git a/pkg/detectors/snowflake/snowflake_test.go b/pkg/detectors/snowflake/snowflake_test.go index 87af00365..322d679b1 100644 --- a/pkg/detectors/snowflake/snowflake_test.go +++ b/pkg/detectors/snowflake/snowflake_test.go @@ -6,11 +6,12 @@ package snowflake import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -133,11 +134,11 @@ func TestSnowflake_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Snowflake.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sourcegraph/sourcegraph.go b/pkg/detectors/sourcegraph/sourcegraph.go index bf463fceb..7580eb35e 100644 --- a/pkg/detectors/sourcegraph/sourcegraph.go +++ b/pkg/detectors/sourcegraph/sourcegraph.go @@ -71,10 +71,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/sourcegraph/sourcegraph_test.go b/pkg/detectors/sourcegraph/sourcegraph_test.go index d95181c52..83b0de6f7 100644 --- a/pkg/detectors/sourcegraph/sourcegraph_test.go +++ b/pkg/detectors/sourcegraph/sourcegraph_test.go @@ -6,11 +6,12 @@ package sourcegraph import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestSourcegraph_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Sourcegraph.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sourcegraphcody/sourcegraphcody.go b/pkg/detectors/sourcegraphcody/sourcegraphcody.go index b4fd7579f..08052cc0f 100644 --- a/pkg/detectors/sourcegraphcody/sourcegraphcody.go +++ b/pkg/detectors/sourcegraphcody/sourcegraphcody.go @@ -68,10 +68,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result // 401 when the token is invalid. // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/sourcegraphcody/sourcegraphcody_test.go b/pkg/detectors/sourcegraphcody/sourcegraphcody_test.go index 8349e1b5f..2445ebfa2 100644 --- a/pkg/detectors/sourcegraphcody/sourcegraphcody_test.go +++ b/pkg/detectors/sourcegraphcody/sourcegraphcody_test.go @@ -133,11 +133,11 @@ func TestSourcegraphcody_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Sourcegraphcody.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/sqlserver/sqlserver.go b/pkg/detectors/sqlserver/sqlserver.go index 71798cc74..02c2ebaa7 100644 --- a/pkg/detectors/sqlserver/sqlserver.go +++ b/pkg/detectors/sqlserver/sqlserver.go @@ -41,7 +41,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result continue } - detected := detectors.Result{ + s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_SQLServer, Raw: []byte(paramsUnsafe.Password), RawV2: []byte(paramsUnsafe.URL().String()), @@ -49,20 +49,20 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - verified, err := ping(paramsUnsafe) + isVerified, err := ping(paramsUnsafe) - detected.Verified = verified + s1.Verified = isVerified if mssqlErr, isMssqlErr := err.(mssql.Error); isMssqlErr && mssqlErr.Number == 18456 { // Login failed // Number taken from https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver16 // Nothing to do; determinate failure to verify } else { - detected.VerificationError = err + s1.SetVerificationError(err, paramsUnsafe.Password) } } - results = append(results, detected) + results = append(results, s1) } return results, nil diff --git a/pkg/detectors/sqlserver/sqlserver_integration_test.go b/pkg/detectors/sqlserver/sqlserver_integration_test.go index 67fd75de2..6e7fcac3c 100644 --- a/pkg/detectors/sqlserver/sqlserver_integration_test.go +++ b/pkg/detectors/sqlserver/sqlserver_integration_test.go @@ -151,11 +151,11 @@ func TestSQLServerIntegration_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("SQLServer.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/stripo/stripo.go b/pkg/detectors/stripo/stripo.go index e79cb8582..165f9993b 100644 --- a/pkg/detectors/stripo/stripo.go +++ b/pkg/detectors/stripo/stripo.go @@ -65,10 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/stripo/stripo_test.go b/pkg/detectors/stripo/stripo_test.go index 5519e443c..3c6671873 100644 --- a/pkg/detectors/stripo/stripo_test.go +++ b/pkg/detectors/stripo/stripo_test.go @@ -99,11 +99,11 @@ func TestStripo_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Stripo.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/surveybot/surveybot.go b/pkg/detectors/surveybot/surveybot.go index 90119f17b..8ecdf7c69 100644 --- a/pkg/detectors/surveybot/surveybot.go +++ b/pkg/detectors/surveybot/surveybot.go @@ -64,7 +64,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if res.StatusCode >= 200 && res.StatusCode < 300 { var r response if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) continue } if len(r.Surveys) > 0 { diff --git a/pkg/detectors/tailscale/tailscale.go b/pkg/detectors/tailscale/tailscale.go index cc51b3c2e..ab585f9ca 100644 --- a/pkg/detectors/tailscale/tailscale.go +++ b/pkg/detectors/tailscale/tailscale.go @@ -62,10 +62,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result case http.StatusUnauthorized: // The secret is determinately not verified (nothing to do) default: - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/twilio/twilio.go b/pkg/detectors/twilio/twilio.go index 9588e858e..caac3751d 100644 --- a/pkg/detectors/twilio/twilio.go +++ b/pkg/detectors/twilio/twilio.go @@ -80,10 +80,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 || res.StatusCode == 403 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, key) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, key) } } diff --git a/pkg/detectors/twilio/twilio_test.go b/pkg/detectors/twilio/twilio_test.go index a6f53eab4..25288e1e7 100644 --- a/pkg/detectors/twilio/twilio_test.go +++ b/pkg/detectors/twilio/twilio_test.go @@ -6,11 +6,12 @@ package twilio import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" @@ -137,11 +138,11 @@ func TestTwilio_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Twilio.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/twitch/twitch.go b/pkg/detectors/twitch/twitch.go index 99d3a1b57..d0c5b52fb 100644 --- a/pkg/detectors/twitch/twitch.go +++ b/pkg/detectors/twitch/twitch.go @@ -65,7 +65,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result client := s.getClient() isVerified, verificationErr := verifyTwitch(ctx, client, resMatch, resIdMatch) s1.Verified = isVerified - s1.VerificationError = verificationErr + s1.SetVerificationError(verificationErr, resMatch) } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key diff --git a/pkg/detectors/twitch/twitch_test.go b/pkg/detectors/twitch/twitch_test.go index f0d5039d4..a708e037a 100644 --- a/pkg/detectors/twitch/twitch_test.go +++ b/pkg/detectors/twitch/twitch_test.go @@ -181,11 +181,11 @@ func TestTwitch_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Errorf("Twitch.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantVerificationErr) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Errorf("Twitch.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError(), tt.wantVerificationErr) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Twitch.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/uri/uri.go b/pkg/detectors/uri/uri.go index 8667ee617..52b3f0144 100644 --- a/pkg/detectors/uri/uri.go +++ b/pkg/detectors/uri/uri.go @@ -83,7 +83,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if s.client == nil { s.client = defaultClient } - s1.Verified, s1.VerificationError = verifyURL(ctx, s.client, parsedURL) + isVerified, verificationError := verifyURL(ctx, s.client, parsedURL) + s1.Verified = isVerified + s1.SetVerificationError(verificationError, password) } if !s1.Verified { diff --git a/pkg/detectors/uri/uri_test.go b/pkg/detectors/uri/uri_test.go index c1c1f840e..567c97979 100644 --- a/pkg/detectors/uri/uri_test.go +++ b/pkg/detectors/uri/uri_test.go @@ -88,13 +88,15 @@ func TestURI_FromChunk(t *testing.T) { data: []byte(fmt.Sprintf("You can find a uri secret %s within", "https://httpwatch:pass@www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx")), verify: true, }, - want: []detectors.Result{ - { + want: func() []detectors.Result { + r := detectors.Result{ DetectorType: detectorspb.DetectorType_URI, Verified: false, Redacted: "https://httpwatch:********@www.httpwatch.com", - }, - }, + } + r.SetVerificationError(fmt.Errorf("context deadline exceeded")) + return []detectors.Result{r} + }(), wantErr: false, wantVerificationErr: true, }, @@ -132,13 +134,12 @@ func TestURI_FromChunk(t *testing.T) { // return // } for i := range got { - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Errorf("URI.FromData() error = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantErr) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Errorf("URI.FromData() error = %v, wantVerificationErr %v", got[i].VerificationError(), tt.want[i]) return } got[i].Raw = nil got[i].RawV2 = nil - got[i].VerificationError = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("URI.FromData() %s diff: (-got +want)\n%s", tt.name, diff) diff --git a/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken.go b/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken.go index c78dbde27..e49aedc05 100644 --- a/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken.go +++ b/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken.go @@ -44,7 +44,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_VagrantCloudPersonalToken, + DetectorType: detectorspb.DetectorType_VagrantCloudPersonalToken, Raw: []byte(resMatch), } @@ -53,7 +53,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if client == nil { client = defaultClient } - + req, err := http.NewRequestWithContext(ctx, "GET", "https://app.vagrantup.com/api/v2/authenticate", nil) if err != nil { continue @@ -67,10 +67,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken_test.go b/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken_test.go index 12e10a637..6ecbafaeb 100644 --- a/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken_test.go +++ b/pkg/detectors/vagrantcloudpersonaltoken/vagrantcloudpersonaltoken_test.go @@ -99,11 +99,11 @@ func TestVagrantcloudpersonaltoken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Vagrantcloudpersonaltoken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/voiceflow/voiceflow.go b/pkg/detectors/voiceflow/voiceflow.go index 0795abdf4..3513be3e9 100644 --- a/pkg/detectors/voiceflow/voiceflow.go +++ b/pkg/detectors/voiceflow/voiceflow.go @@ -80,11 +80,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if err == nil { bodyString = buf.String() } - s1.VerificationError = fmt.Errorf("unexpected HTTP response [status=%d, body=%s]", res.StatusCode, bodyString) + verificationErr := fmt.Errorf("unexpected HTTP response [status=%d, body=%s]", res.StatusCode, bodyString) + s1.SetVerificationError(verificationErr, resMatch) } _ = res.Body.Close() } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/voiceflow/voiceflow_test.go b/pkg/detectors/voiceflow/voiceflow_test.go index 87cb56cc9..5a960a260 100644 --- a/pkg/detectors/voiceflow/voiceflow_test.go +++ b/pkg/detectors/voiceflow/voiceflow_test.go @@ -6,11 +6,12 @@ package voiceflow import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -270,11 +271,11 @@ func TestVoiceflow_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Voiceflow.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/web3storage/web3storage.go b/pkg/detectors/web3storage/web3storage.go index 359362e93..346547447 100644 --- a/pkg/detectors/web3storage/web3storage.go +++ b/pkg/detectors/web3storage/web3storage.go @@ -66,10 +66,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err := fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/web3storage/web3storage_test.go b/pkg/detectors/web3storage/web3storage_test.go index 53d0f43fe..fde0c14ea 100644 --- a/pkg/detectors/web3storage/web3storage_test.go +++ b/pkg/detectors/web3storage/web3storage_test.go @@ -6,11 +6,12 @@ package web3storage import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -132,11 +133,11 @@ func TestWeb3Storage_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Web3Storage.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/zerotier/zerotier.go b/pkg/detectors/zerotier/zerotier.go index 332d51ca7..5c094a1cf 100644 --- a/pkg/detectors/zerotier/zerotier.go +++ b/pkg/detectors/zerotier/zerotier.go @@ -67,10 +67,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(err, resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } diff --git a/pkg/detectors/zerotier/zerotier_test.go b/pkg/detectors/zerotier/zerotier_test.go index 2c879e560..b35ed7149 100644 --- a/pkg/detectors/zerotier/zerotier_test.go +++ b/pkg/detectors/zerotier/zerotier_test.go @@ -99,11 +99,11 @@ func TestZerotier_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Zerotier.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } diff --git a/pkg/detectors/zulipchat/zulipchat.go b/pkg/detectors/zulipchat/zulipchat.go index 1164836c8..89f926690 100644 --- a/pkg/detectors/zulipchat/zulipchat.go +++ b/pkg/detectors/zulipchat/zulipchat.go @@ -12,7 +12,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct{ +type Scanner struct { client *http.Client } @@ -62,14 +62,14 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ZulipChat, Raw: []byte(resMatch), - RawV2: []byte(fmt.Sprintf("%s:%s:%s",resMatch, resIdMatch, resDomainMatch)), + RawV2: []byte(fmt.Sprintf("%s:%s:%s", resMatch, resIdMatch, resDomainMatch)), } if verify { client := s.client - if client == nil { - client = defaultClient - } + if client == nil { + client = defaultClient + } req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/api/v1/users", resDomainMatch), nil) if err != nil { continue @@ -85,15 +85,15 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } else if res.StatusCode == 401 { // This secret is determinately not verified, nothing to do here } else { - s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + s1.SetVerificationError(fmt.Errorf("unexpected HTTP response status %d", res.StatusCode), resMatch) } } else { - s1.VerificationError = err + s1.SetVerificationError(err, resMatch) } } - if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { - continue - } + if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { + continue + } results = append(results, s1) } diff --git a/pkg/detectors/zulipchat/zulipchat_test.go b/pkg/detectors/zulipchat/zulipchat_test.go index c16eb2849..ae76fa9ce 100644 --- a/pkg/detectors/zulipchat/zulipchat_test.go +++ b/pkg/detectors/zulipchat/zulipchat_test.go @@ -6,11 +6,12 @@ package zulipchat import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -134,11 +135,11 @@ func TestZulipChat_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - if (got[i].VerificationError != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Zulipchat.FromData() %s diff: (-got +want)\n%s", tt.name, diff) }