diff --git a/pkg/detectors/coinbase/coinbase.go b/pkg/detectors/coinbase/coinbase.go index 9726341e3..f683b1277 100644 --- a/pkg/detectors/coinbase/coinbase.go +++ b/pkg/detectors/coinbase/coinbase.go @@ -2,74 +2,205 @@ package coinbase import ( "context" + "crypto/ecdsa" + "crypto/rand" + "crypto/x509" + "encoding/pem" "fmt" + "io" "net/http" "strings" + "time" regexp "github.com/wasilibs/go-re2" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" + + "github.com/golang-jwt/jwt/v5" ) -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{"coinbase"}) + `\b([a-zA-Z-0-9]{64})\b`) + // Reference: https://docs.cdp.coinbase.com/coinbase-app/docs/auth/api-key-authentication + keyNamePat = regexp.MustCompile(`\b(organizations\\*/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\\*/apiKeys\\*/\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\b`) + privateKeyPat = regexp.MustCompile(`(-----BEGIN EC(?:DSA)? PRIVATE KEY-----(?:\r|\n|\\+r|\\+n)(?:[a-zA-Z0-9+/]+={0,2}(?:\r|\n|\\+r|\\+n))+-----END EC(?:DSA)? PRIVATE KEY-----(?:\r|\n|\\+r|\\+n)?)`) + + apiHost = "api.coinbase.com" + verificationEndpoint = "/v2/user" + verificationMethod = http.MethodGet + verificationURI = fmt.Sprintf("https://%s%s", apiHost, verificationEndpoint) + + nameReplacer = strings.NewReplacer("\\", "") + keyReplacer = strings.NewReplacer( + "\r\n", "\n", + "\\r\\n", "\n", + "\\n", "\n", + "\\r", "\n", + ) ) // Keywords are used for efficiently pre-filtering chunks. // Use identifiers in the secret preferably, or the provider name. func (s Scanner) Keywords() []string { - return []string{"coinbase"} + return []string{"begin ec"} +} + +func isValidECPrivateKey(pemKey []byte) bool { + block, _ := pem.Decode(pemKey) + if block == nil { + return false + } + + key, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return false + } + + // Check the key type + _, ok := key.Public().(*ecdsa.PublicKey) + return ok +} + +func (s Scanner) getClient() *http.Client { + if s.client != nil { + return s.client + } + return defaultClient } // FromData will find and optionally verify Coinbase 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.FindAllStringSubmatch(dataStr, -1) + uniqueKeyNames, uniquePrivateKeys := map[string]struct{}{}, map[string]struct{}{} - for _, match := range matches { - resMatch := strings.TrimSpace(match[1]) + for _, keyNameMatch := range keyNamePat.FindAllStringSubmatch(dataStr, -1) { + uniqueKeyNames[keyNameMatch[1]] = struct{}{} + } - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_Coinbase, - Raw: []byte(resMatch), - } + for _, privateKeyMatch := range privateKeyPat.FindAllStringSubmatch(dataStr, -1) { + uniquePrivateKeys[privateKeyMatch[1]] = struct{}{} + } - if verify { - req, err := http.NewRequestWithContext(ctx, "GET", "https://api.coinbase.com/v2/user", nil) - if err != nil { + for keyName := range uniqueKeyNames { + for privateKey := range uniquePrivateKeys { + client := s.getClient() + resKeyName := nameReplacer.Replace(strings.TrimSpace(keyName)) + resPrivateKey := keyReplacer.Replace(strings.TrimSpace(privateKey)) + + if !isValidECPrivateKey([]byte(resPrivateKey)) { continue } - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) - 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) + s1 := detectors.Result{ + DetectorType: detectorspb.DetectorType_Coinbase, + Raw: []byte(resPrivateKey), + RawV2: []byte(fmt.Sprintf("%s:%s", resKeyName, resPrivateKey)), + } + + if verify { + isVerified, verificationErr := s.verifyMatch(ctx, client, resKeyName, resPrivateKey) + s1.Verified = isVerified + s1.SetVerificationError(verificationErr, resPrivateKey) + } + results = append(results, s1) + + // If we've found a verified match with this ID, we don't need to look for anymore. So move on to the next ID. + if s1.Verified { + break + } + + } } return results, nil } +func (s Scanner) verifyMatch(ctx context.Context, client *http.Client, keyName, privateKey string) (bool, error) { + jwtToken, err := buildJWT(verificationMethod, apiHost, verificationEndpoint, keyName, privateKey) + if err != nil { + return false, err + } + req, err := http.NewRequestWithContext(ctx, verificationMethod, verificationURI, http.NoBody) + if err != nil { + return false, err + } + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", jwtToken)) + res, err := client.Do(req) + if err != nil { + return false, err + } + defer func() { + _, _ = io.Copy(io.Discard, res.Body) + _ = res.Body.Close() + }() + + switch res.StatusCode { + case http.StatusOK: + return true, nil + case http.StatusUnauthorized: + return false, nil + default: + return false, fmt.Errorf("unexpected status code %d", res.StatusCode) + } +} + +// Coinbase API requires the credentials encoded in a JWT token +// The JWT token is signed with the private key and expires in 2 minutes +func buildJWT(method, host, endpoint, keyName, key string) (string, error) { + // Decode the PEM key + pemStr := strings.ReplaceAll(key, `\n`, "\n") + block, _ := pem.Decode([]byte(pemStr)) + if block == nil || block.Type != "EC PRIVATE KEY" { + return "", fmt.Errorf("failed to decode PEM block containing EC private key") + } + + privateKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("failed to parse EC private key: %v", err) + } + + now := time.Now().Unix() + claims := jwt.MapClaims{ + "sub": keyName, + "iss": "cdp", + "nbf": now, + "exp": now + 120, + "uri": fmt.Sprintf("%s %s%s", method, host, endpoint), + } + + token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) + token.Header["kid"] = keyName + token.Header["nonce"] = fmt.Sprintf("%x", makeNonce()) + + signedToken, err := token.SignedString(privateKey) + if err != nil { + return "", fmt.Errorf("failed to sign JWT: %v", err) + } + + return signedToken, nil +} + +func makeNonce() []byte { + nonce := make([]byte, 16) // 128-bit nonce + _, _ = rand.Read(nonce) + return nonce +} + func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Coinbase } func (s Scanner) Description() string { - return "Coinbase is a digital currency exchange that allows users to buy, sell, and store various cryptocurrencies. A Coinbase API key can be used to access and manage a user's account and transactions." + return "Coinbase is a digital currency exchange that allows users to buy, sell, and store various cryptocurrencies. A Coinbase API key name and private key can be used to access and manage a user's account and transactions." } diff --git a/pkg/detectors/coinbase/coinbase_integration_test.go b/pkg/detectors/coinbase/coinbase_integration_test.go index 12a2cfa39..d51e352f4 100644 --- a/pkg/detectors/coinbase/coinbase_integration_test.go +++ b/pkg/detectors/coinbase/coinbase_integration_test.go @@ -6,25 +6,31 @@ package coinbase import ( "context" "fmt" + "net/http" "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/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoinbase_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) } - secret := testSecrets.MustGetField("COINBASE_TOKEN") - inactiveSecret := testSecrets.MustGetField("COINBASE_INACTIVE") + + keyName := testSecrets.MustGetField("COINBASE_KEY_NAME") + privateKey := testSecrets.MustGetField("COINBASE_PRIVATE_KEY") + inactiveKeyName := testSecrets.MustGetField("COINBASE_INACTIVE_KEY_NAME") + inactivePrivateKey := testSecrets.MustGetField("COINBASE_INACTIVE_PRIVATE_KEY") type args struct { ctx context.Context @@ -32,18 +38,19 @@ func TestCoinbase_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", s: Scanner{}, args: args{ ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a coinbase secret %s within", secret)), + data: []byte(fmt.Sprintf("You can find a coinbase secret %s %s within", keyName, privateKey)), verify: true, }, want: []detectors.Result{ @@ -52,14 +59,15 @@ func TestCoinbase_FromChunk(t *testing.T) { Verified: true, }, }, - wantErr: false, + wantErr: false, + wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a coinbase secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation + data: []byte(fmt.Sprintf("You can find a coinbase secret %s %s within but not valid", inactiveKeyName, inactivePrivateKey)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ @@ -68,7 +76,8 @@ func TestCoinbase_FromChunk(t *testing.T) { Verified: false, }, }, - wantErr: false, + wantErr: false, + wantVerificationErr: false, }, { name: "not found", @@ -78,25 +87,62 @@ func TestCoinbase_FromChunk(t *testing.T) { data: []byte("You cannot find the secret within"), verify: true, }, - want: nil, - wantErr: false, + want: nil, + 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 coinbase secret %s %s within", keyName, privateKey)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_Coinbase, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + { + name: "found, verified but unexpected api surface", + s: Scanner{client: common.ConstantResponseHttpClient(http.StatusInternalServerError, "")}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a coinbase secret %s %s within", keyName, privateKey)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_Coinbase, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, }, } 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("Coinbase.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { - t.Fatal("no raw secret present") + t.Fatalf("no raw secret present: \n %+v", got[i]) + } + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) } - got[i].Raw = nil } - if diff := pretty.Compare(got, tt.want); diff != "" { + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError", "primarySecret") + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Coinbase.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) @@ -108,8 +154,12 @@ func BenchmarkFromData(benchmark *testing.B) { s := Scanner{} for name, data := range detectors.MustGetBenchmarkData() { benchmark.Run(name, func(b *testing.B) { + b.ResetTimer() for n := 0; n < b.N; n++ { - s.FromData(ctx, false, data) + _, err := s.FromData(ctx, false, data) + if err != nil { + b.Fatal(err) + } } }) } diff --git a/pkg/detectors/coinbase/coinbase_test.go b/pkg/detectors/coinbase/coinbase_test.go index 430c41899..69666a93a 100644 --- a/pkg/detectors/coinbase/coinbase_test.go +++ b/pkg/detectors/coinbase/coinbase_test.go @@ -3,88 +3,133 @@ package coinbase import ( "context" "testing" - - "github.com/google/go-cmp/cmp" - - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick" ) -var ( - validPattern = ` - # Configuration File: config.yaml - database: - host: $DB_HOST - port: $DB_PORT - username: $DB_USERNAME - password: $DB_PASS # IMPORTANT: Do not share this password publicly - - api: - auth_type: "Bearer" - base_url: "https://api.example.com/v1/user" - coinbase_key: "IIQle6bXgoQEzxqHBt2VN0gW-Yve3t5k5VVD3nNAUUMCLVdK-3M4k6apjs43l0nM" - - # Notes: - # - Remember to rotate the secret every 90 days. - # - The above credentials should only be used in a secure environment. - ` - secret = "IIQle6bXgoQEzxqHBt2VN0gW-Yve3t5k5VVD3nNAUUMCLVdK-3M4k6apjs43l0nM" -) - -func TestCoinBase_Pattern(t *testing.T) { - d := Scanner{} - ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) - +func TestCoinbase_Pattern(t *testing.T) { tests := []struct { - name string - input string - want []string + name string + data string + shouldMatch bool + match string }{ + // True positives + // https://github.com/coinbase/waas-client-library-go/issues/41 { - name: "valid pattern", - input: validPattern, - want: []string{secret}, + name: "valid_result1", + data: `{ "name": "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f", "principal": "8feb538e-137b-5864-b12a-7c75b60fa20a", "principalType": "USER", "publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmO\nA03Az8Fpv7azpgjAy87ibgQTThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PUBLIC KEY-----\n", "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBddyynZ9Ya7op1B9nu1Dxyc1T6xLy72t45J2Smv9oXNoAoGCCqGSM49\nAwEHoUQDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmOA03Az8Fpv7azpgjAy87ibgQT\nThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PRIVATE KEY-----\n", "createTime": "2023-08-19T12:29:08.938421763Z", "projectId": "5970e137-9c3d-4adc-b65d-58d33af2432d" }`, + shouldMatch: true, + match: "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBddyynZ9Ya7op1B9nu1Dxyc1T6xLy72t45J2Smv9oXNoAoGCCqGSM49\nAwEHoUQDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmOA03Az8Fpv7azpgjAy87ibgQT\nThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PRIVATE KEY-----\n", + }, + // https://github.com/coinbase/waas-client-library-go/pull/32#issuecomment-1666415017 + { + name: "valid_result2_name_slashes", + data: `{ + "name": "organizations\/d3f266dc-0d36-4cd0-91c3-e3a292b0b4b3\/apiKeys\/032c4fdf-d763-4b0c-9ed3-ff41a873bcc8", + "principal": "5d5c9f00-3224-52a7-a1f7-9e6ce3ada40c", + "principalType": "USER", + "publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAP\niqLdg5GFVn9QAS/0oY4/fJGrCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PUBLIC KEY-----\n", + "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFkA1kU4DlNu36wTTHycWy6n1rsUH0UT8mfAKNtOukXHoAoGCCqGSM49\nAwEHoUQDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAPiqLdg5GFVn9QAS/0oY4/fJGr\nCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PRIVATE KEY-----\n", + "createTime": "2023-08-05T06:34:40.265235553Z", + "projectId": "64b3f391-c69d-4c59-91a2-75816c1a0738" + }`, + shouldMatch: true, + match: "organizations/d3f266dc-0d36-4cd0-91c3-e3a292b0b4b3/apiKeys/032c4fdf-d763-4b0c-9ed3-ff41a873bcc8:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFkA1kU4DlNu36wTTHycWy6n1rsUH0UT8mfAKNtOukXHoAoGCCqGSM49\nAwEHoUQDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAPiqLdg5GFVn9QAS/0oY4/fJGr\nCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PRIVATE KEY-----\n", + }, + { + name: "valid_result3", + data: `name: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", + description: "principal": "775fb863-004f-5412-8e4c-e9449c612563" and install dependencies + + runs: "principalType": "USER", + using: composite + steps:"publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b\n/g82Lmz3HpATKFmrICcOBX2lRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PUBLIC KEY-----\n", + - name: Setup Node.js + uses: actions/setup-node@v3 + with: "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIKOQ7lvGL0EiUzZ23pmH/NBPRwVV8yZsqofds5bSR9qFoAoGCCqGSM49\nAwEHoUQDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b/g82Lmz3HpATKFmrICcOBX2l\nRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PRIVATE KEY-----\n", + node-version-file: .nvmrc + + - name: Cache dependencies`, + shouldMatch: true, + match: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIKOQ7lvGL0EiUzZ23pmH/NBPRwVV8yZsqofds5bSR9qFoAoGCCqGSM49\nAwEHoUQDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b/g82Lmz3HpATKFmrICcOBX2l\nRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PRIVATE KEY-----\n", + }, + { + name: "valid_result_ecdsa", + data: `{ + "name": "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", + "privateKey": "-----BEGIN ECDSA PRIVATE KEY-----\nMHcCAQEEINQdZMbF2r07KF0mxfLYt9Y1PNaC0C6UpZ31MxD4NEE8oAoGCCqGSM49\nAwEHoUQDQgAEeRFgMrQEHI/APWaziRH90jN7EozjdbPVxvzc1F4zqWTeCtLASwqA\nqnMugYX2epqsFhGn82xNXu2NwgORc6embQ==\n-----END ECDSA PRIVATE KEY-----\n" +}`, + shouldMatch: true, + match: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN ECDSA PRIVATE KEY-----\nMHcCAQEEINQdZMbF2r07KF0mxfLYt9Y1PNaC0C6UpZ31MxD4NEE8oAoGCCqGSM49\nAwEHoUQDQgAEeRFgMrQEHI/APWaziRH90jN7EozjdbPVxvzc1F4zqWTeCtLASwqA\nqnMugYX2epqsFhGn82xNXu2NwgORc6embQ==\n-----END ECDSA PRIVATE KEY-----\n", + }, + // TODO: Is it worth supporting case-insensitive headers? + // https://github.com/coinbase/waas-sdk-react-native/blob/bbaf597e73d02ecaf64161061e71b85d9eeeb9d6/example/src/.coinbase_cloud_api_key.json#L4 + // { + // name: "valid_result_case_insensitive", + // data: `{ + // "name": "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", + // "privateKey": "-----BEGIN ECDSA private key-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8id7yCfmNp0ppczu\nDhjB1pesdDB6Uwuz6KxARrenNfyhRANCAASI6DBntdr+XSOaK55J++x8ORuDxn81\nENa0RmGFjTwu4vQcWcx5rrIWNh6b7FPxy6mrZl0n3rswEtZmUci8Y5HX\n-----END ECDSA PRIVATE KEY-----\n" + //}`, + // shouldMatch: true, + // match: "organizations/7eegad2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN ECDSA PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8id7yCfmNp0ppczu\nDhjB1pesdDB6Uwuz6KxARrenNfyhRANCAASI6DBntdr+XSOaK55J++x8ORuDxn81\nENa0RmGFjTwu4vQcWcx5rrIWNh6b7FPxy6mrZl0n3rswEtZmUci8Y5HX\n-----END ECDSA PRIVATE KEY-----\n", + // }, + + // False positives + // https://github.com/coinbase/waas-client-library-go/blob/main/example.go + { + name: `invalid_key_name1`, + data: `const ( + // apiKeyName is the name of the API Key to use. Fill this out before running the main function. + apiKeyName = "organizations/my-organization/apiKeys/my-api-key" + + // privKeyTemplate is the private key of the API Key to use. Fill this out before running the main function. + privKeyTemplate = "-----BEGIN EC PRIVATE KEY-----\nmy-private-key\n-----END EC PRIVATE KEY-----\n" +)`, + shouldMatch: false, + }, + // https://github.com/coinbase/waas-sdk-react-native/blob/bbaf597e73d02ecaf64161061e71b85d9eeeb9d6/example/src/.coinbase_cloud_api_key.json#L4 + { + name: `invalid_key_name2`, + data: `{ + "name": "organizations/organizationID/apiKeys/apiKeyName", + "privateKey": "-----BEGIN ECDSA Private Key-----ExamplePrivateKey-----END ECDSA Private Key-----\n" +}`, + }, + { + name: `invalid_private_key`, + data: `{ "name": "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f", "principal": "8feb538e-137b-5864-b12a-7c75b60fa20a", "principalType": "USER", "publicKey": "-----BEGIN EC PUBLIC KEY-----\ninvalid\n-----END EC PUBLIC KEY-----\n", "privateKey": "-----BEGIN EC PRIVATE KEY-----\ninvalid\n-----END EC PRIVATE KEY-----\n", "createTime": "2023-08-19T12:29:08.938421763Z", "projectId": "5970e137-9c3d-4adc-b65d-58d33af2432d" }`, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input)) - if len(matchedDetectors) == 0 { - t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input) - return - } + s := Scanner{} - results, err := d.FromData(context.Background(), false, []byte(test.input)) + results, err := s.FromData(context.Background(), false, []byte(test.data)) if err != nil { - t.Errorf("error = %v", err) + t.Errorf("Coinbase.FromData() error = %v", err) return } - if len(results) != len(test.want) { + if test.shouldMatch { if len(results) == 0 { - t.Errorf("did not receive result") - } else { - t.Errorf("expected %d results, only received %d", len(test.want), len(results)) + t.Errorf("%s: did not receive a match for '%v' when one was expected", test.name, test.data) + return } - return - } - - actual := make(map[string]struct{}, len(results)) - for _, r := range results { - if len(r.RawV2) > 0 { - actual[string(r.RawV2)] = struct{}{} - } else { - actual[string(r.Raw)] = struct{}{} + expected := test.data + if test.match != "" { + expected = test.match + } + result := results[0] + resultData := string(result.RawV2) + if resultData != expected { + t.Errorf("%s: did not receive expected match.\n\texpected: '%s'\n\t actual: '%s'", test.name, expected, resultData) + return + } + } else { + if len(results) > 0 { + t.Errorf("%s: received a match for '%v' when one wasn't wanted", test.name, test.data) + return } - } - expected := make(map[string]struct{}, len(test.want)) - for _, v := range test.want { - expected[v] = struct{}{} - } - - if diff := cmp.Diff(expected, actual); diff != "" { - t.Errorf("%s diff: (-want +got)\n%s", test.name, diff) } }) } diff --git a/pkg/detectors/coinbase_waas/coinbase_waas.go b/pkg/detectors/coinbase_waas/coinbase_waas.go deleted file mode 100644 index 3f3f4cfed..000000000 --- a/pkg/detectors/coinbase_waas/coinbase_waas.go +++ /dev/null @@ -1,153 +0,0 @@ -package coinbase_waas - -import ( - "context" - "crypto/ecdsa" - "crypto/x509" - "encoding/pem" - "errors" - "net/http" - "strings" - - regexp "github.com/wasilibs/go-re2" - - "github.com/coinbase/waas-client-library-go/auth" - "github.com/coinbase/waas-client-library-go/clients" - v1clients "github.com/coinbase/waas-client-library-go/clients/v1" - v1 "github.com/coinbase/waas-client-library-go/gen/go/coinbase/cloud/pools/v1" - "github.com/google/uuid" - "google.golang.org/api/googleapi" - - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" -) - -type Scanner struct { - client *http.Client -} - -// Ensure the Scanner satisfies the interface at compile time. -var _ detectors.Detector = (*Scanner)(nil) - -var ( - // Reference: https://docs.cloud.coinbase.com/waas/docs/auth - keyNamePat = regexp.MustCompile(`(organizations\\*/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\\*/apiKeys\\*/\w{8}-\w{4}-\w{4}-\w{4}-\w{12})`) - privKeyPat = regexp.MustCompile(`(-----BEGIN EC(?:DSA)? PRIVATE KEY-----(?:\r|\n|\\+r|\\+n)(?:[a-zA-Z0-9+/]+={0,2}(?:\r|\n|\\+r|\\+n))+-----END EC(?:DSA)? PRIVATE KEY-----(?:\r|\n|\\+r|\\+n)?)`) - - nameReplacer = strings.NewReplacer("\\", "") - keyReplacer = strings.NewReplacer( - "\r\n", "\n", - "\\r\\n", "\n", - "\\n", "\n", - "\\r", "\n", - ) -) - -// Keywords are used for efficiently pre-filtering chunks. -// Use identifiers in the secret preferably, or the provider name. -func (s Scanner) Keywords() []string { - return []string{"organizations", "apiKeys", "begin ec"} -} - -const maxPrivateKeySize = 4096 - -// MaxSecretSize returns the maximum size of a secret that this detector can find. -func (s Scanner) MaxSecretSize() int64 { return maxPrivateKeySize } - -// FromData will find and optionally verify CoinbaseWaaS 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) - - keyNameMatches := keyNamePat.FindAllStringSubmatch(dataStr, -1) - privKeyMatches := privKeyPat.FindAllStringSubmatch(dataStr, -1) - - for _, keyNameMatch := range keyNameMatches { - resKeyNameMatch := nameReplacer.Replace(strings.TrimSpace(keyNameMatch[1])) - - for _, privKeyMatch := range privKeyMatches { - resPrivKeyMatch := keyReplacer.Replace(strings.TrimSpace(privKeyMatch[1])) - - if !isValidECPrivateKey([]byte(resPrivKeyMatch)) { - continue - } - - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_CoinbaseWaaS, - Raw: []byte(resPrivKeyMatch), - RawV2: []byte(resKeyNameMatch + ":" + resPrivKeyMatch), - } - - if verify { - isVerified, verificationErr := s.verifyMatch(ctx, resKeyNameMatch, resPrivKeyMatch) - s1.Verified = isVerified - s1.SetVerificationError(verificationErr, resPrivKeyMatch) - } - results = append(results, s1) - - // If we've found a verified match with this ID, we don't need to look for anymore. So move on to the next ID. - if s1.Verified { - break - } - } - } - - return results, nil -} - -func isValidECPrivateKey(pemKey []byte) bool { - block, _ := pem.Decode(pemKey) - if block == nil { - return false - } - - key, err := x509.ParseECPrivateKey(block.Bytes) - if err != nil { - return false - } - - // Check the key type - if _, ok := key.Public().(*ecdsa.PublicKey); !ok { - return false - } - - return true -} - -func (s Scanner) verifyMatch(ctx context.Context, apiKeyName, privKey string) (bool, error) { - authOpt := clients.WithAPIKey(&auth.APIKey{ - Name: apiKeyName, - PrivateKey: privKey, - }) - clientOpt := clients.WithHTTPClient(s.client) - client, err := v1clients.NewPoolServiceClient(ctx, authOpt, clientOpt) - if err != nil { - return false, err - } - - // Lookup an arbitrary pool name that shouldn't exist. - _, err = client.GetPool(ctx, &v1.GetPoolRequest{Name: uuid.New().String()}) - if err != nil { - var apiErr *googleapi.Error - if errors.As(err, &apiErr) { - if apiErr.Code == 401 { - // Invalid |Name| or |PrivateKey| - return false, nil - } else if apiErr.Code == 404 { - // Valid |Name| and |PrivateKey| but the pool doesn't exist (expected). - return true, nil - } - } - // Unhandled error. - return false, err - } - // In theory this will never happen, but it also indicates a valid key. - return true, nil -} - -func (s Scanner) Type() detectorspb.DetectorType { - return detectorspb.DetectorType_CoinbaseWaaS -} - -func (s Scanner) Description() string { - return "Coinbase WaaS (Wallet as a Service) provides a secure and scalable infrastructure for managing cryptocurrency wallets. The API keys allow access to this service to perform operations such as creating and managing wallets." -} diff --git a/pkg/detectors/coinbase_waas/coinbase_waas_integration_test.go b/pkg/detectors/coinbase_waas/coinbase_waas_integration_test.go deleted file mode 100644 index 9aa6edacb..000000000 --- a/pkg/detectors/coinbase_waas/coinbase_waas_integration_test.go +++ /dev/null @@ -1,174 +0,0 @@ -//go:build detectors -// +build detectors - -package coinbase_waas - -import ( - "context" - "encoding/base64" - "fmt" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" -) - -func TestCoinbaseWaaS_FromChunk(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") - if err != nil { - t.Fatalf("could not get test secrets from GCP: %s", err) - } - secretb64 := testSecrets.MustGetField("COINBASE_WAAS") - secretB, err := base64.StdEncoding.DecodeString(secretb64) - if err != nil { - t.Fatalf("could not decode secret: %s", err) - } - secret := string(secretB) - - inactiveSecretb64 := testSecrets.MustGetField("COINBASE_WAAS_INACTIVE") - inactiveSecretB, err := base64.StdEncoding.DecodeString(inactiveSecretb64) - if err != nil { - t.Fatalf("could not decode secret: %s", err) - } - inactiveSecret := string(inactiveSecretB) - - type args struct { - ctx context.Context - data []byte - verify bool - } - tests := []struct { - name string - s Scanner - args args - want []detectors.Result - wantErr bool - wantVerificationErr bool - }{ - { - name: "found, verified", - s: Scanner{}, - args: args{ - ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a coinbase_waas secret %s within", secret)), - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_CoinbaseWaaS, - Verified: true, - }, - }, - wantErr: false, - wantVerificationErr: false, - }, - { - name: "found, unverified", - s: Scanner{}, - args: args{ - ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a coinbase_waas secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_CoinbaseWaaS, - Verified: false, - }, - }, - wantErr: false, - wantVerificationErr: false, - }, - { - name: "not found", - s: Scanner{}, - args: args{ - ctx: context.Background(), - data: []byte("You cannot find the secret within"), - verify: true, - }, - want: nil, - 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 coinbase_waas secret %s within", secret)), - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_CoinbaseWaaS, - Verified: false, - }, - }, - wantErr: false, - wantVerificationErr: true, - }, - { - name: "found, verified but unexpected api surface", - s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, - args: args{ - ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a coinbase_waas secret %s within", secret)), - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_CoinbaseWaaS, - Verified: false, - }, - }, - wantErr: false, - wantVerificationErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) - if (err != nil) != tt.wantErr { - t.Errorf("Coinbasewaas.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]) - } - if (got[i].VerificationError() != nil) != tt.wantVerificationErr { - t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) - } - } - ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError") - if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { - t.Errorf("Coinbasewaas.FromData() %s diff: (-got +want)\n%s", tt.name, diff) - } - }) - } -} - -func BenchmarkFromData(benchmark *testing.B) { - ctx := context.Background() - s := Scanner{} - for name, data := range detectors.MustGetBenchmarkData() { - benchmark.Run(name, func(b *testing.B) { - b.ResetTimer() - for n := 0; n < b.N; n++ { - _, err := s.FromData(ctx, false, data) - if err != nil { - b.Fatal(err) - } - } - }) - } -} diff --git a/pkg/detectors/coinbase_waas/coinbase_waas_test.go b/pkg/detectors/coinbase_waas/coinbase_waas_test.go deleted file mode 100644 index e2948a639..000000000 --- a/pkg/detectors/coinbase_waas/coinbase_waas_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package coinbase_waas - -import ( - "context" - "testing" -) - -func TestCoinbaseWaaS_Pattern(t *testing.T) { - tests := []struct { - name string - data string - shouldMatch bool - match string - }{ - // True positives - // https://github.com/coinbase/waas-client-library-go/issues/41 - { - name: "valid_result1", - data: `{ "name": "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f", "principal": "8feb538e-137b-5864-b12a-7c75b60fa20a", "principalType": "USER", "publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmO\nA03Az8Fpv7azpgjAy87ibgQTThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PUBLIC KEY-----\n", "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBddyynZ9Ya7op1B9nu1Dxyc1T6xLy72t45J2Smv9oXNoAoGCCqGSM49\nAwEHoUQDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmOA03Az8Fpv7azpgjAy87ibgQT\nThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PRIVATE KEY-----\n", "createTime": "2023-08-19T12:29:08.938421763Z", "projectId": "5970e137-9c3d-4adc-b65d-58d33af2432d" }`, - shouldMatch: true, - match: "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBddyynZ9Ya7op1B9nu1Dxyc1T6xLy72t45J2Smv9oXNoAoGCCqGSM49\nAwEHoUQDQgAEzR0G+CW0uVJFrpLUELqB+DlsmGmOA03Az8Fpv7azpgjAy87ibgQT\nThaQy1C1BccbCDkPoEs6mOnDkOebkybAKQ==\n-----END EC PRIVATE KEY-----\n", - }, - // https://github.com/coinbase/waas-client-library-go/pull/32#issuecomment-1666415017 - { - name: "valid_result2_name_slashes", - data: `{ - "name": "organizations\/d3f266dc-0d36-4cd0-91c3-e3a292b0b4b3\/apiKeys\/032c4fdf-d763-4b0c-9ed3-ff41a873bcc8", - "principal": "5d5c9f00-3224-52a7-a1f7-9e6ce3ada40c", - "principalType": "USER", - "publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAP\niqLdg5GFVn9QAS/0oY4/fJGrCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PUBLIC KEY-----\n", - "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFkA1kU4DlNu36wTTHycWy6n1rsUH0UT8mfAKNtOukXHoAoGCCqGSM49\nAwEHoUQDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAPiqLdg5GFVn9QAS/0oY4/fJGr\nCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PRIVATE KEY-----\n", - "createTime": "2023-08-05T06:34:40.265235553Z", - "projectId": "64b3f391-c69d-4c59-91a2-75816c1a0738" - }`, - shouldMatch: true, - match: "organizations/d3f266dc-0d36-4cd0-91c3-e3a292b0b4b3/apiKeys/032c4fdf-d763-4b0c-9ed3-ff41a873bcc8:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFkA1kU4DlNu36wTTHycWy6n1rsUH0UT8mfAKNtOukXHoAoGCCqGSM49\nAwEHoUQDQgAEAjw43hwOqS2PF4gAFbhoxIJqCHAPiqLdg5GFVn9QAS/0oY4/fJGr\nCn9rpQGOvHxHf1mtQ6j4bIWN1AtHvA/3uw==\n-----END EC PRIVATE KEY-----\n", - }, - { - name: "valid_result3", - data: `name: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", - description: "principal": "775fb863-004f-5412-8e4c-e9449c612563" and install dependencies - - runs: "principalType": "USER", - using: composite - steps:"publicKey": "-----BEGIN EC PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b\n/g82Lmz3HpATKFmrICcOBX2lRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PUBLIC KEY-----\n", - - name: Setup Node.js - uses: actions/setup-node@v3 - with: "privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIKOQ7lvGL0EiUzZ23pmH/NBPRwVV8yZsqofds5bSR9qFoAoGCCqGSM49\nAwEHoUQDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b/g82Lmz3HpATKFmrICcOBX2l\nRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PRIVATE KEY-----\n", - node-version-file: .nvmrc - - - name: Cache dependencies`, - shouldMatch: true, - match: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIKOQ7lvGL0EiUzZ23pmH/NBPRwVV8yZsqofds5bSR9qFoAoGCCqGSM49\nAwEHoUQDQgAEvHsvI08kox+n/8wSMFwCbK5hEf5b/g82Lmz3HpATKFmrICcOBX2l\nRHo99JWRrupmjUGxnD8i4sj4mZafTEokhA==\n-----END EC PRIVATE KEY-----\n", - }, - { - name: "valid_result_ecdsa", - data: `{ - "name": "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", - "privateKey": "-----BEGIN ECDSA PRIVATE KEY-----\nMHcCAQEEINQdZMbF2r07KF0mxfLYt9Y1PNaC0C6UpZ31MxD4NEE8oAoGCCqGSM49\nAwEHoUQDQgAEeRFgMrQEHI/APWaziRH90jN7EozjdbPVxvzc1F4zqWTeCtLASwqA\nqnMugYX2epqsFhGn82xNXu2NwgORc6embQ==\n-----END ECDSA PRIVATE KEY-----\n" -}`, - shouldMatch: true, - match: "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN ECDSA PRIVATE KEY-----\nMHcCAQEEINQdZMbF2r07KF0mxfLYt9Y1PNaC0C6UpZ31MxD4NEE8oAoGCCqGSM49\nAwEHoUQDQgAEeRFgMrQEHI/APWaziRH90jN7EozjdbPVxvzc1F4zqWTeCtLASwqA\nqnMugYX2epqsFhGn82xNXu2NwgORc6embQ==\n-----END ECDSA PRIVATE KEY-----\n", - }, - // TODO: Is it worth supporting case-insensitive headers? - // https://github.com/coinbase/waas-sdk-react-native/blob/bbaf597e73d02ecaf64161061e71b85d9eeeb9d6/example/src/.coinbase_cloud_api_key.json#L4 - // { - // name: "valid_result_case_insensitive", - // data: `{ - // "name": "organizations/7eead2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9", - // "privateKey": "-----BEGIN ECDSA private key-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8id7yCfmNp0ppczu\nDhjB1pesdDB6Uwuz6KxARrenNfyhRANCAASI6DBntdr+XSOaK55J++x8ORuDxn81\nENa0RmGFjTwu4vQcWcx5rrIWNh6b7FPxy6mrZl0n3rswEtZmUci8Y5HX\n-----END ECDSA PRIVATE KEY-----\n" - //}`, - // shouldMatch: true, - // match: "organizations/7eegad2d5-fa48-4423-8f40-c70d8ce398ae/apiKeys/7b9516b6-d82e-44e8-bed5-89b160452ed9:-----BEGIN ECDSA PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8id7yCfmNp0ppczu\nDhjB1pesdDB6Uwuz6KxARrenNfyhRANCAASI6DBntdr+XSOaK55J++x8ORuDxn81\nENa0RmGFjTwu4vQcWcx5rrIWNh6b7FPxy6mrZl0n3rswEtZmUci8Y5HX\n-----END ECDSA PRIVATE KEY-----\n", - // }, - - // False positives - // https://github.com/coinbase/waas-client-library-go/blob/main/example.go - { - name: `invalid_key_name1`, - data: `const ( - // apiKeyName is the name of the API Key to use. Fill this out before running the main function. - apiKeyName = "organizations/my-organization/apiKeys/my-api-key" - - // privKeyTemplate is the private key of the API Key to use. Fill this out before running the main function. - privKeyTemplate = "-----BEGIN EC PRIVATE KEY-----\nmy-private-key\n-----END EC PRIVATE KEY-----\n" -)`, - shouldMatch: false, - }, - // https://github.com/coinbase/waas-sdk-react-native/blob/bbaf597e73d02ecaf64161061e71b85d9eeeb9d6/example/src/.coinbase_cloud_api_key.json#L4 - { - name: `invalid_key_name2`, - data: `{ - "name": "organizations/organizationID/apiKeys/apiKeyName", - "privateKey": "-----BEGIN ECDSA Private Key-----ExamplePrivateKey-----END ECDSA Private Key-----\n" -}`, - }, - { - name: `invalid_private_key`, - data: `{ "name": "organizations/14d1742b-3575-4490-b9bc-a8a9c7e4973d/apiKeys/7473d38c-80c6-4a69-a715-1ea8fd950f6f", "principal": "8feb538e-137b-5864-b12a-7c75b60fa20a", "principalType": "USER", "publicKey": "-----BEGIN EC PUBLIC KEY-----\ninvalid\n-----END EC PUBLIC KEY-----\n", "privateKey": "-----BEGIN EC PRIVATE KEY-----\ninvalid\n-----END EC PRIVATE KEY-----\n", "createTime": "2023-08-19T12:29:08.938421763Z", "projectId": "5970e137-9c3d-4adc-b65d-58d33af2432d" }`, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := Scanner{} - - results, err := s.FromData(context.Background(), false, []byte(test.data)) - if err != nil { - t.Errorf("CoinbaseWaaS.FromData() error = %v", err) - return - } - - if test.shouldMatch { - if len(results) == 0 { - t.Errorf("%s: did not receive a match for '%v' when one was expected", test.name, test.data) - return - } - expected := test.data - if test.match != "" { - expected = test.match - } - result := results[0] - resultData := string(result.RawV2) - if resultData != expected { - t.Errorf("%s: did not receive expected match.\n\texpected: '%s'\n\t actual: '%s'", test.name, expected, resultData) - return - } - } else { - if len(results) > 0 { - t.Errorf("%s: received a match for '%v' when one wasn't wanted", test.name, test.data) - return - } - } - }) - } -} diff --git a/pkg/engine/defaults/defaults.go b/pkg/engine/defaults/defaults.go index 88718488c..dfa4fe174 100644 --- a/pkg/engine/defaults/defaults.go +++ b/pkg/engine/defaults/defaults.go @@ -175,7 +175,6 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/codequiry" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinapi" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinbase" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinbase_waas" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinlayer" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinlib" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/collect2" @@ -1028,7 +1027,6 @@ func buildDetectorList() []detectors.Detector { &codequiry.Scanner{}, &coinapi.Scanner{}, &coinbase.Scanner{}, - &coinbase_waas.Scanner{}, &coinlayer.Scanner{}, &coinlib.Scanner{}, &collect2.Scanner{}, diff --git a/pkg/pb/detectorspb/detectors.pb.go b/pkg/pb/detectorspb/detectors.pb.go index b2c7f3f53..d7444bcf0 100644 --- a/pkg/pb/detectorspb/detectors.pb.go +++ b/pkg/pb/detectorspb/detectors.pb.go @@ -1015,46 +1015,47 @@ const ( DetectorType_Moralis DetectorType = 909 DetectorType_BscScan DetectorType = 910 // Deprecated: Marked as deprecated in detectors.proto. - DetectorType_CoinMarketCap DetectorType = 911 - DetectorType_Percy DetectorType = 912 - DetectorType_TinesWebhook DetectorType = 913 - DetectorType_Pulumi DetectorType = 914 - DetectorType_SupabaseToken DetectorType = 915 - DetectorType_NuGetApiKey DetectorType = 916 - DetectorType_Aiven DetectorType = 917 - DetectorType_Prefect DetectorType = 918 - DetectorType_Docusign DetectorType = 919 - DetectorType_Couchbase DetectorType = 920 - DetectorType_Dockerhub DetectorType = 921 - DetectorType_TrufflehogEnterprise DetectorType = 922 - DetectorType_EnvoyApiKey DetectorType = 923 - DetectorType_GitHubOauth2 DetectorType = 924 - DetectorType_Salesforce DetectorType = 925 - DetectorType_HuggingFace DetectorType = 926 - DetectorType_Snowflake DetectorType = 927 - DetectorType_Sourcegraph DetectorType = 928 - DetectorType_Tailscale DetectorType = 929 - DetectorType_Web3Storage DetectorType = 930 - DetectorType_AzureStorage DetectorType = 931 - DetectorType_PlanetScaleDb DetectorType = 932 - DetectorType_Anthropic DetectorType = 933 - DetectorType_Ramp DetectorType = 934 - DetectorType_Klaviyo DetectorType = 935 - DetectorType_SourcegraphCody DetectorType = 936 - DetectorType_Voiceflow DetectorType = 937 - DetectorType_Privacy DetectorType = 938 - DetectorType_IPInfo DetectorType = 939 - DetectorType_Ip2location DetectorType = 940 - DetectorType_Instamojo DetectorType = 941 - DetectorType_Portainer DetectorType = 942 - DetectorType_PortainerToken DetectorType = 943 - DetectorType_Loggly DetectorType = 944 - DetectorType_OpenVpn DetectorType = 945 - DetectorType_VagrantCloudPersonalToken DetectorType = 946 - DetectorType_BetterStack DetectorType = 947 - DetectorType_ZeroTier DetectorType = 948 - DetectorType_AppOptics DetectorType = 949 - DetectorType_Metabase DetectorType = 950 + DetectorType_CoinMarketCap DetectorType = 911 + DetectorType_Percy DetectorType = 912 + DetectorType_TinesWebhook DetectorType = 913 + DetectorType_Pulumi DetectorType = 914 + DetectorType_SupabaseToken DetectorType = 915 + DetectorType_NuGetApiKey DetectorType = 916 + DetectorType_Aiven DetectorType = 917 + DetectorType_Prefect DetectorType = 918 + DetectorType_Docusign DetectorType = 919 + DetectorType_Couchbase DetectorType = 920 + DetectorType_Dockerhub DetectorType = 921 + DetectorType_TrufflehogEnterprise DetectorType = 922 + DetectorType_EnvoyApiKey DetectorType = 923 + DetectorType_GitHubOauth2 DetectorType = 924 + DetectorType_Salesforce DetectorType = 925 + DetectorType_HuggingFace DetectorType = 926 + DetectorType_Snowflake DetectorType = 927 + DetectorType_Sourcegraph DetectorType = 928 + DetectorType_Tailscale DetectorType = 929 + DetectorType_Web3Storage DetectorType = 930 + DetectorType_AzureStorage DetectorType = 931 + DetectorType_PlanetScaleDb DetectorType = 932 + DetectorType_Anthropic DetectorType = 933 + DetectorType_Ramp DetectorType = 934 + DetectorType_Klaviyo DetectorType = 935 + DetectorType_SourcegraphCody DetectorType = 936 + DetectorType_Voiceflow DetectorType = 937 + DetectorType_Privacy DetectorType = 938 + DetectorType_IPInfo DetectorType = 939 + DetectorType_Ip2location DetectorType = 940 + DetectorType_Instamojo DetectorType = 941 + DetectorType_Portainer DetectorType = 942 + DetectorType_PortainerToken DetectorType = 943 + DetectorType_Loggly DetectorType = 944 + DetectorType_OpenVpn DetectorType = 945 + DetectorType_VagrantCloudPersonalToken DetectorType = 946 + DetectorType_BetterStack DetectorType = 947 + DetectorType_ZeroTier DetectorType = 948 + DetectorType_AppOptics DetectorType = 949 + DetectorType_Metabase DetectorType = 950 + // Deprecated: Marked as deprecated in detectors.proto. DetectorType_CoinbaseWaaS DetectorType = 951 DetectorType_LemonSqueezy DetectorType = 952 DetectorType_Budibase DetectorType = 953 @@ -3649,7 +3650,7 @@ var file_detectors_proto_rawDesc = []byte{ 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x41, 0x53, 0x45, 0x36, 0x34, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x54, 0x46, 0x31, 0x36, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x53, 0x43, 0x41, 0x50, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x49, 0x43, 0x4f, 0x44, 0x45, - 0x10, 0x04, 0x2a, 0x81, 0x85, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x10, 0x04, 0x2a, 0x85, 0x85, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x4d, 0x51, 0x50, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x10, 0x03, 0x12, @@ -4610,114 +4611,115 @@ var file_detectors_proto_rawDesc = []byte{ 0x42, 0x65, 0x74, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xb3, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x5a, 0x65, 0x72, 0x6f, 0x54, 0x69, 0x65, 0x72, 0x10, 0xb4, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x4f, 0x70, 0x74, 0x69, 0x63, 0x73, 0x10, 0xb5, 0x07, 0x12, 0x0d, 0x0a, - 0x08, 0x4d, 0x65, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x10, 0xb6, 0x07, 0x12, 0x11, 0x0a, 0x0c, - 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x57, 0x61, 0x61, 0x53, 0x10, 0xb7, 0x07, 0x12, - 0x11, 0x0a, 0x0c, 0x4c, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x79, 0x10, - 0xb8, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x75, 0x64, 0x69, 0x62, 0x61, 0x73, 0x65, 0x10, 0xb9, - 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x44, 0x65, 0x6e, 0x6f, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x10, - 0xba, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x69, 0x70, 0x6f, 0x10, 0xbb, 0x07, 0x12, - 0x0c, 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x49, 0x4f, 0x10, 0xbc, 0x07, 0x12, 0x0f, 0x0a, - 0x0a, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x10, 0xbd, 0x07, 0x12, 0x1b, - 0x0a, 0x16, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x10, 0xbe, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x41, - 0x57, 0x53, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xbf, 0x07, 0x12, - 0x09, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x61, 0x10, 0xc0, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x4c, 0x6f, - 0x67, 0x7a, 0x49, 0x4f, 0x10, 0xc1, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x62, 0x72, 0x69, 0x74, 0x65, 0x10, 0xc2, 0x07, 0x12, 0x1a, 0x0a, 0x15, 0x47, 0x72, 0x61, 0x66, - 0x61, 0x6e, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x10, 0xc3, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, - 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x10, 0xc4, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x4f, 0x76, 0x65, - 0x72, 0x6c, 0x6f, 0x6f, 0x70, 0x10, 0xc5, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x4e, 0x67, 0x72, 0x6f, - 0x6b, 0x10, 0xc6, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x10, 0xc7, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73, - 0x10, 0xc8, 0x07, 0x12, 0x2a, 0x0a, 0x25, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x10, 0xc9, 0x07, 0x12, - 0x20, 0x0a, 0x1b, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x46, 0x6f, 0x72, - 0x52, 0x65, 0x64, 0x69, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x10, 0xca, - 0x07, 0x12, 0x21, 0x0a, 0x1c, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x44, 0x42, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x10, 0xcb, 0x07, 0x12, 0x23, 0x0a, 0x1e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x44, 0x65, 0x76, - 0x6f, 0x70, 0x73, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xcc, 0x07, 0x12, 0x15, 0x0a, 0x10, 0x41, 0x7a, 0x75, - 0x72, 0x65, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xcd, 0x07, - 0x12, 0x2c, 0x0a, 0x27, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4d, 0x4c, 0x57, 0x65, 0x62, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x10, 0xce, 0x07, 0x12, 0x12, - 0x0a, 0x0d, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x61, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, - 0xcf, 0x07, 0x12, 0x18, 0x0a, 0x13, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xd0, 0x07, 0x12, 0x18, 0x0a, 0x13, - 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x4b, 0x65, 0x79, 0x10, 0xd1, 0x07, 0x12, 0x1f, 0x0a, 0x1a, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x10, 0xd2, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x53, 0x51, 0x4c, 0x10, 0xd3, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x46, 0x6c, 0x79, 0x49, 0x4f, 0x10, - 0xd4, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x57, 0x69, 0x74, 0x68, 0x10, - 0xd5, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x4a, 0x75, 0x70, 0x69, 0x74, 0x65, 0x72, 0x4f, 0x6e, 0x65, - 0x10, 0xd6, 0x07, 0x12, 0x25, 0x0a, 0x20, 0x47, 0x43, 0x50, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x10, 0xd7, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x57, 0x69, - 0x7a, 0x10, 0xd8, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x67, 0x61, 0x72, 0x6d, 0x65, 0x10, - 0xd9, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4f, 0x6e, 0x66, 0x6c, 0x65, 0x65, 0x74, 0x10, 0xda, 0x07, - 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x34, 0x32, 0x10, 0xdb, 0x07, 0x12, 0x09, - 0x0a, 0x04, 0x47, 0x72, 0x6f, 0x71, 0x10, 0xdc, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x54, 0x77, 0x69, - 0x74, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x6b, 0x65, 0x79, 0x10, - 0xdd, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x45, 0x72, 0x61, 0x73, 0x65, 0x72, 0x10, 0xde, 0x07, 0x12, - 0x0e, 0x0a, 0x09, 0x4c, 0x61, 0x72, 0x6b, 0x53, 0x75, 0x69, 0x74, 0x65, 0x10, 0xdf, 0x07, 0x12, - 0x14, 0x0a, 0x0f, 0x4c, 0x61, 0x72, 0x6b, 0x53, 0x75, 0x69, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, - 0x65, 0x79, 0x10, 0xe0, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x4c, 0x61, - 0x62, 0x73, 0x10, 0xe1, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x45, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x10, 0xe2, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x4e, 0x65, 0x74, 0x73, 0x75, 0x69, - 0x74, 0x65, 0x10, 0xe3, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x52, 0x6f, 0x62, 0x69, 0x6e, 0x68, 0x6f, - 0x6f, 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x10, 0xe4, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x4e, - 0x56, 0x41, 0x50, 0x49, 0x10, 0xe5, 0x07, 0x12, 0x09, 0x0a, 0x04, 0x50, 0x79, 0x50, 0x49, 0x10, - 0xe6, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x52, 0x61, 0x69, 0x6c, 0x77, 0x61, 0x79, 0x41, 0x70, 0x70, - 0x10, 0xe7, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x4d, 0x65, 0x72, 0x61, 0x6b, 0x69, 0x10, 0xe8, 0x07, - 0x12, 0x15, 0x0a, 0x10, 0x53, 0x61, 0x6c, 0x61, 0x64, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x70, - 0x69, 0x4b, 0x65, 0x79, 0x10, 0xe9, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x42, 0x6f, 0x78, 0x10, 0xea, - 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x6f, 0x78, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x10, 0xeb, 0x07, - 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x10, 0xec, - 0x07, 0x12, 0x15, 0x0a, 0x10, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x41, 0x6e, 0x64, 0x42, - 0x69, 0x61, 0x73, 0x65, 0x73, 0x10, 0xed, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x5a, 0x6f, 0x68, 0x6f, - 0x43, 0x52, 0x4d, 0x10, 0xee, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4f, - 0x70, 0x65, 0x6e, 0x41, 0x49, 0x10, 0xef, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x6f, 0x44, 0x61, - 0x64, 0x64, 0x79, 0x10, 0xf0, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6c, 0x65, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x10, 0xf1, 0x07, 0x12, 0x16, 0x0a, 0x11, 0x54, 0x77, 0x69, 0x74, 0x63, 0x68, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf2, 0x07, 0x12, 0x11, 0x0a, - 0x0c, 0x54, 0x77, 0x69, 0x6c, 0x69, 0x6f, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0xf3, 0x07, - 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x79, 0x10, 0xf4, 0x07, 0x12, 0x16, 0x0a, - 0x11, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x10, 0xf5, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x41, 0x69, 0x72, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x4f, 0x41, 0x75, 0x74, 0x68, 0x10, 0xf6, 0x07, 0x12, 0x20, 0x0a, 0x1b, 0x41, 0x69, 0x72, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf7, 0x07, 0x12, 0x21, 0x0a, 0x1c, 0x53, - 0x74, 0x6f, 0x72, 0x79, 0x62, 0x6c, 0x6f, 0x6b, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf8, 0x07, 0x12, 0x13, - 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x4f, 0x72, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x10, 0xf9, 0x07, 0x12, 0x24, 0x0a, 0x1f, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x10, 0xfa, 0x07, 0x12, 0x26, 0x0a, 0x21, 0x41, 0x7a, 0x75, - 0x72, 0x65, 0x41, 0x50, 0x49, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xfb, - 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x48, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x10, 0xfc, 0x07, 0x12, - 0x0d, 0x0a, 0x08, 0x4c, 0x61, 0x6e, 0x67, 0x66, 0x75, 0x73, 0x65, 0x10, 0xfd, 0x07, 0x12, 0x18, - 0x0a, 0x13, 0x42, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xfe, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x58, 0x41, 0x49, 0x10, - 0xff, 0x07, 0x12, 0x1d, 0x0a, 0x18, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x10, 0x80, - 0x08, 0x12, 0x23, 0x0a, 0x1e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x70, 0x70, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x10, 0x81, 0x08, 0x12, 0x0d, 0x0a, 0x08, 0x44, 0x65, 0x65, 0x70, 0x53, 0x65, - 0x65, 0x6b, 0x10, 0x82, 0x08, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x10, 0x83, 0x08, 0x12, - 0x0e, 0x0a, 0x09, 0x4c, 0x61, 0x6e, 0x67, 0x53, 0x6d, 0x69, 0x74, 0x68, 0x10, 0x84, 0x08, 0x12, - 0x19, 0x0a, 0x14, 0x42, 0x69, 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x41, 0x70, 0x70, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x85, 0x08, 0x12, 0x0b, 0x0a, 0x06, 0x48, 0x61, - 0x73, 0x75, 0x72, 0x61, 0x10, 0x86, 0x08, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, - 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x08, 0x4d, 0x65, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x10, 0xb6, 0x07, 0x12, 0x15, 0x0a, 0x0c, + 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x57, 0x61, 0x61, 0x53, 0x10, 0xb7, 0x07, 0x1a, + 0x02, 0x08, 0x01, 0x12, 0x11, 0x0a, 0x0c, 0x4c, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x71, 0x75, 0x65, + 0x65, 0x7a, 0x79, 0x10, 0xb8, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x75, 0x64, 0x69, 0x62, 0x61, + 0x73, 0x65, 0x10, 0xb9, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x44, 0x65, 0x6e, 0x6f, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x10, 0xba, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x69, 0x70, 0x6f, + 0x10, 0xbb, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x49, 0x4f, 0x10, 0xbc, + 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x10, + 0xbd, 0x07, 0x12, 0x1b, 0x0a, 0x16, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x10, 0xbe, 0x07, 0x12, + 0x12, 0x0a, 0x0d, 0x41, 0x57, 0x53, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, + 0x10, 0xbf, 0x07, 0x12, 0x09, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x61, 0x10, 0xc0, 0x07, 0x12, 0x0b, + 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x7a, 0x49, 0x4f, 0x10, 0xc1, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x62, 0x72, 0x69, 0x74, 0x65, 0x10, 0xc2, 0x07, 0x12, 0x1a, 0x0a, 0x15, + 0x47, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0xc3, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x10, 0xc4, 0x07, 0x12, 0x0d, 0x0a, + 0x08, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x6f, 0x70, 0x10, 0xc5, 0x07, 0x12, 0x0a, 0x0a, 0x05, + 0x4e, 0x67, 0x72, 0x6f, 0x6b, 0x10, 0xc6, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x10, 0xc7, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x6f, 0x73, 0x74, + 0x67, 0x72, 0x65, 0x73, 0x10, 0xc8, 0x07, 0x12, 0x2a, 0x0a, 0x25, 0x41, 0x7a, 0x75, 0x72, 0x65, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x10, 0xc9, 0x07, 0x12, 0x20, 0x0a, 0x1b, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x61, 0x63, 0x68, + 0x65, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x64, 0x69, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, + 0x65, 0x79, 0x10, 0xca, 0x07, 0x12, 0x21, 0x0a, 0x1c, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x43, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x44, 0x42, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x10, 0xcb, 0x07, 0x12, 0x23, 0x0a, 0x1e, 0x41, 0x7a, 0x75, 0x72, + 0x65, 0x44, 0x65, 0x76, 0x6f, 0x70, 0x73, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xcc, 0x07, 0x12, 0x15, 0x0a, + 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, + 0x79, 0x10, 0xcd, 0x07, 0x12, 0x2c, 0x0a, 0x27, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4d, 0x4c, 0x57, + 0x65, 0x62, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x10, + 0xce, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x61, 0x73, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x10, 0xcf, 0x07, 0x12, 0x18, 0x0a, 0x13, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xd0, 0x07, + 0x12, 0x18, 0x0a, 0x13, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x10, 0xd1, 0x07, 0x12, 0x1f, 0x0a, 0x1a, 0x41, 0x7a, + 0x75, 0x72, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x10, 0xd2, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x41, + 0x7a, 0x75, 0x72, 0x65, 0x53, 0x51, 0x4c, 0x10, 0xd3, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x46, 0x6c, + 0x79, 0x49, 0x4f, 0x10, 0xd4, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x57, + 0x69, 0x74, 0x68, 0x10, 0xd5, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x4a, 0x75, 0x70, 0x69, 0x74, 0x65, + 0x72, 0x4f, 0x6e, 0x65, 0x10, 0xd6, 0x07, 0x12, 0x25, 0x0a, 0x20, 0x47, 0x43, 0x50, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x10, 0xd7, 0x07, 0x12, 0x08, + 0x0a, 0x03, 0x57, 0x69, 0x7a, 0x10, 0xd8, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x67, 0x61, + 0x72, 0x6d, 0x65, 0x10, 0xd9, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4f, 0x6e, 0x66, 0x6c, 0x65, 0x65, + 0x74, 0x10, 0xda, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x34, 0x32, 0x10, + 0xdb, 0x07, 0x12, 0x09, 0x0a, 0x04, 0x47, 0x72, 0x6f, 0x71, 0x10, 0xdc, 0x07, 0x12, 0x17, 0x0a, + 0x12, 0x54, 0x77, 0x69, 0x74, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, + 0x6b, 0x65, 0x79, 0x10, 0xdd, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x45, 0x72, 0x61, 0x73, 0x65, 0x72, + 0x10, 0xde, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x4c, 0x61, 0x72, 0x6b, 0x53, 0x75, 0x69, 0x74, 0x65, + 0x10, 0xdf, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x4c, 0x61, 0x72, 0x6b, 0x53, 0x75, 0x69, 0x74, 0x65, + 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0xe0, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x45, 0x6e, 0x64, + 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x73, 0x10, 0xe1, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x45, 0x6c, 0x65, + 0x76, 0x65, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x10, 0xe2, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x4e, 0x65, + 0x74, 0x73, 0x75, 0x69, 0x74, 0x65, 0x10, 0xe3, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x52, 0x6f, 0x62, + 0x69, 0x6e, 0x68, 0x6f, 0x6f, 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x10, 0xe4, 0x07, 0x12, + 0x0a, 0x0a, 0x05, 0x4e, 0x56, 0x41, 0x50, 0x49, 0x10, 0xe5, 0x07, 0x12, 0x09, 0x0a, 0x04, 0x50, + 0x79, 0x50, 0x49, 0x10, 0xe6, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x52, 0x61, 0x69, 0x6c, 0x77, 0x61, + 0x79, 0x41, 0x70, 0x70, 0x10, 0xe7, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x4d, 0x65, 0x72, 0x61, 0x6b, + 0x69, 0x10, 0xe8, 0x07, 0x12, 0x15, 0x0a, 0x10, 0x53, 0x61, 0x6c, 0x61, 0x64, 0x43, 0x6c, 0x6f, + 0x75, 0x64, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0xe9, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x42, + 0x6f, 0x78, 0x10, 0xea, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x6f, 0x78, 0x4f, 0x61, 0x75, 0x74, + 0x68, 0x10, 0xeb, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x10, 0xec, 0x07, 0x12, 0x15, 0x0a, 0x10, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, + 0x41, 0x6e, 0x64, 0x42, 0x69, 0x61, 0x73, 0x65, 0x73, 0x10, 0xed, 0x07, 0x12, 0x0c, 0x0a, 0x07, + 0x5a, 0x6f, 0x68, 0x6f, 0x43, 0x52, 0x4d, 0x10, 0xee, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x7a, + 0x75, 0x72, 0x65, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x49, 0x10, 0xef, 0x07, 0x12, 0x0c, 0x0a, 0x07, + 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x10, 0xf0, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6c, + 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x10, 0xf1, 0x07, 0x12, 0x16, 0x0a, 0x11, 0x54, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf2, + 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x77, 0x69, 0x6c, 0x69, 0x6f, 0x41, 0x70, 0x69, 0x4b, 0x65, + 0x79, 0x10, 0xf3, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x79, 0x10, 0xf4, + 0x07, 0x12, 0x16, 0x0a, 0x11, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf5, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x41, 0x69, 0x72, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x41, 0x75, 0x74, 0x68, 0x10, 0xf6, 0x07, 0x12, 0x20, 0x0a, + 0x1b, 0x41, 0x69, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, + 0x6c, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf7, 0x07, 0x12, + 0x21, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x6c, 0x6f, 0x6b, 0x50, 0x65, 0x72, 0x73, + 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, + 0xf8, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x4f, 0x72, 0x67, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xf9, 0x07, 0x12, 0x24, 0x0a, 0x1f, 0x41, 0x7a, 0x75, 0x72, 0x65, + 0x41, 0x70, 0x69, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x10, 0xfa, 0x07, 0x12, 0x26, 0x0a, + 0x21, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x50, 0x49, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x10, 0xfb, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x48, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, + 0x10, 0xfc, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x4c, 0x61, 0x6e, 0x67, 0x66, 0x75, 0x73, 0x65, 0x10, + 0xfd, 0x07, 0x12, 0x18, 0x0a, 0x13, 0x42, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0xfe, 0x07, 0x12, 0x08, 0x0a, 0x03, + 0x58, 0x41, 0x49, 0x10, 0xff, 0x07, 0x12, 0x1d, 0x0a, 0x18, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, + 0x65, 0x79, 0x10, 0x80, 0x08, 0x12, 0x23, 0x0a, 0x1e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x70, + 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x10, 0x81, 0x08, 0x12, 0x0d, 0x0a, 0x08, 0x44, 0x65, + 0x65, 0x70, 0x53, 0x65, 0x65, 0x6b, 0x10, 0x82, 0x08, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x74, 0x72, + 0x69, 0x70, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x10, 0x83, 0x08, 0x12, 0x0e, 0x0a, 0x09, 0x4c, 0x61, 0x6e, 0x67, 0x53, 0x6d, 0x69, 0x74, 0x68, + 0x10, 0x84, 0x08, 0x12, 0x19, 0x0a, 0x14, 0x42, 0x69, 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x41, 0x70, 0x70, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x85, 0x08, 0x12, 0x0b, + 0x0a, 0x06, 0x48, 0x61, 0x73, 0x75, 0x72, 0x61, 0x10, 0x86, 0x08, 0x42, 0x3d, 0x5a, 0x3b, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/proto/detectors.proto b/proto/detectors.proto index fc1d80e45..4500c1731 100644 --- a/proto/detectors.proto +++ b/proto/detectors.proto @@ -960,7 +960,7 @@ enum DetectorType { ZeroTier = 948; AppOptics = 949; Metabase = 950; - CoinbaseWaaS = 951; + CoinbaseWaaS = 951 [deprecated = true]; LemonSqueezy = 952; Budibase = 953; DenoDeploy = 954;