Merge branch 'main' into 3631-privatekey-detector-should-emit-some-metadata-about-the-corresponding-certificate-if-available

This commit is contained in:
ahrav
2024-11-20 19:40:52 -08:00
committed by GitHub
8 changed files with 237 additions and 138 deletions
+2 -2
View File
@@ -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
@@ -54,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
@@ -137,6 +135,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
@@ -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
-2
View File
@@ -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=
@@ -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 != "<redacted>" {
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 {
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+56 -37
View File
@@ -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, `<mailto:`) {
creds.ClientEmail = strings.Split(strings.Split(creds.ClientEmail, `<mailto:`)[1], `|`)[0]
}
creds.AuthProviderX509CertURL = trimCarrots(creds.AuthProviderX509CertURL)
creds.AuthURI = trimCarrots(creds.AuthURI)
creds.ClientX509CertURL = trimCarrots(creds.ClientX509CertURL)
creds.TokenURI = trimCarrots(creds.TokenURI)
creds.AuthProviderX509CertURL = trimCarets(creds.AuthProviderX509CertURL)
creds.AuthURI = trimCarets(creds.AuthURI)
creds.ClientX509CertURL = trimCarets(creds.ClientX509CertURL)
creds.TokenURI = trimCarets(creds.TokenURI)
// Not sure why this might happen, but we've observed this with a verified cred
raw := []byte(creds.ClientEmail)
@@ -106,11 +109,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
Raw: raw,
RawV2: credBytes,
Redacted: creds.ClientEmail,
}
// Set the RotationGuideURL in the ExtraData
result.ExtraData = map[string]string{
"rotation_guide": "https://howtorotate.com/docs/tutorials/gcp/",
"project": creds.ProjectID,
ExtraData: map[string]string{
"rotation_guide": "https://howtorotate.com/docs/tutorials/gcp/",
"project": creds.ProjectID,
},
}
if verify {
@@ -118,12 +120,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
if err != nil {
continue
}
if credentials != nil {
_, err = credentials.TokenSource.Token()
if err == nil {
result.Verified = true
}
if _, err = credentials.TokenSource.Token(); err != nil {
continue
}
result.Verified = true
}
results = append(results, result)
@@ -136,10 +137,28 @@ func (s Scanner) IsFalsePositive(_ detectors.Result) (bool, string) {
return false, ""
}
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_GCP
// region Helper methods
func cleanInput(input string) string {
input = strings.ReplaceAll(input, `,\\n`, `\n`)
input = strings.ReplaceAll(input, `\"\\n`, `\n`)
input = strings.ReplaceAll(input, `\\"`, `"`)
// If the JSON is encoded, it needs to be unquoted for `json.Unmarshal` to succeed.
// https://github.com/trufflesecurity/trufflehog/issues/2864
if strings.Contains(input, `\"auth_provider_x509_cert_url\"`) {
unquoted, err := strconv.Unquote(`"` + input + `"`)
if err == nil {
return unquoted
}
}
return input
}
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."
func trimCarets(s string) string {
s = strings.TrimPrefix(s, "<")
s = strings.TrimSuffix(s, ">")
return s
}
//endregion
+89 -19
View File
@@ -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":"[email protected]","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":"[email protected]","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\": \"[email protected]\",\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\":\"[email protected]\",\"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: "[email protected]",
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 || '[email protected]',
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)
+1 -1
View File
@@ -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"