From 91340c1e00d6599de918bee4edb22b3a1f06ab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=A2h=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, 12 Sep 2023 10:48:11 -0400 Subject: [PATCH] updating sendbirdorganizationapi detector to use tri-state verification (#1763) --- .../sendbirdorganizationapi.go | 29 +++++--- .../sendbirdorganizationapi_test.go | 69 +++++++++++++++---- 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go index 36e40c131f80..52f22ce6c072 100644 --- a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go +++ b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi.go @@ -2,6 +2,7 @@ package sendbirdorganizationapi import ( "context" + "fmt" "net/http" "regexp" "strings" @@ -11,13 +12,15 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct{} +type Scanner struct { + client *http.Client +} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( - client = common.SaneHttpClient() + 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{"sendbird"}) + `\b([0-9a-f]{24})\b`) @@ -47,25 +50,33 @@ 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://gate.sendbird.com/api/v2/applications", nil) if err != nil { - continue + s1.VerificationError = err } req.Header.Add("SENDBIRDORGANIZATIONAPITOKEN", resMatch) res, err := client.Do(req) - if err == nil { + if err != nil { + s1.VerificationError = err + } else { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true - } else { - // 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 detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { - continue - } + } else if res.StatusCode != http.StatusForbidden { + s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } } + // 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(resMatch, detectors.DefaultFalsePositives, true) { + continue + } results = append(results, s1) } diff --git a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go index de2c557fa0c6..46ea5b339388 100644 --- a/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go +++ b/pkg/detectors/sendbirdorganizationapi/sendbirdorganizationapi_test.go @@ -9,7 +9,8 @@ import ( "testing" "time" - "github.com/kylelemons/godebug/pretty" + "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" @@ -19,7 +20,7 @@ import ( func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") + testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } @@ -32,11 +33,12 @@ func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { verify bool } tests := []struct { - name string - s Scanner - args args - want []detectors.Result - wantErr bool + name string + s Scanner + args args + want []detectors.Result + wantErr bool + wantVerificationErr bool }{ { name: "found, verified", @@ -52,7 +54,8 @@ func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { Verified: true, }, }, - wantErr: false, + wantErr: false, + wantVerificationErr: false, }, { name: "found, unverified", @@ -68,7 +71,42 @@ func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { Verified: false, }, }, - wantErr: false, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "found, would be verified if not for timeout", + s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a sendbird secret %s within", secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_SendbirdOrganizationAPI, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + { + name: "found, verified but unexpected api surface", + s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a sendbird secret %s within", secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_SendbirdOrganizationAPI, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, }, { name: "not found", @@ -84,20 +122,23 @@ func TestSendbirdOrganizationAPI_FromChunk(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := Scanner{} - got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) + got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("SendbirdOrganizationAPI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } + for i := range got { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - got[i].Raw = nil + if (got[i].VerificationError != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + } } - if diff := pretty.Compare(got, tt.want); diff != "" { - t.Errorf("SendbirdOrganizationAPI.FromData() %s diff: (-got +want)\n%s", tt.name, diff) + 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) } }) }