From 735bac03911974e37641f570a5582a9b72f49a44 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:07:10 -0500 Subject: [PATCH 1/4] feat(algolia): upgrade detector (#3613) --- .../algoliaadminkey/algoliaadminkey.go | 162 ++++++++++-------- 1 file changed, 87 insertions(+), 75 deletions(-) diff --git a/pkg/detectors/algoliaadminkey/algoliaadminkey.go b/pkg/detectors/algoliaadminkey/algoliaadminkey.go index a72a5ddee..26486a73f 100644 --- a/pkg/detectors/algoliaadminkey/algoliaadminkey.go +++ b/pkg/detectors/algoliaadminkey/algoliaadminkey.go @@ -2,10 +2,12 @@ package algoliaadminkey import ( "context" - "fmt" "encoding/json" + "fmt" regexp "github.com/wasilibs/go-re2" + "io" "net/http" + "slices" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/common" @@ -13,7 +15,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct{ +type Scanner struct { detectors.DefaultMultiPartCredentialProvider } @@ -24,8 +26,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{"algolia", "docsearch", "apiKey"}) + `\b([a-zA-Z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia", "docsearch", "appId"}) + `\b([A-Z0-9]{10})\b`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia", "docsearch", "apiKey"}) + `\b([a-zA-Z0-9]{32})\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -38,105 +40,115 @@ func (s Scanner) Keywords() []string { func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) - matches := keyPat.FindAllStringSubmatch(dataStr, -1) - idMatches := idPat.FindAllStringSubmatch(dataStr, -1) - - for _, match := range matches { - if len(match) != 2 { - continue + // Deduplicate matches. + idMatches := make(map[string]struct{}) + for _, match := range idPat.FindAllStringSubmatch(dataStr, -1) { + id := match[1] + if detectors.StringShannonEntropy(id) > 2 { + idMatches[id] = struct{}{} } - resMatch := strings.TrimSpace(match[1]) - for _, idMatch := range idMatches { - if len(idMatch) != 2 { - continue - } - resIdMatch := strings.TrimSpace(idMatch[1]) + } + keyMatches := make(map[string]struct{}) + for _, match := range keyPat.FindAllStringSubmatch(dataStr, -1) { + key := match[1] + if detectors.StringShannonEntropy(key) > 3 { + keyMatches[key] = struct{}{} + } + } - s1 := detectors.Result{ + // Test matches. + for key := range keyMatches { + for id := range idMatches { + r := detectors.Result{ DetectorType: detectorspb.DetectorType_AlgoliaAdminKey, - Raw: []byte(resMatch), - RawV2: []byte(resMatch + resIdMatch), + Raw: []byte(key), + RawV2: []byte(id + ":" + key), } if verify { // Verify if the key is a valid Algolia Admin Key. - isVerified, verificationErr := verifyAlgoliaKey(ctx, resIdMatch, resMatch) - - // Verify if the key has sensitive permissions, even if it's not an Admin Key. - if !isVerified { - isVerified, verificationErr = verifyAlgoliaKeyACL(ctx, resIdMatch, resMatch) - } - - s1.SetVerificationError(verificationErr, resMatch) - s1.Verified = isVerified + isVerified, extraData, verificationErr := verifyMatch(ctx, id, key) + r.Verified = isVerified + r.ExtraData = extraData + r.SetVerificationError(verificationErr, key) } - results = append(results, s1) + results = append(results, r) + if r.Verified { + break + } } } return results, nil } -func verifyAlgoliaKey(ctx context.Context, appId, apiKey string) (bool, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://"+appId+"-dsn.algolia.net/1/keys", nil) - if err != nil { - return false, err - } - - req.Header.Add("X-Algolia-Application-Id", appId) - req.Header.Add("X-Algolia-API-Key", apiKey) - - res, err := client.Do(req) - if err != nil { - return false, err - } - defer res.Body.Close() - - if res.StatusCode == 403 { - return false, nil - } else if res.StatusCode < 200 || res.StatusCode > 299 { - return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) - } - - return true, nil +// https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl +var nonSensitivePermissions = map[string]struct{}{ + "listIndexes": {}, + "search": {}, + "settings": {}, } -func verifyAlgoliaKeyACL(ctx context.Context, appId, apiKey string) (bool, error) { +func verifyMatch(ctx context.Context, appId, apiKey string) (bool, map[string]string, error) { + // https://www.algolia.com/doc/rest-api/search/#section/Base-URLs req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://"+appId+".algolia.net/1/keys/"+apiKey, nil) if err != nil { - return false, err + return false, nil, err } - req.Header.Add("X-Algolia-Application-Id", appId) - req.Header.Add("X-Algolia-API-Key", apiKey) + req.Header.Set("X-Algolia-Application-Id", appId) + req.Header.Set("X-Algolia-API-Key", apiKey) res, err := client.Do(req) if err != nil { - return false, err + return false, nil, err } - defer res.Body.Close() + defer func() { + _, _ = io.Copy(io.Discard, res.Body) + _ = res.Body.Close() + }() - if res.StatusCode == 403 { - return false, nil - } else if res.StatusCode < 200 || res.StatusCode > 299 { - return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) - } - - var jsonResponse struct { - ACL []string `json:"acl"` - } - - if err := json.NewDecoder(res.Body).Decode(&jsonResponse); err != nil { - return false, err - } - - for _, acl := range jsonResponse.ACL { - if acl != "search" && acl != "listIndexes" && acl != "settings" { - return true, nil // Other permissions are sensitive. + switch res.StatusCode { + case http.StatusOK: + var keyRes keyResponse + if err := json.NewDecoder(res.Body).Decode(&keyRes); err != nil { + return false, nil, err } - } - return false, nil + // Check if the key has sensitive permissions, even if it's not an Admin Key. + hasSensitivePerms := false + for _, acl := range keyRes.ACL { + if _, ok := nonSensitivePermissions[acl]; !ok { + hasSensitivePerms = true + break + } + } + if !hasSensitivePerms { + return false, nil, nil + } + + slices.Sort(keyRes.ACL) + extraData := map[string]string{ + "acl": strings.Join(keyRes.ACL, ","), + } + if keyRes.Description != "" && keyRes.Description != "" { + extraData["description"] = keyRes.Description + } + return true, extraData, nil + case http.StatusUnauthorized: + return false, nil, nil + case http.StatusForbidden: + // Key is valid but lacks permissions. + return true, nil, nil + default: + return false, nil, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + } +} + +// https://www.algolia.com/doc/rest-api/search/#tag/Api-Keys/operation/getApiKey +type keyResponse struct { + ACL []string `json:"acl"` + Description string `json:"description"` } func (s Scanner) Type() detectorspb.DetectorType { From b2158535059d77f9594789510f10a1a52edf9742 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:52:42 -0500 Subject: [PATCH 2/4] build: remove azure sdk (#3642) --- go.mod | 2 +- go.sum | 2 -- pkg/detectors/privatekey/privatekey.go | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e9dbf2a30..d3905b28e 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ replace github.com/STARRY-S/zip => github.com/STARRY-S/zip v0.1.0 require ( cloud.google.com/go/secretmanager v1.14.2 cloud.google.com/go/storage v1.47.0 - github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 github.com/BobuSumisu/aho-corasick v1.0.3 github.com/TheZeroSlave/zapsentry v1.23.0 github.com/adrg/strutil v0.3.1 @@ -137,6 +136,7 @@ require ( github.com/99designs/keyring v1.2.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect diff --git a/go.sum b/go.sum index a10e9c27a..ab786cdc0 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,6 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1 h1:gUDtaZk8heteyfdmv+pcfHvhR9llnh7c7GMwZ8RVG04= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= diff --git a/pkg/detectors/privatekey/privatekey.go b/pkg/detectors/privatekey/privatekey.go index 87cb8ccc2..8fe43c580 100644 --- a/pkg/detectors/privatekey/privatekey.go +++ b/pkg/detectors/privatekey/privatekey.go @@ -3,13 +3,13 @@ package privatekey import ( "context" "encoding/json" + "errors" "fmt" "net/http" "strings" "sync" "time" - "github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors" regexp "github.com/wasilibs/go-re2" "golang.org/x/crypto/ssh" From 5f2366588ab7f04b987d258c524474e68b6c10f7 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:53:00 -0500 Subject: [PATCH 3/4] build: remove golang-jwt@v4 (#3644) --- go.mod | 2 +- pkg/detectors/dockerhub/v1/dockerhub.go | 2 +- pkg/detectors/dockerhub/v2/dockerhub.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d3905b28e..6392674e0 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,6 @@ require ( github.com/go-sql-driver/mysql v1.8.1 github.com/gobwas/glob v0.2.3 github.com/golang-jwt/jwt v3.2.2+incompatible - github.com/golang-jwt/jwt/v4 v4.5.1 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 @@ -207,6 +206,7 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/pkg/detectors/dockerhub/v1/dockerhub.go b/pkg/detectors/dockerhub/v1/dockerhub.go index 6a0c4ce44..f2f2b807a 100644 --- a/pkg/detectors/dockerhub/v1/dockerhub.go +++ b/pkg/detectors/dockerhub/v1/dockerhub.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" regexp "github.com/wasilibs/go-re2" "github.com/trufflesecurity/trufflehog/v3/pkg/common" diff --git a/pkg/detectors/dockerhub/v2/dockerhub.go b/pkg/detectors/dockerhub/v2/dockerhub.go index 1034c3442..9ed36886d 100644 --- a/pkg/detectors/dockerhub/v2/dockerhub.go +++ b/pkg/detectors/dockerhub/v2/dockerhub.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" regexp "github.com/wasilibs/go-re2" "github.com/trufflesecurity/trufflehog/v3/pkg/common" From c19a46ce13b77985cb218266e7d7370c6a547dc0 Mon Sep 17 00:00:00 2001 From: Richard Gomez <32133502+rgmz@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:02:44 -0500 Subject: [PATCH 4/4] fix(gcp): handle quoted JSON (#2865) --- pkg/detectors/gcp/gcp.go | 93 +++++++++++++++++------------ pkg/detectors/gcp/gcp_test.go | 108 ++++++++++++++++++++++++++++------ 2 files changed, 145 insertions(+), 56 deletions(-) diff --git a/pkg/detectors/gcp/gcp.go b/pkg/detectors/gcp/gcp.go index 7abcad21a..f8b19edab 100644 --- a/pkg/detectors/gcp/gcp.go +++ b/pkg/detectors/gcp/gcp.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "strconv" "strings" regexp "github.com/wasilibs/go-re2" @@ -16,10 +17,12 @@ import ( type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. -var _ detectors.Detector = (*Scanner)(nil) -var _ detectors.CustomFalsePositiveChecker = (*Scanner)(nil) -var _ detectors.MaxSecretSizeProvider = (*Scanner)(nil) -var _ detectors.StartOffsetProvider = (*Scanner)(nil) +var _ interface { + detectors.Detector + detectors.CustomFalsePositiveChecker + detectors.MaxSecretSizeProvider + detectors.StartOffsetProvider +} = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(`\{[^{]+auth_provider_x509_cert_url[^}]+\}`) @@ -38,12 +41,6 @@ type gcpKey struct { ClientX509CertURL string `json:"client_x509_cert_url"` } -func trimCarrots(s string) string { - s = strings.TrimPrefix(s, "<") - s = strings.TrimSuffix(s, ">") - return s -} - // Keywords are used for efficiently pre-filtering chunks. // Use identifiers in the secret preferably, or the provider name. func (s Scanner) Keywords() []string { @@ -60,22 +57,28 @@ const startOffset = 4096 // StartOffset returns the start offset for the secret this detector finds. func (Scanner) StartOffset() int64 { return startOffset } +func (s Scanner) Type() detectorspb.DetectorType { + return detectorspb.DetectorType_GCP +} + +func (s Scanner) Description() string { + return "GCP (Google Cloud Platform) is a suite of cloud computing services that runs on the same infrastructure that Google uses internally for its end-user products. GCP keys can be used to access and manage these services." +} + // FromData will find and optionally verify GCP 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) - matches := keyPat.FindAllString(dataStr, -1) + uniqueMatches := make(map[string]struct{}) + for _, match := range keyPat.FindAllString(dataStr, -1) { + uniqueMatches[match] = struct{}{} + } - for _, match := range matches { - key := match - - key = strings.ReplaceAll(key, `,\\n`, `\n`) - key = strings.ReplaceAll(key, `\"\\n`, `\n`) - key = strings.ReplaceAll(key, `\\"`, `"`) + for match := range uniqueMatches { + key := cleanInput(match) creds := gcpKey{} - err := json.Unmarshal([]byte(key), &creds) - if err != nil { + if err := json.NewDecoder(strings.NewReader(key)).Decode(&creds); err != nil { continue } @@ -83,10 +86,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if strings.Contains(creds.ClientEmail, `") + return s } + +//endregion diff --git a/pkg/detectors/gcp/gcp_test.go b/pkg/detectors/gcp/gcp_test.go index af5a38e36..3b9d1129f 100644 --- a/pkg/detectors/gcp/gcp_test.go +++ b/pkg/detectors/gcp/gcp_test.go @@ -10,8 +10,18 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick" ) -var ( - validPattern = `[{ +func TestGCP_Pattern(t *testing.T) { + d := Scanner{} + ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) + tests := []struct { + name string + input string + want []string + skip bool + }{ + { + name: "valid pattern", + input: `[{ "_id": "1a8d0cca-e1a9-4318-bc2f-f5658ab2dcb5", "name": "GCP", "type": "Detector", @@ -35,28 +45,88 @@ var ( "expected_response": "200", "method": "GET", "deprecated": false - }]` - secret = `{"type":"service_account","project_id":"my-test-project","private_key_id":"abc12345def67890ghi","private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASC...\n-----END PRIVATE KEY-----\n","client_email":"my-test-project@my-gcp-project.iam.gserviceaccount.com","client_id":"123456789012345678901","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/my-service-account%40my-gcp-project.iam.gserviceaccount.com"}` -) - -func TestGCP_Pattern(t *testing.T) { - d := Scanner{} - ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) - - tests := []struct { - name string - input string - want []string - }{ - { - name: "valid pattern", - input: validPattern, - want: []string{secret}, + }]`, + want: []string{`{"type":"service_account","project_id":"my-test-project","private_key_id":"abc12345def67890ghi","private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASC...\n-----END PRIVATE KEY-----\n","client_email":"my-test-project@my-gcp-project.iam.gserviceaccount.com","client_id":"123456789012345678901","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/my-service-account%40my-gcp-project.iam.gserviceaccount.com"}`}, }, + { + name: "normal file", + input: `{ + "type": "service_account", + "project_id": "api-5153635936162123384-123456", + "private_key_id": "2b387b72ec1b082aa7e52189d9c43f58fb19fb48", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDE/XlaMP419pkUEXAMPLE=\n-----END PRIVATE KEY-----\n", + "client_email": "unit-test-value@api-5153635936162123384-123456.iam.gserviceaccount.com", + "client_id": "109763165530299657612", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/unit-test-value%40api-5153635936162123384-123456.iam.gserviceaccount.com" +}`, + want: []string{"{\"type\":\"service_account\",\"project_id\":\"api-5153635936162123384-123456\",\"private_key_id\":\"2b387b72ec1b082aa7e52189d9c43f58fb19fb48\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDE/XlaMP419pkUEXAMPLE=\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"unit-test-value@api-5153635936162123384-123456.iam.gserviceaccount.com\",\"client_id\":\"109763165530299657612\",\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"token_uri\":\"https://oauth2.googleapis.com/token\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\",\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/metadata/x509/unit-test-value%40api-5153635936162123384-123456.iam.gserviceaccount.com\"}"}, + }, + { + name: "escaped JSON", + input: `{"credentials":"{\n \"type\": \"service_account\",\n \"project_id\": \"unit-test\",\n \"private_key_id\": \"10f922eb17fba903dc59f7baf753976233520012\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCgyAZHbtJu1MRf\\ng9+Wg==\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"fake-value@unit-test.iam.gserviceaccount.com\",\n \"client_id\": \"123456476766156356779\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"https://oauth2.googleapis.com/token\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/fake-value%40unit-test.iam.gserviceaccount.com\"\n}\n"}`, + want: []string{"{\"type\":\"service_account\",\"project_id\":\"unit-test\",\"private_key_id\":\"10f922eb17fba903dc59f7baf753976233520012\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCgyAZHbtJu1MRf\\ng9+Wg==\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"fake-value@unit-test.iam.gserviceaccount.com\",\"client_id\":\"123456476766156356779\",\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"token_uri\":\"https://oauth2.googleapis.com/token\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\",\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/metadata/x509/fake-value%40unit-test.iam.gserviceaccount.com\"}g"}, + }, + { + name: "no private_key_id (1)", + input: ` { + resolve: 'gatsby-source-google-spreadsheet', + options: { + /// TODO /// + // Find a better way to include credentials without literally displaying in the config. + credentials: { + client_email: "gatsby-project@gatsby-project.iam.gserviceaccount.com", + private_key: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCuT6NTCf5cILhY\nQrqiah7ukgW1yzOR2brT2V1jiux77q22ljeB2XcnVivg97MWLKpPbghvV+zd7QLi\n4WQZL0f7x5W0yTs8CU6cyVNn/dWewEyWQbKifxWoJZiOhxw7cLIoZkY0Vtz9WQy3\nt0rvwMrkRpvA8dSVtgfOr2uSIAxA5vqsvl9VkT+WFy3bU1oKdUcUWGqhB2kg9wJh\nXLFdfGewt1xezEiFom2WTHx1lltQ5NURJALzo9GfelMfeBR2BPlRdrDp+vpPT1V4\nv90JO7V7yaJm5LeyOX1eKmcrQRw/gKnA9XYifdKl01sTIM4p3Rl6mJZe1iPkWVNj\nWGhW+zzZAgMBAAECggEABO85tSXVBsghV9RJwsPEzOWi1j7inpgejxU57NG3uJLs\n3vyZJSqiEiHBG7z/W5sXliiMAhTn3m9xc7lEMjdQfxrrDMNekyhDSrJzU0APDk+s\ny1sgQrPcydYIn85I2RDrZjPg/GXSGzTsZH4Cl89qHvS1v4xJA5Tz2yDBp5ETL+oe\nK3Xba1+TtvsCh14xAnx81yTFIGvjVukh3iXa8PjRdeANrw/WV/0eK6rA5wga20Ik\nsiV8C5t0rXMtF/ZMTagtQa/PL7G890T8CnbulMeYlZ1vhXTlNTMqrL9gl6Scmyuk\n0LWEWeElLHm9C+jDkdPjp+GAF7S6T7QO3r7k4tFHJQKBgQDqjmgtu0nqUqdpl5Un\nr6oUqLCYdMqCl2QjXmBEWdzXqtwo0YeBYGJHI+fc1JhI/XXAZX3AlIvaxzTuEgK6\n3T5pix51jXafFP3mR/WSWj9oFaH3MVp4V28fdnTkZjugPnEDl2vIKYMaiKQdEV0u\nqQLVHi1PT98STp1fnICew3h3/QKBgQC+Pz4qiJUTkuLP1n77K3/GzqVVnQFtPYMr\n1Ob04tDMeAnVL/yW38eP7tVHufeRMGt3A936vl+TF+c2doQLH3zsUFKF368TXsDi\nhSNsxYWeMhi/VejQkpPG7MXZj30nT3AI6YAmcwd5wwWkhGUSDeF6p/H9p89t+f/m\njpyBAB5JDQKBgFjWcxLPGuHLSGkv5mhPmkWU1r4HjiQEHwNeXWvF9WUh65zyLzaL\nQO3c5Za4Vq1egljKl+R23rmQNWXt0GbiIR9sd67iU4lRNBEiNBqoX9eWSfAMG031\nH7t07DUNm4vH2poXodUAFA3arv3rc7WWgeIiOdsOT1jpuaVa60Q2mMwpAoGBAI9d\ng1B0KrtcZpWvE3PdvOWplghlT8ztnOqr/vut7SEimHhSOCvOKUn69jieGMUN0v4W\nKPKrAcUML03ok+r56J8AjJ+cCAg10G8jW6W9V8r1/5Y4fECpJvxzM0mXCv5Tq57b\nr5nJ92k3oQnwR2YKlc9jvkWjbvp2efRZpfDEkQ4FAoGBAJLgC5YaWiFjn2EGtlGa\nKxunQ+zU/TtReRNCA9fjTdlxzH7uqHPrTTZ58TAf8VfI1T01rqY2LPXYsgxfU6ut\nbJrxof0hp00dDKldxFOl5QbvoXhF7T3Bq5RK9uFpxSOpaXvKIMjmYmACgeP1XNNq\n4ThWdWJOMDXLaJN2Mj8n3kih\n-----END PRIVATE KEY-----\n" + }, + // Spreadsheet is here: https://docs.google.com/spreadsheets/d/... +`, + skip: true, + }, + { + name: "no private_key_id (2)", + input: ` +module.exports = { + credentials: { + google: { + client_email: process.env.GOOGLE_CLIENT_EMAIL || 'cloudsploit@your-project-name.iam.gserviceaccount.com', + private_key: process.env.GOOGLE_PRIVATE_KEY || '-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY-GOES-HERE\n-----END PRIVATE KEY-----\n' + },`, + skip: true, + }, + { + name: ".env", + input: `GOOGLE_SERVICE_ACCOUNT_EMAIL=jedicconnect@f4k3acc.iam.gserviceaccount.com +GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkVEmh423RApn2\nv262VYWKhkyf11erwQPIdQP2f+zSXDxZJwQ2vAY/ecamlYz2TnDoXTM7LghLGXXd\n1ZdcMaAX3dgQdxt36B/gRLOO8eyQwEQjvseSm3NFQjyrX9x94STmrEOWP7xR8DuZ\nhq3hFXT9knD8CW+TwG7rQn0e6mdUqz2tujptLFwo+GUZZ4vbw1Y2pU9QBiVDtA8R\nIVdpDFujLS3nxJEhKUY7WYSWr1cwJwqBNMfIq0GW+Sk8/E25qHztZVsMsvyUtIIF\nH21dGMoyc4CSDWqdgSQvEeyTi9zrlVWA4/gHyb246UlkvEdOBVKH1jiZy76atsLS\nNCtIk6A3AgMBAAECggEAHiluXm19EZj1o4mdi5AE89kUpV4ENH038Yow0QTH9hCB\n7ycvKdC3IN18LcVTWz4okS3SInGfihFBRhdXMc/V/6tzZgpGm2qalzJE9t7Gugbg\nOuNghDNOJA81TYtJ0D0L5d8GhMRsD2oVtmc28RJcJ9LCNDCTLz5p3XqUhTSnBK4h\n2ad5BHgXgCinVXZ+KgCnrm6obijcrlvTGacGXT42UsRA/AJE9Lh2t+zNQE0v6T+B\niI/DEHcq0gNU8yKQSiLsL7P1nc3sLZs7DKsLP0NH1Wd4wgupM9PnPMPAHAwye1tA\nz/hdaUbU94lPW/40rw26DwwX1rfHFavRy/JN2VH52QKBgQDl9v4XxGjgmcted01H\nXPELsfJPod9PCr6M7swUVCRzP2m1CB9efOJBd3yzFI/beQxNjhVH3tUEioHdMgzJ\nrJHB44NyYLaVDFhIcTDlyTKlEYpd7kT5xMGqbcuLRSr0/EH9Ut69+buMqvcXCo9t\nlpgfvx5PizZ6FWCzUeR/qQ5DAwKBgQC27v9UT6dpl47DYUKls1laTsN+wiaFXiuY\n39MPGwXW4AmfY2VqrVuGaGc1+ls9Am75/BkEQr9rDmuy/p+tqeOrILGMskuSzwzO\n5NJ9DbXwgU+rBkRGYP4QdY3eYlsQQJXjrYmtXi03J6NZiVLozMN5Clk6yJLjZue0\nNPMpjl8YXQKBgGfiqmrGObKtB2hHcMu6OtJTsukycRTd/7Le9aaBVG4TyYcUgkdH\nF1cHyXeE5G/7QQmQFCEBky2X/I6WW5yHrtjuFKWI9zJh/0fKipJjz9MuF1nTl6lV\nrz90liz2NC+z/YOY+jLMLGOhoDnydVTGYTaGOgUpGJUSLzsS1ayuDFlNAoGALJRz\nrrU2pBnmFaEHH+BkHwjgxWxE/O1lDH1HLwAz3Rh939TWKzgR/OBGfrYDNAv6xXr1\nEb++bDV4c4dvnF+xdsuh2Rq+JgnFIkpLLWSA+RpaMuB1FP2gDJzJNO+dJ4nFvVVW\nHuS7ehxzx2cayFbWMBIgip3EhhxWzOi2dWMXs5UCgYA6wgiuw7w/Yw+/E6VThyPz\nzAspslQJzT4CzrW1WYwyZPCKAkdxIUd31pVtfj3M8duIpFkT5QidaBY54a3A8aU6\nY3kigasNRpSEMK7/ldMkX9pgPL6LGC1mmb7oTxIJjjAN6xcKVznF756LoK9A7+m6\nNrT7PzoKmZkC7BpmKV4CdQ==\n-----END PRIVATE KEY-----\n" +`, + skip: true, + }, + // TODO: Create an example of these. + //{ + // name: "Slack mangled email", + // input: ``, + // want: []string{""}, + //}, + //{ + // name: "Empty client email", + // input: ``, + // want: []string{""}, + //}, + //{ + // name: "Carets", + // input: ``, + // want: []string{""}, + //}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + if test.skip { + t.Skip() + return + } + matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input)) if len(matchedDetectors) == 0 { t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)