Merge branch 'main' into 3631-privatekey-detector-should-emit-some-metadata-about-the-corresponding-certificate-if-available
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user