From 49495613014ce2701f6cd8469e1b01e05cd033d6 Mon Sep 17 00:00:00 2001 From: Kashif Khan <70996046+kashifkhan0771@users.noreply.github.com> Date: Tue, 10 Dec 2024 22:20:15 +0500 Subject: [PATCH] Fix SatisMeter Detector (#3692) * fixed and updated satismeter detector * close the response body * added tags for integration test cases --- .../satismeterprojectkey.go | 91 ++++++++++--------- .../satismeterprojectkey_integration_test.go | 17 ++-- .../satismeterprojectkey_test.go | 13 +-- 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/pkg/detectors/satismeterprojectkey/satismeterprojectkey.go b/pkg/detectors/satismeterprojectkey/satismeterprojectkey.go index e94f57bc60ba..8b16a4987e15 100644 --- a/pkg/detectors/satismeterprojectkey/satismeterprojectkey.go +++ b/pkg/detectors/satismeterprojectkey/satismeterprojectkey.go @@ -2,8 +2,8 @@ package satismeterprojectkey import ( "context" - b64 "encoding/base64" "fmt" + "io" "net/http" "strings" @@ -25,9 +25,8 @@ var ( client = 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{"satismeter"}) + `\b([a-zA-Z0-9]{24})\b`) - emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"satismeter"}) + common.EmailPattern) - passPat = regexp.MustCompile(detectors.PrefixRegex([]string{"satismeter"}) + `\b([a-zA-Z0-9!=@#$%^]{6,32})`) + projectPat = regexp.MustCompile(detectors.PrefixRegex([]string{"satismeter"}) + `\b([a-zA-Z0-9]{24})\b`) + tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"satismeter"}) + `\b([A-Za-z0-9]{32})\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -40,49 +39,30 @@ func (s Scanner) Keywords() []string { func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) - uniqueEmailMatches, uniqueKeyMatches, uniquePassMatches := make(map[string]struct{}), make(map[string]struct{}), make(map[string]struct{}) - for _, match := range emailPat.FindAllStringSubmatch(dataStr, -1) { - uniqueEmailMatches[strings.TrimSpace(match[1])] = struct{}{} + uniqueProjectMatches, uniqueTokenMatches := make(map[string]struct{}), make(map[string]struct{}) + for _, match := range projectPat.FindAllStringSubmatch(dataStr, -1) { + uniqueProjectMatches[strings.TrimSpace(match[1])] = struct{}{} } - for _, match := range keyPat.FindAllStringSubmatch(dataStr, -1) { - uniqueKeyMatches[strings.TrimSpace(match[1])] = struct{}{} + for _, match := range tokenPat.FindAllStringSubmatch(dataStr, -1) { + uniqueTokenMatches[strings.TrimSpace(match[1])] = struct{}{} } - for _, match := range passPat.FindAllStringSubmatch(dataStr, -1) { - uniquePassMatches[strings.TrimSpace(match[1])] = struct{}{} - } + for projectID := range uniqueProjectMatches { + for token := range uniqueTokenMatches { + s1 := detectors.Result{ + DetectorType: detectorspb.DetectorType_SatismeterProjectkey, + Raw: []byte(projectID), + RawV2: []byte(projectID + token), + } - for keyMatch := range uniqueKeyMatches { - for emailMatch := range uniqueEmailMatches { - for passMatch := range uniquePassMatches { - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_SatismeterProjectkey, - Raw: []byte(keyMatch), - RawV2: []byte(keyMatch + passMatch), - } - - if verify { - - data := fmt.Sprintf("%s:%s", emailMatch, passMatch) - sEnc := b64.StdEncoding.EncodeToString([]byte(data)) - - req, err := http.NewRequestWithContext(ctx, "GET", "https://app.satismeter.com/api/users?project="+keyMatch, nil) - if err != nil { - continue - } - req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) - res, err := client.Do(req) - if err == nil { - defer res.Body.Close() - if res.StatusCode >= 200 && res.StatusCode < 300 { - s1.Verified = true - } - } - } - - results = append(results, s1) + if verify { + isVerified, verificationErr := verifySatisMeterApp(ctx, client, projectID, token) + s1.Verified = isVerified + s1.SetVerificationError(verificationErr, token) } + + results = append(results, s1) } } @@ -97,3 +77,32 @@ func (s Scanner) Type() detectorspb.DetectorType { func (s Scanner) Description() string { return "Satismeter is a customer feedback platform. Satismeter project keys can be used to access project-specific data and manage feedback settings." } + +func verifySatisMeterApp(ctx context.Context, client *http.Client, projectID, token string) (bool, error) { + req, err := http.NewRequestWithContext(ctx, "GET", "https://app.satismeter.com/api/users?project="+projectID, nil) + if err != nil { + return false, err + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) + resp, err := client.Do(req) + if err != nil { + return false, err + } + defer func() { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + }() + + switch resp.StatusCode { + case http.StatusOK: + return true, nil + case http.StatusUnauthorized, http.StatusForbidden: + return false, nil + case http.StatusNotFound: + // if project id is not found, api return 401 + return false, nil + default: + return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } +} diff --git a/pkg/detectors/satismeterprojectkey/satismeterprojectkey_integration_test.go b/pkg/detectors/satismeterprojectkey/satismeterprojectkey_integration_test.go index 8b0a3bfcd249..6234e3f78d0b 100644 --- a/pkg/detectors/satismeterprojectkey/satismeterprojectkey_integration_test.go +++ b/pkg/detectors/satismeterprojectkey/satismeterprojectkey_integration_test.go @@ -10,9 +10,9 @@ import ( "time" "github.com/kylelemons/godebug/pretty" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) @@ -23,10 +23,11 @@ func TestSatismeterProjectkey_FromChunk(t *testing.T) { if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } - secret := testSecrets.MustGetField("SATISMETERPROJECTKEY_TOKEN") - inactiveSecret := testSecrets.MustGetField("SATISMETERPROJECTKEY_INACTIVE") - email := testSecrets.MustGetField("SATISMETERPROJECTKEY_EMAIL") - password := testSecrets.MustGetField("SATISMETERPROJECTKEY_PASSWORD") + + projectID := testSecrets.MustGetField("SATISMETERPROJECTKEY") + inactiveProjectID := testSecrets.MustGetField("SATISMETERPROJECTKEY_INACTIVE") + token := testSecrets.MustGetField("SATISMETER_TOKEN") + inactiveToken := testSecrets.MustGetField("SATISMETER_TOKEN_INACTIVE") type args struct { ctx context.Context @@ -45,13 +46,14 @@ func TestSatismeterProjectkey_FromChunk(t *testing.T) { s: Scanner{}, args: args{ ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a satismeterprojectkey secret %s within satismeterprojectkeyemail %s satismeterprojectkeypassword %s", secret, email, password)), + data: []byte(fmt.Sprintf("You can find a satismeterprojectkey id %s within satismetertoken %s", projectID, token)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_SatismeterProjectkey, Verified: true, + RawV2: []byte(projectID + token), }, }, wantErr: false, @@ -61,13 +63,14 @@ func TestSatismeterProjectkey_FromChunk(t *testing.T) { s: Scanner{}, args: args{ ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a satismeterprojectkey secret %s within but not valid satismeterprojectkeyemail %s satismeterprojectkeypassword %s", inactiveSecret, email, password)), // the secret would satisfy the regex but not pass validation), + data: []byte(fmt.Sprintf("You can find a satismeterprojectkey secret %s within satismetertoken: %s", inactiveProjectID, inactiveToken)), // the secret would satisfy the regex but not pass validation), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_SatismeterProjectkey, Verified: false, + RawV2: []byte(inactiveProjectID + inactiveToken), }, }, wantErr: false, diff --git a/pkg/detectors/satismeterprojectkey/satismeterprojectkey_test.go b/pkg/detectors/satismeterprojectkey/satismeterprojectkey_test.go index 1e2c6414cb2c..a5975226149e 100644 --- a/pkg/detectors/satismeterprojectkey/satismeterprojectkey_test.go +++ b/pkg/detectors/satismeterprojectkey/satismeterprojectkey_test.go @@ -13,11 +13,10 @@ import ( var ( validPattern = ` - satismeter_key = satismeter12345678901234 - satismeter_email = satismeter@example.com - satismeter_pass = satismeterSecureP@ss123 + satismeter_project = satismeter12345678901234 + satusmeter_token = VVVCVDXuoVwRFAKEiCseXmDiaC32jq7x ` - invalidPattern = "abcde/testing@go" + invalidPattern = "satismeter45678901234/testing@go" ) func TestSatisMeterProjectKey_Pattern(t *testing.T) { @@ -32,11 +31,7 @@ func TestSatisMeterProjectKey_Pattern(t *testing.T) { { name: "valid pattern", input: validPattern, - want: []string{ - "satismeter12345678901234satismeter12345678901234", - "satismeter12345678901234satismeter@example", - "satismeter12345678901234satismeterSecureP@ss123", - }, + want: []string{"satismeter12345678901234VVVCVDXuoVwRFAKEiCseXmDiaC32jq7x"}, }, { name: "invalid pattern",