From 499cb64546e68d3f27e7f77d61999b7cb3886e91 Mon Sep 17 00:00:00 2001 From: dylanTruffle <52866392+dylanTruffle@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:17:55 -0700 Subject: [PATCH 01/14] Detector-Competition-Fix: Fix redis to now support SSL, and look for azure redis connection strings (#1957) * adding azure redis, and fixing the old detector to support ssl too * fix? * other way --------- Co-authored-by: Chair Co-authored-by: Zachary Rice --- pkg/detectors/redis/redis.go | 51 +++++++++++++++++++++++++++++-- pkg/detectors/redis/redis_test.go | 3 -- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/pkg/detectors/redis/redis.go b/pkg/detectors/redis/redis.go index 138f5306967f..5aa4efdd8b0e 100644 --- a/pkg/detectors/redis/redis.go +++ b/pkg/detectors/redis/redis.go @@ -2,6 +2,7 @@ package redis import ( "context" + "fmt" "net/url" "regexp" "strings" @@ -18,7 +19,8 @@ type Scanner struct{} var _ detectors.Detector = (*Scanner)(nil) var ( - keyPat = regexp.MustCompile(`\bredis://[\S]{3,50}:([\S]{3,50})@[-.%\w\/:]+\b`) + keyPat = regexp.MustCompile(`\bredi[s]{1,2}://[\S]{3,50}:([\S]{3,50})@[-.%\w\/:]+\b`) + azureRedisPat = regexp.MustCompile(`\b([\w\d.-]{1,100}\.redis\.cache\.windows\.net:6380),password=([^,]{44}),ssl=True,abortConnect=False\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -32,6 +34,51 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result dataStr := string(data) matches := keyPat.FindAllStringSubmatch(dataStr, -1) + azureMatches := azureRedisPat.FindAllStringSubmatch(dataStr, -1) + + for _, match := range azureMatches { + host := match[1] + password := match[2] + urlMatch := fmt.Sprintf("rediss://:%s@%s", password, host) + + // Skip findings where the password only has "*" characters, this is a redacted password + if strings.Trim(password, "*") == "" { + continue + } + + parsedURL, err := url.Parse(urlMatch) + if err != nil { + continue + } + if _, ok := parsedURL.User.Password(); !ok { + continue + } + + redact := strings.TrimSpace(strings.Replace(urlMatch, password, "*******", -1)) + + s := detectors.Result{ + DetectorType: detectorspb.DetectorType_Redis, + Raw: []byte(urlMatch), + Redacted: redact, + } + + if verify { + s.Verified = verifyRedis(ctx, parsedURL) + } + + if !s.Verified { + // Skip unverified findings where the password starts with a `$` - it's almost certainly a variable. + if strings.HasPrefix(password, "$") { + continue + } + } + + if !s.Verified && detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, false) { + continue + } + + results = append(results, s) + } for _, match := range matches { urlMatch := match[0] @@ -50,7 +97,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result continue } - redact := strings.TrimSpace(strings.Replace(urlMatch, password, "********", -1)) + redact := strings.TrimSpace(strings.Replace(urlMatch, password, "*******", -1)) s := detectors.Result{ DetectorType: detectorspb.DetectorType_Redis, diff --git a/pkg/detectors/redis/redis_test.go b/pkg/detectors/redis/redis_test.go index d83db2caf155..92b58dece5a1 100644 --- a/pkg/detectors/redis/redis_test.go +++ b/pkg/detectors/redis/redis_test.go @@ -61,9 +61,6 @@ func TestURI_FromChunk(t *testing.T) { t.Errorf("URI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } - // if os.Getenv("FORCE_PASS_DIFF") == "true" { - // return - // } for i := range got { got[i].Raw = nil } From 8bac2b15bac50966ecb106fc88855695eaa019dc Mon Sep 17 00:00:00 2001 From: dylanTruffle <52866392+dylanTruffle@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:49:04 -0700 Subject: [PATCH 02/14] Detector-Competition-Feat: Adding Azure Batch keys (#1956) * adding azure batch * fmt * fix lint --------- Co-authored-by: Chair Co-authored-by: Zachary Rice --- pkg/detectors/azurebatch/azurebatch.go | 114 +++++++++++++++++ pkg/detectors/azurebatch/azurebatch_test.go | 130 ++++++++++++++++++++ pkg/engine/defaults.go | 2 + pkg/pb/detectorspb/detectors.pb.go | 17 ++- proto/detectors.proto | 1 + 5 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 pkg/detectors/azurebatch/azurebatch.go create mode 100644 pkg/detectors/azurebatch/azurebatch_test.go diff --git a/pkg/detectors/azurebatch/azurebatch.go b/pkg/detectors/azurebatch/azurebatch.go new file mode 100644 index 000000000000..576fcd43af40 --- /dev/null +++ b/pkg/detectors/azurebatch/azurebatch.go @@ -0,0 +1,114 @@ +package azurebatch + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "net/http" + "regexp" + "strings" + "time" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "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 ( + defaultClient = common.SaneHttpClient() + // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. + urlPat = regexp.MustCompile(`https://(.{1,50})\.(.{1,50})\.batch\.azure\.com`) + secretPat = regexp.MustCompile(`[A-Za-z0-9+/=]{88}`) +) + +// 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{".batch.azure.com"} +} + +// FromData will find and optionally verify Azurebatch 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) + + urlMatches := urlPat.FindAllStringSubmatch(dataStr, -1) + secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) + + for _, urlMatch := range urlMatches { + + for _, secretMatch := range secretMatches { + + endpoint := urlMatch[0] + accountName := urlMatch[1] + accountKey := secretMatch[0] + + s1 := detectors.Result{ + DetectorType: detectorspb.DetectorType_AzureBatch, + Raw: []byte(endpoint), + Redacted: endpoint, + RawV2: []byte(endpoint + accountKey), + } + + if verify { + client := s.client + if client == nil { + client = defaultClient + } + url := fmt.Sprintf("%s/applications?api-version=2020-09-01.12.0", endpoint) + date := time.Now().UTC().Format(http.TimeFormat) + stringToSign := fmt.Sprintf( + "GET\n\n\n\n\napplication/json\n%s\n\n\n\n\n\n%s\napi-version:%s", + date, + strings.ToLower(fmt.Sprintf("/%s/applications", accountName)), + "2020-09-01.12.0", + ) + key, _ := base64.StdEncoding.DecodeString(accountKey) + h := hmac.New(sha256.New, key) + h.Write([]byte(stringToSign)) + signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + continue + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("SharedKey %s:%s", accountName, signature)) + req.Header.Set("Date", date) + resp, err := client.Do(req) + if err != nil { + continue + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + s1.Verified = true + } + + } + + // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. + if !s1.Verified && detectors.IsKnownFalsePositive(accountKey, detectors.DefaultFalsePositives, true) { + continue + } + + results = append(results, s1) + if s1.Verified { + break + } + } + } + + return results, nil +} + +func (s Scanner) Type() detectorspb.DetectorType { + return detectorspb.DetectorType_AzureBatch +} diff --git a/pkg/detectors/azurebatch/azurebatch_test.go b/pkg/detectors/azurebatch/azurebatch_test.go new file mode 100644 index 000000000000..b7a356275d66 --- /dev/null +++ b/pkg/detectors/azurebatch/azurebatch_test.go @@ -0,0 +1,130 @@ +//go:build detectors +// +build detectors + +package azurebatch + +import ( + "context" + "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 TestAzurebatch_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) + } + url := testSecrets.MustGetField("AZUREBATCH_URL") + secret := testSecrets.MustGetField("AZUREBATCH_KEY") + inactiveSecret := testSecrets.MustGetField("AZUREBATCH_KEY_INACTIVE") + + 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 azurebatch secret %s and %s within", url, secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_AzureBatch, + Verified: true, + }, + }, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "found, unverified", + s: Scanner{}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a azurebatch secret %s and %s within but not valid", url, inactiveSecret)), // the secret would satisfy the regex but not pass validation + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_AzureBatch, + 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, + }, + } + 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("AzureBatch.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{}, "RawV2", "Raw", "Redacted", "VerificationError") + + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { + t.Errorf("AzureBatch.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/engine/defaults.go b/pkg/engine/defaults.go index aad9f1ecebca..7ce1efca05a4 100644 --- a/pkg/engine/defaults.go +++ b/pkg/engine/defaults.go @@ -62,6 +62,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/aylien" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/ayrshare" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azure" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azurebatch" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/bannerbear" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/baremetrics" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/beamer" @@ -780,6 +781,7 @@ func DefaultDetectors() []detectors.Detector { &alibaba.Scanner{}, aws.New(), &azure.Scanner{}, + &azurebatch.Scanner{}, &slack.Scanner{}, // has 4 secret types &gitlab.Scanner{}, &gitlabv2.Scanner{}, diff --git a/pkg/pb/detectorspb/detectors.pb.go b/pkg/pb/detectorspb/detectors.pb.go index df7d5b0243a1..73528bf93bee 100644 --- a/pkg/pb/detectorspb/detectors.pb.go +++ b/pkg/pb/detectorspb/detectors.pb.go @@ -1034,6 +1034,7 @@ const ( DetectorType_DenoDeploy DetectorType = 954 DetectorType_Stripo DetectorType = 955 DetectorType_ReplyIO DetectorType = 956 + DetectorType_AzureBatch DetectorType = 957 ) // Enum value maps for DetectorType. @@ -1992,6 +1993,7 @@ var ( 954: "DenoDeploy", 955: "Stripo", 956: "ReplyIO", + 957: "AzureBatch", } DetectorType_value = map[string]int32{ "Alibaba": 0, @@ -2947,6 +2949,7 @@ var ( "DenoDeploy": 954, "Stripo": 955, "ReplyIO": 956, + "AzureBatch": 957, } ) @@ -3325,7 +3328,7 @@ var file_detectors_proto_rawDesc = []byte{ 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 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, 0x2a, 0x8c, 0x78, 0x0a, 0x0c, 0x44, + 0x09, 0x0a, 0x05, 0x55, 0x54, 0x46, 0x31, 0x36, 0x10, 0x03, 0x2a, 0x9d, 0x78, 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, @@ -4286,11 +4289,13 @@ var file_detectors_proto_rawDesc = []byte{ 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, 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, + 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, 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 98d639cff2ea..0d4f75a58668 100644 --- a/proto/detectors.proto +++ b/proto/detectors.proto @@ -965,6 +965,7 @@ enum DetectorType { DenoDeploy = 954; Stripo = 955; ReplyIO = 956; + AzureBatch = 957; } message Result { From 74a56de8313c2e2b0f3694b9ce9e8a030fe0c59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81h=CC=95=CC=B3m=CD=AD=CD=AD=CD=A8=CD=A9=CC=90e=CD=AC?= =?UTF-8?q?=CC=81=CD=8B=CD=AC=CC=8A=CC=93=CD=82=CC=98d?= <13666360+0x1@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:13:48 -0400 Subject: [PATCH 03/14] update braintreepayments detector to tri-state verification (#1834) * update braintreepayments detector to tri-state verification * Update pkg/detectors/braintreepayments/braintreepayments.go Co-authored-by: Zachary Rice * small nits * small nits --------- Co-authored-by: Zachary Rice --- .../braintreepayments/braintreepayments.go | 100 ++++++++++++------ .../braintreepayments_test.go | 69 +++++++++--- 2 files changed, 124 insertions(+), 45 deletions(-) diff --git a/pkg/detectors/braintreepayments/braintreepayments.go b/pkg/detectors/braintreepayments/braintreepayments.go index b073a2aa8438..1600945305d0 100644 --- a/pkg/detectors/braintreepayments/braintreepayments.go +++ b/pkg/detectors/braintreepayments/braintreepayments.go @@ -2,6 +2,7 @@ package braintreepayments import ( "context" + "fmt" "io" "net/http" "regexp" @@ -12,14 +13,21 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -type Scanner struct{} +type Scanner struct { + client *http.Client + useTestURL bool +} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) -var ( - client = common.SaneHttpClient() +const ( + verifyURL = "https://payments.braintree-api.com/graphql" + verifyTestURL = "https://payments.sandbox.braintree-api.com/graphql" +) +var ( + 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{"braintree"}) + `\b([0-9a-f]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"braintree"}) + `\b([0-9a-z]{16})\b`) @@ -48,7 +56,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if len(idMatch) != 2 { continue } - resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ @@ -57,34 +64,16 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - payload := strings.NewReader(`{"query": "query { ping }"}`) - req, err := http.NewRequestWithContext(ctx, "POST", "https://payments.braintree-api.com/graphql", payload) - if err != nil { - continue - } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Braintree-Version", "2019-01-01") - req.SetBasicAuth(resIdMatch, resMatch) - res, err := client.Do(req) - if err == nil { - bodyBytes, err := io.ReadAll(res.Body) - if err != nil { - continue - } - - bodyString := string(bodyBytes) - validResponse := strings.Contains(bodyString, `"data":{`) - - defer res.Body.Close() - if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { - s1.Verified = true - } else { - // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key - if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { - continue - } - } - } + client := s.getClient() + url := s.getBraintreeURL() + isVerified, verificationErr := verifyBraintree(ctx, client, url, resIdMatch, resMatch) + s1.Verified = isVerified + s1.VerificationError = verificationErr + } + + // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key + if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { + continue } results = append(results, s1) @@ -94,6 +83,53 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result return results, nil } +func (s Scanner) getBraintreeURL() string { + if s.useTestURL { + return verifyTestURL + } + return verifyURL +} + +func (s Scanner) getClient() *http.Client { + if s.client != nil { + return s.client + } + return defaultClient +} + +func verifyBraintree(ctx context.Context, client *http.Client, url, pubKey, privKey string) (bool, error) { + payload := strings.NewReader(`{"query": "query { ping }"}`) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, payload) + if err != nil { + return false, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Braintree-Version", "2019-01-01") + req.SetBasicAuth(pubKey, privKey) + res, err := client.Do(req) + if err != nil { + return false, err + } + + bodyBytes, err := io.ReadAll(res.Body) + if err != nil { + return false, err + } + defer res.Body.Close() + + bodyString := string(bodyBytes) + if !(res.StatusCode == http.StatusOK) { + return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + } + + validResponse := `"data":{` + if strings.Contains(bodyString, validResponse) { + return true, nil + } + + return false, nil +} + func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BraintreePayments } diff --git a/pkg/detectors/braintreepayments/braintreepayments_test.go b/pkg/detectors/braintreepayments/braintreepayments_test.go index 8a3daea09c55..0713f124b614 100644 --- a/pkg/detectors/braintreepayments/braintreepayments_test.go +++ b/pkg/detectors/braintreepayments/braintreepayments_test.go @@ -6,10 +6,11 @@ import ( "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/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) @@ -30,15 +31,16 @@ func TestBraintreePayments_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{}, + s: Scanner{useTestURL: true}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a braintree secret %s within braintree %s", secret, id)), @@ -52,9 +54,47 @@ func TestBraintreePayments_FromChunk(t *testing.T) { }, wantErr: false, }, + { + name: "found, verified but unexpected api surface", + s: Scanner{ + client: common.ConstantResponseHttpClient(404, ""), + }, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a braintree secret %s within braintree %s", secret, id)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_BraintreePayments, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + { + 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 braintree secret %s within braintree %s", secret, id)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_BraintreePayments, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, { name: "found, unverified", - s: Scanner{}, + s: Scanner{useTestURL: true}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a braintree secret %s within braintree %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation @@ -82,8 +122,7 @@ func TestBraintreePayments_FromChunk(t *testing.T) { } 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("BraintreePayments.FromData() error = %v, wantErr %v", err, tt.wantErr) return @@ -92,9 +131,13 @@ func TestBraintreePayments_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - got[i].Raw = nil + if (got[i].VerificationError != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + } } - if diff := pretty.Compare(got, tt.want); diff != "" { + + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("BraintreePayments.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) From 89b6315e19a32235917900ef818c41c82991ed85 Mon Sep 17 00:00:00 2001 From: ahrav Date: Tue, 31 Oct 2023 16:40:19 -0700 Subject: [PATCH 04/14] [chore] - add binutils dep to dockerfile (#2061) * add binutils dep to dockerfile * add cpio * add dep --- Dockerfile | 2 +- Dockerfile.goreleaser | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89d60229cbce..1fdfce29e046 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \ GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o trufflehog . FROM alpine:3.18 -RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio \ +RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio binutils cpio \ && rm -rf /var/cache/apk/* && update-ca-certificates COPY --from=builder /build/trufflehog /usr/bin/trufflehog COPY entrypoint.sh /etc/entrypoint.sh diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser index db663c467885..bd1b1aad00b4 100644 --- a/Dockerfile.goreleaser +++ b/Dockerfile.goreleaser @@ -1,6 +1,6 @@ FROM alpine:3.18 -RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio \ +RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio binutils cpio \ && rm -rf /var/cache/apk/* && update-ca-certificates WORKDIR /usr/bin/ COPY trufflehog . From 95e0090bc26a7d43d4e2dd68a415fcedc4edef85 Mon Sep 17 00:00:00 2001 From: ahrav Date: Tue, 31 Oct 2023 16:42:42 -0700 Subject: [PATCH 05/14] [chore] - correctly handle input shorter than 512 bytes (#2077) * correctly handle input shorter than 512 bytes * add tests * reorder tests * add another test case * update test * address comment --- pkg/handlers/archive.go | 4 +- pkg/handlers/archive_test.go | 77 +++++++++++++++++++++++++++++++++++- pkg/sources/chunker_test.go | 2 +- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/pkg/handlers/archive.go b/pkg/handlers/archive.go index 6b4062556760..942e513288c8 100644 --- a/pkg/handlers/archive.go +++ b/pkg/handlers/archive.go @@ -414,9 +414,11 @@ func (a *Archive) handleNestedFileMIME(ctx logContext.Context, tempEnv tempEnv, // determineMimeType reads from the provided reader to detect the MIME type. // It returns the detected MIME type and a new reader that includes the read portion. func determineMimeType(reader io.Reader) (mimeType, io.Reader, error) { + // A buffer of 512 bytes is used since many file formats store their magic numbers within the first 512 bytes. + // If fewer bytes are read, MIME type detection may still succeed. buffer := make([]byte, 512) n, err := reader.Read(buffer) - if err != nil { + if err != nil && !errors.Is(err, io.EOF) { return "", nil, fmt.Errorf("unable to read file for MIME type detection: %w", err) } diff --git a/pkg/handlers/archive_test.go b/pkg/handlers/archive_test.go index 009446c62b89..3a2d9e5e4c30 100644 --- a/pkg/handlers/archive_test.go +++ b/pkg/handlers/archive_test.go @@ -11,8 +11,9 @@ import ( "testing" "time" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" + "github.com/h2non/filetype" "github.com/stretchr/testify/assert" + diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" @@ -271,3 +272,77 @@ func TestNestedDirArchive(t *testing.T) { } assert.Equal(t, want, count) } + +func TestDetermineMimeType(t *testing.T) { + filetype.AddMatcher(filetype.NewType("txt", "text/plain"), func(buf []byte) bool { + return strings.HasPrefix(string(buf), "text:") + }) + + pngBytes := []byte("\x89PNG\r\n\x1a\n") + jpegBytes := []byte{0xFF, 0xD8, 0xFF} + textBytes := []byte("text: This is a plain text") + rpmBytes := []byte("\xed\xab\xee\xdb") + + tests := []struct { + name string + input io.Reader + expected mimeType + shouldFail bool + }{ + { + name: "PNG file", + input: bytes.NewReader(pngBytes), + expected: mimeType("image/png"), + shouldFail: false, + }, + { + name: "JPEG file", + input: bytes.NewReader(jpegBytes), + expected: mimeType("image/jpeg"), + shouldFail: false, + }, + { + name: "Text file", + input: bytes.NewReader(textBytes), + expected: mimeType("text/plain"), + shouldFail: false, + }, + { + name: "RPM file", + input: bytes.NewReader(rpmBytes), + expected: rpmMimeType, + shouldFail: false, + }, + { + name: "Truncated JPEG file", + input: io.LimitReader(bytes.NewReader(jpegBytes), 2), + expected: mimeType("unknown"), + shouldFail: true, + }, + { + name: "Empty reader", + input: bytes.NewReader([]byte{}), + shouldFail: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + originalData, _ := io.ReadAll(io.TeeReader(tt.input, &bytes.Buffer{})) + tt.input = bytes.NewReader(originalData) // Reset the reader + + mime, reader, err := determineMimeType(tt.input) + if err != nil && !tt.shouldFail { + t.Fatalf("unexpected error: %v", err) + } + + if !tt.shouldFail { + assert.Equal(t, tt.expected, mime) + } + + // Ensure the reader still contains all the original data. + data, _ := io.ReadAll(reader) + assert.Equal(t, originalData, data) + }) + } +} diff --git a/pkg/sources/chunker_test.go b/pkg/sources/chunker_test.go index 0a6711707156..aa350f7f0f0b 100644 --- a/pkg/sources/chunker_test.go +++ b/pkg/sources/chunker_test.go @@ -7,8 +7,8 @@ import ( "testing" "testing/iotest" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/stretchr/testify/assert" + diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) From 7197e4b3f15b73126c746ee5c552ebe715325e08 Mon Sep 17 00:00:00 2001 From: Cody Rose Date: Wed, 1 Nov 2023 10:14:28 -0400 Subject: [PATCH 06/14] use rawv2 for pubnubpublish (#2062) We're seeing secrets of this type flap between verified and unverified, which is expected behavior for multipart secrets without RawV2 defined. This PR adds RawV2 for secrets of this type. --- pkg/detectors/pubnubpublishkey/pubnubpublishkey.go | 1 + pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go b/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go index d0376a65ea09..f83296d940a1 100644 --- a/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go +++ b/pkg/detectors/pubnubpublishkey/pubnubpublishkey.go @@ -55,6 +55,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PubNubPublishKey, Raw: []byte(resMatch), + RawV2: []byte(resMatch + "/" + ressubMatch), } if verify { diff --git a/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go b/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go index a91b38a5fd53..7978ae490ce6 100644 --- a/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go +++ b/pkg/detectors/pubnubpublishkey/pubnubpublishkey_test.go @@ -6,11 +6,12 @@ package pubnubpublishkey import ( "context" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "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" @@ -134,7 +135,7 @@ func TestPubNubPublishKey_FromChunk(t *testing.T) { t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } - opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") + opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("PubNubPublishKey.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } From d55cb56db4a8a57088106bd40d8d67eda70b4527 Mon Sep 17 00:00:00 2001 From: ahrav Date: Wed, 1 Nov 2023 07:36:22 -0700 Subject: [PATCH 07/14] update comment (#2084) update Cache.Contents() comment --- pkg/cache/memory/memory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cache/memory/memory.go b/pkg/cache/memory/memory.go index 549dc2c06a28..b4e9c87c3cfd 100644 --- a/pkg/cache/memory/memory.go +++ b/pkg/cache/memory/memory.go @@ -94,7 +94,7 @@ func (c *Cache) Values() []string { return res } -// Contents returns all key-value pairs in the cache encodes as a string. +// Contents returns a comma-separated string containing all keys in the cache. func (c *Cache) Contents() string { items := c.c.Items() res := make([]string, 0, len(items)) From 52600a897aefc1cfb2ed354360a42c8b67b75ee9 Mon Sep 17 00:00:00 2001 From: Miccah Date: Wed, 1 Nov 2023 09:22:44 -0700 Subject: [PATCH 08/14] [chore] Replace chunks channel with ChunkReporter in git based sources (#2082) ChunkReporter is more flexible and will allow code reuse for unit chunking. ChanReporter was added as a way to maintain the original channel functionality, so this PR should not alter existing behavior. --- hack/snifftest/main.go | 2 +- pkg/handlers/archive_test.go | 14 +++--- pkg/handlers/handlers.go | 18 ++++--- pkg/sources/chan_reporter.go | 22 +++++++++ pkg/sources/filesystem/filesystem.go | 4 +- pkg/sources/gcs/gcs.go | 4 +- pkg/sources/git/git.go | 71 ++++++++++++++++++---------- pkg/sources/git/git_test.go | 2 +- pkg/sources/github/github.go | 2 +- pkg/sources/gitlab/gitlab.go | 2 +- pkg/sources/s3/s3.go | 4 +- 11 files changed, 92 insertions(+), 53 deletions(-) create mode 100644 pkg/sources/chan_reporter.go diff --git a/hack/snifftest/main.go b/hack/snifftest/main.go index 1a1062d373fb..217ecd079f27 100644 --- a/hack/snifftest/main.go +++ b/hack/snifftest/main.go @@ -204,7 +204,7 @@ func main() { }) logger.Info("scanning repo", "repo", r) - err = s.ScanRepo(ctx, repo, path, git.NewScanOptions(), chunksChan) + err = s.ScanRepo(ctx, repo, path, git.NewScanOptions(), sources.ChanReporter{Ch: chunksChan}) if err != nil { logFatal(err, "error scanning repo") } diff --git a/pkg/handlers/archive_test.go b/pkg/handlers/archive_test.go index 3a2d9e5e4c30..8f2186f47ef0 100644 --- a/pkg/handlers/archive_test.go +++ b/pkg/handlers/archive_test.go @@ -107,12 +107,12 @@ func TestArchiveHandler(t *testing.T) { } func TestHandleFile(t *testing.T) { - ch := make(chan *sources.Chunk, 2) + reporter := sources.ChanReporter{Ch: make(chan *sources.Chunk, 2)} // Context cancels the operation. canceledCtx, cancel := context.WithCancel(context.Background()) cancel() - assert.False(t, HandleFile(canceledCtx, strings.NewReader("file"), &sources.Chunk{}, ch)) + assert.False(t, HandleFile(canceledCtx, strings.NewReader("file"), &sources.Chunk{}, reporter)) // Only one chunk is sent on the channel. // TODO: Embed a zip without making an HTTP request. @@ -124,9 +124,9 @@ func TestHandleFile(t *testing.T) { reader, err := diskbufferreader.New(resp.Body) assert.NoError(t, err) - assert.Equal(t, 0, len(ch)) - assert.True(t, HandleFile(context.Background(), reader, &sources.Chunk{}, ch)) - assert.Equal(t, 1, len(ch)) + assert.Equal(t, 0, len(reporter.Ch)) + assert.True(t, HandleFile(context.Background(), reader, &sources.Chunk{}, reporter)) + assert.Equal(t, 1, len(reporter.Ch)) } func TestReadToMax(t *testing.T) { @@ -209,7 +209,7 @@ func TestExtractTarContent(t *testing.T) { chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - ok := HandleFile(ctx, file, &sources.Chunk{}, chunkCh) + ok := HandleFile(ctx, file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) assert.True(t, ok) }() @@ -262,7 +262,7 @@ func TestNestedDirArchive(t *testing.T) { go func() { defer close(sourceChan) - HandleFile(ctx, file, &sources.Chunk{}, sourceChan) + HandleFile(ctx, file, &sources.Chunk{}, sources.ChanReporter{Ch: sourceChan}) }() count := 0 diff --git a/pkg/handlers/handlers.go b/pkg/handlers/handlers.go index 131e77c963a4..d2792c0e69f6 100644 --- a/pkg/handlers/handlers.go +++ b/pkg/handlers/handlers.go @@ -34,10 +34,10 @@ type Handler interface { // HandleFile processes a given file by selecting an appropriate handler from DefaultHandlers. // It first checks if the handler implements SpecializedHandler for any special processing, // then falls back to regular file type handling. If successful, it reads the file in chunks, -// packages them in the provided chunk skeleton, and sends them to chunksChan. +// packages them in the provided chunk skeleton, and reports them to the chunk reporter. // The function returns true if processing was successful and false otherwise. // Context is used for cancellation, and the caller is responsible for canceling it if needed. -func HandleFile(ctx context.Context, file io.Reader, chunkSkel *sources.Chunk, chunksChan chan *sources.Chunk) bool { +func HandleFile(ctx context.Context, file io.Reader, chunkSkel *sources.Chunk, reporter sources.ChunkReporter) bool { aCtx := logContext.AddLogger(ctx) for _, h := range DefaultHandlers() { h.New() @@ -51,7 +51,7 @@ func HandleFile(ctx context.Context, file io.Reader, chunkSkel *sources.Chunk, c return false } - if success := processHandler(aCtx, h, reReader, chunkSkel, chunksChan); success { + if success := processHandler(aCtx, h, reReader, chunkSkel, reporter); success { return true } } @@ -59,14 +59,14 @@ func HandleFile(ctx context.Context, file io.Reader, chunkSkel *sources.Chunk, c return false } -func processHandler(ctx logContext.Context, h Handler, reReader *diskbufferreader.DiskBufferReader, chunkSkel *sources.Chunk, chunksChan chan *sources.Chunk) bool { +func processHandler(ctx logContext.Context, h Handler, reReader *diskbufferreader.DiskBufferReader, chunkSkel *sources.Chunk, reporter sources.ChunkReporter) bool { defer reReader.Close() defer reReader.Stop() if specialHandler, ok := h.(SpecializedHandler); ok { file, isSpecial, err := specialHandler.HandleSpecialized(ctx, reReader) if isSpecial { - return handleChunks(ctx, h.FromFile(ctx, file), chunkSkel, chunksChan) + return handleChunks(ctx, h.FromFile(ctx, file), chunkSkel, reporter) } if err != nil { ctx.Logger().Error(err, "error handling file") @@ -82,10 +82,10 @@ func processHandler(ctx logContext.Context, h Handler, reReader *diskbufferreade return false } - return handleChunks(ctx, h.FromFile(ctx, reReader), chunkSkel, chunksChan) + return handleChunks(ctx, h.FromFile(ctx, reReader), chunkSkel, reporter) } -func handleChunks(ctx context.Context, handlerChan chan []byte, chunkSkel *sources.Chunk, chunksChan chan *sources.Chunk) bool { +func handleChunks(ctx context.Context, handlerChan chan []byte, chunkSkel *sources.Chunk, reporter sources.ChunkReporter) bool { for { select { case data, open := <-handlerChan: @@ -94,9 +94,7 @@ func handleChunks(ctx context.Context, handlerChan chan []byte, chunkSkel *sourc } chunk := *chunkSkel chunk.Data = data - select { - case chunksChan <- &chunk: - case <-ctx.Done(): + if err := reporter.ChunkOk(logContext.AddLogger(ctx), chunk); err != nil { return false } case <-ctx.Done(): diff --git a/pkg/sources/chan_reporter.go b/pkg/sources/chan_reporter.go new file mode 100644 index 000000000000..11c1619806e4 --- /dev/null +++ b/pkg/sources/chan_reporter.go @@ -0,0 +1,22 @@ +package sources + +import ( + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "github.com/trufflesecurity/trufflehog/v3/pkg/context" +) + +var _ ChunkReporter = (*ChanReporter)(nil) + +// ChanReporter is a ChunkReporter that writes to a channel. +type ChanReporter struct { + Ch chan<- *Chunk +} + +func (c ChanReporter) ChunkOk(ctx context.Context, chunk Chunk) error { + return common.CancellableWrite(ctx, c.Ch, &chunk) +} + +func (ChanReporter) ChunkErr(ctx context.Context, err error) error { + ctx.Logger().Error(err, "error chunking") + return nil +} diff --git a/pkg/sources/filesystem/filesystem.go b/pkg/sources/filesystem/filesystem.go index 791f5eb93f60..e969a12b1f4c 100644 --- a/pkg/sources/filesystem/filesystem.go +++ b/pkg/sources/filesystem/filesystem.go @@ -7,9 +7,9 @@ import ( "os" "path/filepath" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/go-errors/errors" "github.com/go-logr/logr" + diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -172,7 +172,7 @@ func (s *Source) scanFile(ctx context.Context, path string, chunksChan chan *sou }, Verify: s.verify, } - if handlers.HandleFile(ctx, reReader, chunkSkel, chunksChan) { + if handlers.HandleFile(ctx, reReader, chunkSkel, sources.ChanReporter{Ch: chunksChan}) { return nil } diff --git a/pkg/sources/gcs/gcs.go b/pkg/sources/gcs/gcs.go index 202752085ddd..72ac33708fee 100644 --- a/pkg/sources/gcs/gcs.go +++ b/pkg/sources/gcs/gcs.go @@ -10,9 +10,9 @@ import ( "sync" "cloud.google.com/go/storage" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/go-errors/errors" "github.com/go-logr/logr" + diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "golang.org/x/oauth2" "golang.org/x/oauth2/endpoints" "google.golang.org/protobuf/proto" @@ -375,7 +375,7 @@ func (s *Source) readObjectData(ctx context.Context, o object, chunk *sources.Ch } defer reader.Close() - if handlers.HandleFile(ctx, reader, chunk, s.chunksCh) { + if handlers.HandleFile(ctx, reader, chunk, sources.ChanReporter{Ch: s.chunksCh}) { ctx.Logger().V(3).Info("File was handled", "name", s.name, "bucket", o.bucket, "object", o.name) return nil, nil } diff --git a/pkg/sources/git/git.go b/pkg/sources/git/git.go index 807c7b459744..c01fee4e58c4 100644 --- a/pkg/sources/git/git.go +++ b/pkg/sources/git/git.go @@ -25,7 +25,6 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/trufflesecurity/trufflehog/v3/pkg/cleantemp" - "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/gitparse" "github.com/trufflesecurity/trufflehog/v3/pkg/handlers" @@ -157,10 +156,11 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so // Chunks emits chunks of bytes over a channel. func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ ...sources.ChunkingTarget) error { - if err := s.scanRepos(ctx, chunksChan); err != nil { + reporter := sources.ChanReporter{Ch: chunksChan} + if err := s.scanRepos(ctx, reporter); err != nil { return err } - if err := s.scanDirs(ctx, chunksChan); err != nil { + if err := s.scanDirs(ctx, reporter); err != nil { return err } @@ -174,7 +174,7 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ . } // scanRepos scans the configured repositories in s.conn.Repositories. -func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) error { +func (s *Source) scanRepos(ctx context.Context, reporter sources.ChunkReporter) error { if len(s.conn.Repositories) == 0 { return nil } @@ -196,7 +196,7 @@ func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) if err != nil { return err } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, chunksChan) + return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) }(repoURI) if err != nil { ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) @@ -215,7 +215,7 @@ func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) if err != nil { return err } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, chunksChan) + return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) }(repoURI) if err != nil { ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) @@ -234,7 +234,7 @@ func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) if err != nil { return err } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, chunksChan) + return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) }(repoURI) if err != nil { ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) @@ -248,7 +248,7 @@ func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) } // scanDirs scans the configured directories in s.conn.Directories. -func (s *Source) scanDirs(ctx context.Context, chunksChan chan *sources.Chunk) error { +func (s *Source) scanDirs(ctx context.Context, reporter sources.ChunkReporter) error { totalRepos := len(s.conn.Repositories) + len(s.conn.Directories) for i, gitDir := range s.conn.Directories { s.SetProgressComplete(len(s.conn.Repositories)+i, totalRepos, fmt.Sprintf("Repo: %s", gitDir), "") @@ -272,7 +272,7 @@ func (s *Source) scanDirs(ctx context.Context, chunksChan chan *sources.Chunk) e defer os.RemoveAll(repoPath) } - return s.git.ScanRepo(ctx, repo, repoPath, s.scanOptions, chunksChan) + return s.git.ScanRepo(ctx, repo, repoPath, s.scanOptions, reporter) }(gitDir) if err != nil { ctx.Logger().Info("error scanning repository", "repo", gitDir, "error", err) @@ -445,7 +445,7 @@ func (s *Git) CommitsScanned() uint64 { return atomic.LoadUint64(&s.metrics.commitsScanned) } -func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string, scanOptions *ScanOptions, chunksChan chan *sources.Chunk) error { +func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string, scanOptions *ScanOptions, reporter sources.ChunkReporter) error { if err := GitCmdCheck(); err != nil { return err } @@ -505,18 +505,18 @@ func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string SourceMetadata: metadata, Verify: s.verify, } - if err := handleBinary(ctx, repo, chunksChan, chunkSkel, commitHash, fileName); err != nil { + if err := handleBinary(ctx, repo, reporter, chunkSkel, commitHash, fileName); err != nil { logger.V(1).Info("error handling binary file", "error", err, "filename", fileName, "commit", commitHash, "file", diff.PathB) } continue } if diff.Content.Len() > sources.ChunkSize+sources.PeekSize { - s.gitChunk(ctx, diff, fileName, email, hash, when, urlMetadata, chunksChan) + s.gitChunk(ctx, diff, fileName, email, hash, when, urlMetadata, reporter) continue } metadata := s.sourceMetadataFunc(fileName, email, hash, when, urlMetadata, int64(diff.LineStart)) - chunksChan <- &sources.Chunk{ + chunk := sources.Chunk{ SourceName: s.sourceName, SourceID: s.sourceID, JobID: s.jobID, @@ -525,12 +525,15 @@ func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string Data: diff.Content.Bytes(), Verify: s.verify, } + if err := reporter.ChunkOk(ctx, chunk); err != nil { + return err + } } } return nil } -func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, hash, when, urlMetadata string, chunksChan chan *sources.Chunk) { +func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, hash, when, urlMetadata string, reporter sources.ChunkReporter) { originalChunk := bufio.NewScanner(&diff.Content) newChunkBuffer := bytes.Buffer{} lastOffset := 0 @@ -543,7 +546,7 @@ func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, if newChunkBuffer.Len() > 0 { // Send the existing fragment. metadata := s.sourceMetadataFunc(fileName, email, hash, when, urlMetadata, int64(diff.LineStart+lastOffset)) - chunksChan <- &sources.Chunk{ + chunk := sources.Chunk{ SourceName: s.sourceName, SourceID: s.sourceID, JobID: s.jobID, @@ -552,13 +555,18 @@ func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, Data: append([]byte{}, newChunkBuffer.Bytes()...), Verify: s.verify, } + if err := reporter.ChunkOk(ctx, chunk); err != nil { + // TODO: Return error. + return + } + newChunkBuffer.Reset() lastOffset = offset } if len(line) > sources.ChunkSize { // Send the oversize line. metadata := s.sourceMetadataFunc(fileName, email, hash, when, urlMetadata, int64(diff.LineStart+offset)) - chunksChan <- &sources.Chunk{ + chunk := sources.Chunk{ SourceName: s.sourceName, SourceID: s.sourceID, JobID: s.jobID, @@ -567,6 +575,10 @@ func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, Data: line, Verify: s.verify, } + if err := reporter.ChunkOk(ctx, chunk); err != nil { + // TODO: Return error. + return + } continue } } @@ -578,7 +590,7 @@ func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, // Send anything still in the new chunk buffer if newChunkBuffer.Len() > 0 { metadata := s.sourceMetadataFunc(fileName, email, hash, when, urlMetadata, int64(diff.LineStart+lastOffset)) - chunksChan <- &sources.Chunk{ + chunk := sources.Chunk{ SourceName: s.sourceName, SourceID: s.sourceID, JobID: s.jobID, @@ -587,11 +599,15 @@ func (s *Git) gitChunk(ctx context.Context, diff gitparse.Diff, fileName, email, Data: append([]byte{}, newChunkBuffer.Bytes()...), Verify: s.verify, } + if err := reporter.ChunkOk(ctx, chunk); err != nil { + // TODO: Return error. + return + } } } // ScanStaged chunks staged changes. -func (s *Git) ScanStaged(ctx context.Context, repo *git.Repository, path string, scanOptions *ScanOptions, chunksChan chan *sources.Chunk) error { +func (s *Git) ScanStaged(ctx context.Context, repo *git.Repository, path string, scanOptions *ScanOptions, reporter sources.ChunkReporter) error { // Get the URL metadata for reporting (may be empty). urlMetadata := getSafeRemoteURL(repo, "origin") @@ -652,14 +668,14 @@ func (s *Git) ScanStaged(ctx context.Context, repo *git.Repository, path string, SourceMetadata: metadata, Verify: s.verify, } - if err := handleBinary(ctx, repo, chunksChan, chunkSkel, commitHash, fileName); err != nil { + if err := handleBinary(ctx, repo, reporter, chunkSkel, commitHash, fileName); err != nil { logger.V(1).Info("error handling binary file", "error", err, "filename", fileName) } continue } metadata := s.sourceMetadataFunc(fileName, email, "Staged", when, urlMetadata, int64(diff.LineStart)) - chunksChan <- &sources.Chunk{ + chunk := sources.Chunk{ SourceName: s.sourceName, SourceID: s.sourceID, JobID: s.jobID, @@ -668,12 +684,15 @@ func (s *Git) ScanStaged(ctx context.Context, repo *git.Repository, path string, Data: diff.Content.Bytes(), Verify: s.verify, } + if err := reporter.ChunkOk(ctx, chunk); err != nil { + return err + } } } return nil } -func (s *Git) ScanRepo(ctx context.Context, repo *git.Repository, repoPath string, scanOptions *ScanOptions, chunksChan chan *sources.Chunk) error { +func (s *Git) ScanRepo(ctx context.Context, repo *git.Repository, repoPath string, scanOptions *ScanOptions, reporter sources.ChunkReporter) error { if scanOptions == nil { scanOptions = NewScanOptions() } @@ -682,11 +701,11 @@ func (s *Git) ScanRepo(ctx context.Context, repo *git.Repository, repoPath strin } start := time.Now().Unix() - if err := s.ScanCommits(ctx, repo, repoPath, scanOptions, chunksChan); err != nil { + if err := s.ScanCommits(ctx, repo, repoPath, scanOptions, reporter); err != nil { return err } if !scanOptions.Bare { - if err := s.ScanStaged(ctx, repo, repoPath, scanOptions, chunksChan); err != nil { + if err := s.ScanStaged(ctx, repo, repoPath, scanOptions, reporter); err != nil { ctx.Logger().V(1).Info("error scanning unstaged changes", "error", err) } } @@ -934,7 +953,7 @@ func getSafeRemoteURL(repo *git.Repository, preferred string) string { return safeURL } -func handleBinary(ctx context.Context, repo *git.Repository, chunksChan chan *sources.Chunk, chunkSkel *sources.Chunk, commitHash plumbing.Hash, path string) error { +func handleBinary(ctx context.Context, repo *git.Repository, reporter sources.ChunkReporter, chunkSkel *sources.Chunk, commitHash plumbing.Hash, path string) error { ctx.Logger().V(5).Info("handling binary file", "path", path) commit, err := repo.CommitObject(commitHash) if err != nil { @@ -958,7 +977,7 @@ func handleBinary(ctx context.Context, repo *git.Repository, chunksChan chan *so } defer reader.Close() - if handlers.HandleFile(ctx, reader, chunkSkel, chunksChan) { + if handlers.HandleFile(ctx, reader, chunkSkel, reporter) { return nil } @@ -976,7 +995,7 @@ func handleBinary(ctx context.Context, repo *git.Repository, chunksChan chan *so if err := data.Error(); err != nil { return err } - if err := common.CancellableWrite(ctx, chunksChan, &chunk); err != nil { + if err := reporter.ChunkOk(ctx, chunk); err != nil { return err } } diff --git a/pkg/sources/git/git_test.go b/pkg/sources/git/git_test.go index 15914b9ecda9..e9ec417f3d54 100644 --- a/pkg/sources/git/git_test.go +++ b/pkg/sources/git/git_test.go @@ -236,7 +236,7 @@ func TestSource_Chunks_Integration(t *testing.T) { if err != nil { panic(err) } - err = s.git.ScanRepo(ctx, repo, repoPath, &tt.scanOptions, chunksCh) + err = s.git.ScanRepo(ctx, repo, repoPath, &tt.scanOptions, sources.ChanReporter{Ch: chunksCh}) if err != nil { panic(err) } diff --git a/pkg/sources/github/github.go b/pkg/sources/github/github.go index 0cfd3af305eb..6e60ff239cd1 100644 --- a/pkg/sources/github/github.go +++ b/pkg/sources/github/github.go @@ -797,7 +797,7 @@ func (s *Source) scan(ctx context.Context, installationClient *github.Client, ch logger.V(2).Info(fmt.Sprintf("scanned %d/%d repos", scanned, len(s.repos)), "repo_size", repoSize, "duration_seconds", time.Since(start).Seconds()) }(now) - if err = s.git.ScanRepo(ctx, repo, path, s.scanOptions, chunksChan); err != nil { + if err = s.git.ScanRepo(ctx, repo, path, s.scanOptions, sources.ChanReporter{Ch: chunksChan}); err != nil { scanErrs.Add(fmt.Errorf("error scanning repo %s: %w", repoURL, err)) return nil } diff --git a/pkg/sources/gitlab/gitlab.go b/pkg/sources/gitlab/gitlab.go index f1efa8917931..6923d63f0cd5 100644 --- a/pkg/sources/gitlab/gitlab.go +++ b/pkg/sources/gitlab/gitlab.go @@ -453,7 +453,7 @@ func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk) } logger.V(2).Info(fmt.Sprintf("Starting to scan repo %d/%d", i+1, len(s.repos))) - if err = s.git.ScanRepo(ctx, repo, path, s.scanOptions, chunksChan); err != nil { + if err = s.git.ScanRepo(ctx, repo, path, s.scanOptions, sources.ChanReporter{Ch: chunksChan}); err != nil { scanErrs.Add(err) return nil } diff --git a/pkg/sources/s3/s3.go b/pkg/sources/s3/s3.go index e21830b7a5a1..977cc33b2adc 100644 --- a/pkg/sources/s3/s3.go +++ b/pkg/sources/s3/s3.go @@ -14,9 +14,9 @@ import ( "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/sts" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/go-errors/errors" "github.com/go-logr/logr" + diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -376,7 +376,7 @@ func (s *Source) pageChunker(ctx context.Context, client *s3.S3, chunksChan chan }, Verify: s.verify, } - if handlers.HandleFile(ctx, reader, chunkSkel, chunksChan) { + if handlers.HandleFile(ctx, reader, chunkSkel, sources.ChanReporter{Ch: chunksChan}) { atomic.AddUint64(objectCount, 1) s.log.V(5).Info("S3 object scanned.", "object_count", objectCount, "page_number", pageNumber) return nil From 9d6bc8c50443f410df46bc6ff8c486f456036cad Mon Sep 17 00:00:00 2001 From: Miccah Date: Wed, 1 Nov 2023 09:52:58 -0700 Subject: [PATCH 09/14] Refactor git source to support scanning units (#2083) --- pkg/sources/git/git.go | 174 +++++++++++++++++++++--------------- pkg/sources/git/git_test.go | 70 +++++++++++++++ 2 files changed, 171 insertions(+), 73 deletions(-) diff --git a/pkg/sources/git/git.go b/pkg/sources/git/git.go index c01fee4e58c4..8b04fe392abd 100644 --- a/pkg/sources/git/git.go +++ b/pkg/sources/git/git.go @@ -80,8 +80,11 @@ func NewGit(sourceType sourcespb.SourceType, jobID sources.JobID, sourceID sourc } // Ensure the Source satisfies the interfaces at compile time. -var _ sources.Source = (*Source)(nil) -var _ sources.SourceUnitUnmarshaller = (*Source)(nil) +var _ interface { + sources.Source + sources.SourceUnitEnumChunker + sources.SourceUnitUnmarshaller +} = (*Source)(nil) // Type returns the type of source. // It is used for matching source types in configuration and job input. @@ -179,71 +182,52 @@ func (s *Source) scanRepos(ctx context.Context, reporter sources.ChunkReporter) return nil } totalRepos := len(s.conn.Repositories) + len(s.conn.Directories) - // TODO: refactor to remove duplicate code + for i, repoURI := range s.conn.Repositories { + s.SetProgressComplete(i, totalRepos, fmt.Sprintf("Repo: %s", repoURI), "") + if len(repoURI) == 0 { + continue + } + if err := s.scanRepo(ctx, repoURI, reporter); err != nil { + ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) + continue + } + } + return nil +} + +// scanRepo scans a single provided repository. +func (s *Source) scanRepo(ctx context.Context, repoURI string, reporter sources.ChunkReporter) error { + var cloneFunc func() (string, *git.Repository, error) switch cred := s.conn.GetCredential().(type) { case *sourcespb.Git_BasicAuth: - user := cred.BasicAuth.Username - token := cred.BasicAuth.Password - - for i, repoURI := range s.conn.Repositories { - s.SetProgressComplete(i, totalRepos, fmt.Sprintf("Repo: %s", repoURI), "") - if len(repoURI) == 0 { - continue - } - err := func(repoURI string) error { - path, repo, err := CloneRepoUsingToken(ctx, token, repoURI, user) - defer os.RemoveAll(path) - if err != nil { - return err - } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) - }(repoURI) - if err != nil { - ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) - continue - } + cloneFunc = func() (string, *git.Repository, error) { + user := cred.BasicAuth.Username + token := cred.BasicAuth.Password + return CloneRepoUsingToken(ctx, token, repoURI, user) } case *sourcespb.Git_Unauthenticated: - for i, repoURI := range s.conn.Repositories { - s.SetProgressComplete(i, totalRepos, fmt.Sprintf("Repo: %s", repoURI), "") - if len(repoURI) == 0 { - continue - } - err := func(repoURI string) error { - path, repo, err := CloneRepoUsingUnauthenticated(ctx, repoURI) - defer os.RemoveAll(path) - if err != nil { - return err - } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) - }(repoURI) - if err != nil { - ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) - continue - } + cloneFunc = func() (string, *git.Repository, error) { + return CloneRepoUsingUnauthenticated(ctx, repoURI) } case *sourcespb.Git_SshAuth: - for i, repoURI := range s.conn.Repositories { - s.SetProgressComplete(i, totalRepos, fmt.Sprintf("Repo: %s", repoURI), "") - if len(repoURI) == 0 { - continue - } - err := func(repoURI string) error { - path, repo, err := CloneRepoUsingSSH(ctx, repoURI) - defer os.RemoveAll(path) - if err != nil { - return err - } - return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) - }(repoURI) - if err != nil { - ctx.Logger().Info("error scanning repository", "repo", repoURI, "error", err) - continue - } + cloneFunc = func() (string, *git.Repository, error) { + return CloneRepoUsingSSH(ctx, repoURI) } default: return errors.New("invalid connection type for git source") } + + err := func() error { + path, repo, err := cloneFunc() + defer os.RemoveAll(path) + if err != nil { + return err + } + return s.git.ScanRepo(ctx, repo, path, s.scanOptions, reporter) + }() + if err != nil { + return reporter.ChunkErr(ctx, err) + } return nil } @@ -256,29 +240,35 @@ func (s *Source) scanDirs(ctx context.Context, reporter sources.ChunkReporter) e if len(gitDir) == 0 { continue } - if !s.scanOptions.Bare && strings.HasSuffix(gitDir, "git") { - // TODO: Figure out why we skip directories ending in "git". - continue - } - // try paths instead of url - repo, err := RepoFromPath(gitDir, s.scanOptions.Bare) - if err != nil { + if err := s.scanDir(ctx, gitDir, reporter); err != nil { ctx.Logger().Info("error scanning repository", "repo", gitDir, "error", err) continue } + } + return nil +} - err = func(repoPath string) error { - if !s.preserveTempDirs && strings.HasPrefix(repoPath, filepath.Join(os.TempDir(), "trufflehog")) { - defer os.RemoveAll(repoPath) - } +// scanDir scans a single provided directory. +func (s *Source) scanDir(ctx context.Context, gitDir string, reporter sources.ChunkReporter) error { + if !s.scanOptions.Bare && strings.HasSuffix(gitDir, "git") { + // TODO: Figure out why we skip directories ending in "git". + return nil + } + // try paths instead of url + repo, err := RepoFromPath(gitDir, s.scanOptions.Bare) + if err != nil { + return reporter.ChunkErr(ctx, err) + } - return s.git.ScanRepo(ctx, repo, repoPath, s.scanOptions, reporter) - }(gitDir) - if err != nil { - ctx.Logger().Info("error scanning repository", "repo", gitDir, "error", err) - continue + err = func() error { + if !s.preserveTempDirs && strings.HasPrefix(gitDir, filepath.Join(os.TempDir(), "trufflehog")) { + defer os.RemoveAll(gitDir) } + return s.git.ScanRepo(ctx, repo, gitDir, s.scanOptions, reporter) + }() + if err != nil { + return reporter.ChunkErr(ctx, err) } return nil } @@ -1003,6 +993,44 @@ func handleBinary(ctx context.Context, repo *git.Repository, reporter sources.Ch return nil } +func (s *Source) Enumerate(ctx context.Context, reporter sources.UnitReporter) error { + for _, repo := range s.conn.GetDirectories() { + if repo == "" { + continue + } + unit := SourceUnit{ID: repo, Kind: UnitDir} + if err := reporter.UnitOk(ctx, unit); err != nil { + return err + } + } + for _, repo := range s.conn.GetRepositories() { + if repo == "" { + continue + } + unit := SourceUnit{ID: repo, Kind: UnitRepo} + if err := reporter.UnitOk(ctx, unit); err != nil { + return err + } + } + return nil +} + +func (s *Source) ChunkUnit(ctx context.Context, unit sources.SourceUnit, reporter sources.ChunkReporter) error { + gitUnit, ok := unit.(SourceUnit) + if !ok { + return fmt.Errorf("unsupported unit type: %T", unit) + } + + switch gitUnit.Kind { + case UnitRepo: + return s.scanRepo(ctx, gitUnit.ID, reporter) + case UnitDir: + return s.scanDir(ctx, gitUnit.ID, reporter) + default: + return fmt.Errorf("unexpected git unit kind: %q", gitUnit.Kind) + } +} + func (s *Source) UnmarshalSourceUnit(data []byte) (sources.SourceUnit, error) { return UnmarshalUnit(data) } diff --git a/pkg/sources/git/git_test.go b/pkg/sources/git/git_test.go index e9ec417f3d54..cc9d31756d3d 100644 --- a/pkg/sources/git/git_test.go +++ b/pkg/sources/git/git_test.go @@ -16,6 +16,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" + "github.com/trufflesecurity/trufflehog/v3/pkg/sourcestest" ) func TestSource_Scan(t *testing.T) { @@ -504,3 +505,72 @@ func TestGitURLParse(t *testing.T) { assert.Equal(t, tt.scheme, u.Scheme) } } + +func TestEnumerate(t *testing.T) { + t.Parallel() + ctx := context.Background() + + // Setup the connection to test enumeration. + units := []string{ + "foo", "bar", "baz", + "/path/to/dir/", "/path/to/another/dir/", + } + conn, err := anypb.New(&sourcespb.Git{ + Repositories: units[0:3], + Directories: units[3:], + }) + assert.NoError(t, err) + + // Initialize the source. + s := Source{} + err = s.Init(ctx, "test enumerate", 0, 0, true, conn, 1) + assert.NoError(t, err) + + reporter := sourcestest.TestReporter{} + err = s.Enumerate(ctx, &reporter) + assert.NoError(t, err) + + assert.Equal(t, len(units), len(reporter.Units)) + assert.Equal(t, 0, len(reporter.UnitErrs)) + for _, unit := range reporter.Units { + assert.Contains(t, units, unit.SourceUnitID()) + } + for _, unit := range units[:3] { + assert.Contains(t, reporter.Units, SourceUnit{ID: unit, Kind: UnitRepo}) + } + for _, unit := range units[3:] { + assert.Contains(t, reporter.Units, SourceUnit{ID: unit, Kind: UnitDir}) + } +} + +func TestChunkUnit(t *testing.T) { + t.Parallel() + ctx := context.Background() + // Initialize the source. + s := Source{} + conn, err := anypb.New(&sourcespb.Git{ + Credential: &sourcespb.Git_Unauthenticated{}, + }) + assert.NoError(t, err) + err = s.Init(ctx, "test chunk", 0, 0, true, conn, 1) + assert.NoError(t, err) + + reporter := sourcestest.TestReporter{} + + // Happy path single repository. + err = s.ChunkUnit(ctx, SourceUnit{ + ID: "https://github.com/dustin-decker/secretsandstuff.git", + Kind: UnitRepo, + }, &reporter) + assert.NoError(t, err) + + // Error path. + err = s.ChunkUnit(ctx, SourceUnit{ + ID: "/file/not/found", + Kind: UnitDir, + }, &reporter) + assert.NoError(t, err) + + assert.Equal(t, 11, len(reporter.Chunks)) + assert.Equal(t, 1, len(reporter.ChunkErrs)) +} From 07f6c84aa47558540d34308355e1e7d622a97c4b Mon Sep 17 00:00:00 2001 From: Corben Leo <19563282+lc@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:54:40 -0500 Subject: [PATCH 10/14] Detector-Competition-Fix: Fix SentimentInvestor (deprecated) (#2078) --- pkg/detectors/sentiment/sentiment.go | 92 --- pkg/detectors/sentiment/sentiment_test.go | 122 ---- pkg/engine/defaults.go | 1 - pkg/pb/detectorspb/detectors.pb.go | 691 +++++++++++----------- proto/detectors.proto | 2 +- 5 files changed, 347 insertions(+), 561 deletions(-) delete mode 100644 pkg/detectors/sentiment/sentiment.go delete mode 100644 pkg/detectors/sentiment/sentiment_test.go diff --git a/pkg/detectors/sentiment/sentiment.go b/pkg/detectors/sentiment/sentiment.go deleted file mode 100644 index df3b67571a6c..000000000000 --- a/pkg/detectors/sentiment/sentiment.go +++ /dev/null @@ -1,92 +0,0 @@ -package sentiment - -import ( - "context" - "net/http" - "regexp" - "strings" - - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" -) - -type Scanner struct{} - -// Ensure the Scanner satisfies the interface at compile time. -var _ detectors.Detector = (*Scanner)(nil) - -var ( - client = common.SaneHttpClient() - - // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. - tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"sentiment"}) + `\b([a-zA-Z0-9]{20})\b`) - keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"sentiment"}) + `\b([0-9]{17})\b`) -) - -// 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{"sentiment"} -} - -// FromData will find and optionally verify Sentiment 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) - - tokenMatches := tokenPat.FindAllStringSubmatch(dataStr, -1) - keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) - - for _, match := range tokenMatches { - if len(match) != 2 { - continue - } - - tokenMatch := strings.TrimSpace(match[1]) - - for _, secret := range keyMatches { - if len(secret) != 2 { - continue - } - - keyMatch := strings.TrimSpace(secret[1]) - - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_Sentiment, - Raw: []byte(tokenMatch), - } - - if verify { - req, err := http.NewRequestWithContext(ctx, "GET", "https://api.sentimentinvestor.com/v4/parsed?symbol=AAPL&token="+tokenMatch+"&key="+keyMatch, nil) - if err != nil { - continue - } - - res, err := client.Do(req) - if err == nil { - defer res.Body.Close() - if res.StatusCode >= 200 && res.StatusCode < 300 { - s1.Verified = true - } else { - // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. - if detectors.IsKnownFalsePositive(tokenMatch, detectors.DefaultFalsePositives, true) { - continue - } - - if detectors.IsKnownFalsePositive(keyMatch, detectors.DefaultFalsePositives, true) { - continue - } - } - } - } - - results = append(results, s1) - } - } - - return results, nil -} - -func (s Scanner) Type() detectorspb.DetectorType { - return detectorspb.DetectorType_Sentiment -} diff --git a/pkg/detectors/sentiment/sentiment_test.go b/pkg/detectors/sentiment/sentiment_test.go deleted file mode 100644 index 1859fecdc63a..000000000000 --- a/pkg/detectors/sentiment/sentiment_test.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build detectors -// +build detectors - -package sentiment - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/kylelemons/godebug/pretty" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" - - "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" -) - -func TestSentiment_FromChunk(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") - if err != nil { - t.Fatalf("could not get test secrets from GCP: %s", err) - } - token := testSecrets.MustGetField("SENTIMENT_TOKEN") - inactiveToken := testSecrets.MustGetField("SENTIMENT_TOKEN_INACTIVE") - key := testSecrets.MustGetField("SENTIMENT_KEY") - inactiveKey := testSecrets.MustGetField("SENTIMENT_KEY_INACTIVE") - - type args struct { - ctx context.Context - data []byte - verify bool - } - tests := []struct { - name string - s Scanner - args args - want []detectors.Result - wantErr bool - }{ - { - name: "found, verified", - s: Scanner{}, - args: args{ - ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a sentiment key %s with sentiment user token %s within", key, token)), - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_Sentiment, - Verified: true, - }, - }, - wantErr: false, - }, - { - name: "found, unverified", - s: Scanner{}, - args: args{ - ctx: context.Background(), - data: []byte(fmt.Sprintf("You can find a sentiment key %s with sentiment user token %s within but not valid", inactiveKey, inactiveToken)), // the secret would satisfy the regex but not pass validation - verify: true, - }, - want: []detectors.Result{ - { - DetectorType: detectorspb.DetectorType_Sentiment, - Verified: false, - }, - }, - wantErr: 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, - }, - } - 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) - if (err != nil) != tt.wantErr { - t.Errorf("Sentiment.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]) - } - got[i].Raw = nil - } - if diff := pretty.Compare(got, tt.want); diff != "" { - t.Errorf("Sentiment.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/engine/defaults.go b/pkg/engine/defaults.go index 7ce1efca05a4..f7f43396b042 100644 --- a/pkg/engine/defaults.go +++ b/pkg/engine/defaults.go @@ -1259,7 +1259,6 @@ func DefaultDetectors() []detectors.Detector { blitapp.Scanner{}, restpackhtmltopdfapi.Scanner{}, webscraping.Scanner{}, - // sentiment.Scanner{}, geoapify.Scanner{}, dfuse.Scanner{}, gitter.Scanner{}, diff --git a/pkg/pb/detectorspb/detectors.pb.go b/pkg/pb/detectorspb/detectors.pb.go index 73528bf93bee..b570f7163a1c 100644 --- a/pkg/pb/detectorspb/detectors.pb.go +++ b/pkg/pb/detectorspb/detectors.pb.go @@ -694,12 +694,13 @@ const ( DetectorType_Squareup DetectorType = 618 DetectorType_Dandelion DetectorType = 619 // Deprecated: Do not use. - DetectorType_DataFire DetectorType = 620 - DetectorType_DeepAI DetectorType = 621 - DetectorType_MeaningCloud DetectorType = 622 - DetectorType_NeutrinoApi DetectorType = 623 - DetectorType_Storecove DetectorType = 624 - DetectorType_Shipday DetectorType = 625 + DetectorType_DataFire DetectorType = 620 + DetectorType_DeepAI DetectorType = 621 + DetectorType_MeaningCloud DetectorType = 622 + DetectorType_NeutrinoApi DetectorType = 623 + DetectorType_Storecove DetectorType = 624 + DetectorType_Shipday DetectorType = 625 + // Deprecated: Do not use. DetectorType_Sentiment DetectorType = 626 DetectorType_StreamChatMessaging DetectorType = 627 DetectorType_TeamworkCRM DetectorType = 628 @@ -3328,7 +3329,7 @@ var file_detectors_proto_rawDesc = []byte{ 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 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, 0x2a, 0x9d, 0x78, 0x0a, 0x0c, 0x44, + 0x09, 0x0a, 0x05, 0x55, 0x54, 0x46, 0x31, 0x36, 0x10, 0x03, 0x2a, 0xa1, 0x78, 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, @@ -3958,344 +3959,344 @@ var file_detectors_proto_rawDesc = []byte{ 0x75, 0x64, 0x10, 0xee, 0x04, 0x12, 0x10, 0x0a, 0x0b, 0x4e, 0x65, 0x75, 0x74, 0x72, 0x69, 0x6e, 0x6f, 0x41, 0x70, 0x69, 0x10, 0xef, 0x04, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x10, 0xf0, 0x04, 0x12, 0x0c, 0x0a, 0x07, 0x53, 0x68, 0x69, 0x70, 0x64, - 0x61, 0x79, 0x10, 0xf1, 0x04, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x6e, 0x74, 0x10, 0xf2, 0x04, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, - 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x10, 0xf3, 0x04, 0x12, - 0x10, 0x0a, 0x0b, 0x54, 0x65, 0x61, 0x6d, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x52, 0x4d, 0x10, 0xf4, - 0x04, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x65, 0x61, 0x6d, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x73, - 0x6b, 0x10, 0xf5, 0x04, 0x12, 0x13, 0x0a, 0x0e, 0x54, 0x65, 0x61, 0x6d, 0x77, 0x6f, 0x72, 0x6b, - 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0x10, 0xf6, 0x04, 0x12, 0x0f, 0x0a, 0x0a, 0x54, 0x68, 0x65, - 0x4f, 0x64, 0x64, 0x73, 0x41, 0x70, 0x69, 0x10, 0xf7, 0x04, 0x12, 0x0b, 0x0a, 0x06, 0x41, 0x70, - 0x61, 0x63, 0x74, 0x61, 0x10, 0xf8, 0x04, 0x12, 0x0f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x61, - 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x10, 0xf9, 0x04, 0x12, 0x0e, 0x0a, 0x05, 0x48, 0x61, 0x70, 0x70, - 0x69, 0x10, 0xfa, 0x04, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0a, 0x0a, 0x05, 0x4f, 0x61, 0x6e, 0x64, - 0x61, 0x10, 0xfb, 0x04, 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x61, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x65, - 0x78, 0x10, 0xfc, 0x04, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x4d, 0x61, 0x74, 0x69, 0x63, - 0x10, 0xfd, 0x04, 0x12, 0x0f, 0x0a, 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x79, - 0x65, 0x10, 0xfe, 0x04, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x61, 0x67, 0x6c, 0x65, 0x45, 0x79, 0x65, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x10, 0xff, 0x04, 0x12, 0x11, 0x0a, 0x0c, 0x54, - 0x68, 0x6f, 0x75, 0x73, 0x61, 0x6e, 0x64, 0x45, 0x79, 0x65, 0x73, 0x10, 0x80, 0x05, 0x12, 0x0e, - 0x0a, 0x09, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x50, 0x44, 0x46, 0x10, 0x81, 0x05, 0x12, 0x10, - 0x0a, 0x0b, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x74, 0x61, 0x74, 0x73, 0x10, 0x82, 0x05, - 0x12, 0x0b, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x49, 0x4f, 0x10, 0x83, 0x05, 0x12, 0x0d, 0x0a, - 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x10, 0x84, 0x05, 0x12, 0x0f, 0x0a, 0x0a, - 0x41, 0x70, 0x69, 0x53, 0x63, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x10, 0x85, 0x05, 0x12, 0x0f, 0x0a, - 0x0a, 0x41, 0x70, 0x70, 0x53, 0x79, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x10, 0x86, 0x05, 0x12, 0x0b, - 0x0a, 0x06, 0x43, 0x61, 0x66, 0x6c, 0x6f, 0x75, 0x10, 0x87, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x43, - 0x61, 0x73, 0x70, 0x69, 0x6f, 0x10, 0x88, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x6c, 0x79, 0x48, 0x51, 0x10, 0x89, 0x05, 0x12, 0x12, 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x75, - 0x64, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x10, 0x8a, 0x05, 0x12, 0x0c, 0x0a, 0x07, - 0x44, 0x72, 0x6f, 0x6e, 0x61, 0x48, 0x51, 0x10, 0x8b, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x78, 0x10, 0x8c, 0x05, 0x12, 0x09, 0x0a, 0x04, 0x46, 0x6d, 0x66, 0x77, - 0x10, 0x8d, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x6f, 0x6f, 0x64, 0x44, 0x61, 0x79, 0x10, 0x8e, - 0x05, 0x12, 0x09, 0x0a, 0x04, 0x4c, 0x75, 0x6e, 0x6f, 0x10, 0x8f, 0x05, 0x12, 0x10, 0x0a, 0x0b, - 0x4d, 0x65, 0x69, 0x73, 0x74, 0x65, 0x72, 0x74, 0x61, 0x73, 0x6b, 0x10, 0x90, 0x05, 0x12, 0x10, - 0x0a, 0x0b, 0x4d, 0x69, 0x6e, 0x64, 0x6d, 0x65, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x91, 0x05, - 0x12, 0x13, 0x0a, 0x0e, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x61, - 0x62, 0x73, 0x10, 0x92, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x63, 0x72, 0x61, 0x70, 0x65, 0x72, - 0x53, 0x69, 0x74, 0x65, 0x10, 0x93, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x63, 0x72, 0x61, 0x70, - 0x66, 0x6c, 0x79, 0x10, 0x94, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x79, - 0x4e, 0x6f, 0x74, 0x65, 0x64, 0x10, 0x95, 0x05, 0x12, 0x12, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x76, - 0x65, 0x6c, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x10, 0x96, 0x05, 0x12, 0x0f, 0x0a, 0x0a, - 0x57, 0x65, 0x62, 0x53, 0x63, 0x72, 0x61, 0x70, 0x65, 0x72, 0x10, 0x97, 0x05, 0x12, 0x0c, 0x0a, - 0x07, 0x43, 0x6f, 0x6e, 0x76, 0x69, 0x65, 0x72, 0x10, 0x98, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x43, - 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x10, 0x99, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x44, 0x69, 0x74, - 0x74, 0x6f, 0x10, 0x9a, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x46, 0x69, 0x6e, 0x64, 0x6c, 0x10, 0x9b, - 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4c, 0x65, 0x6e, 0x64, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0x9c, 0x05, - 0x12, 0x0f, 0x0a, 0x0a, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x9d, - 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x66, - 0x74, 0x10, 0x9e, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x50, 0x6f, 0x64, 0x69, 0x6f, 0x10, 0x9f, 0x05, - 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x65, 0x74, 0x10, 0xa0, 0x05, 0x12, 0x0a, - 0x0a, 0x05, 0x52, 0x6f, 0x77, 0x6e, 0x64, 0x10, 0xa1, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x68, - 0x6f, 0x74, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xa2, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x77, - 0x69, 0x66, 0x74, 0x79, 0x70, 0x65, 0x10, 0xa3, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x77, 0x69, - 0x74, 0x74, 0x65, 0x72, 0x10, 0xa4, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x48, 0x6f, 0x6e, 0x65, 0x79, - 0x10, 0xa5, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x72, 0x65, 0x73, 0x68, 0x64, 0x65, 0x73, 0x6b, - 0x10, 0xa6, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x55, 0x70, 0x77, 0x61, 0x76, 0x65, 0x10, 0xa7, 0x05, - 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x10, 0xa8, 0x05, 0x12, - 0x0f, 0x0a, 0x0a, 0x46, 0x72, 0x65, 0x73, 0x68, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x10, 0xa9, 0x05, - 0x12, 0x09, 0x0a, 0x04, 0x4d, 0x69, 0x74, 0x65, 0x10, 0xaa, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x44, - 0x65, 0x70, 0x75, 0x74, 0x79, 0x10, 0xab, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x42, 0x65, 0x65, 0x62, - 0x6f, 0x6c, 0x65, 0x10, 0xac, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x61, 0x73, 0x68, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x10, 0xad, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x4b, 0x61, 0x6e, 0x62, 0x61, 0x6e, - 0x10, 0xae, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x6e, 0x61, 0x70, 0x73, - 0x10, 0xaf, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x73, 0x10, 0xb0, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x10, 0xb1, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x68, 0x65, 0x72, - 0x70, 0x61, 0x64, 0x65, 0x73, 0x6b, 0x10, 0xb2, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x4d, 0x72, 0x74, - 0x69, 0x63, 0x6b, 0x74, 0x6f, 0x63, 0x6b, 0x10, 0xb3, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x43, 0x68, - 0x61, 0x74, 0x66, 0x75, 0x6c, 0x65, 0x10, 0xb4, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x41, 0x65, 0x72, - 0x6f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0xb5, 0x05, 0x12, 0x11, 0x0a, 0x0c, - 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x6f, 0x63, 0x74, 0x6f, 0x70, 0x75, 0x73, 0x10, 0xb6, 0x05, 0x12, - 0x0d, 0x0a, 0x08, 0x46, 0x75, 0x73, 0x65, 0x62, 0x69, 0x6c, 0x6c, 0x10, 0xb7, 0x05, 0x12, 0x0f, - 0x0a, 0x0a, 0x47, 0x65, 0x63, 0x6b, 0x6f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x10, 0xb8, 0x05, 0x12, - 0x0e, 0x0a, 0x09, 0x47, 0x6f, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x64, 0x10, 0xb9, 0x05, 0x12, - 0x0e, 0x0a, 0x09, 0x4d, 0x6f, 0x6f, 0x6e, 0x63, 0x6c, 0x65, 0x72, 0x6b, 0x10, 0xba, 0x05, 0x12, - 0x0d, 0x0a, 0x08, 0x50, 0x61, 0x79, 0x6d, 0x6f, 0x61, 0x70, 0x70, 0x10, 0xbb, 0x05, 0x12, 0x0b, - 0x0a, 0x06, 0x4d, 0x69, 0x78, 0x6d, 0x61, 0x78, 0x10, 0xbc, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x73, 0x74, 0x10, 0xbd, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x52, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x68, 0x6f, 0x70, 0x72, 0x10, 0xbe, 0x05, 0x12, 0x0d, 0x0a, - 0x08, 0x47, 0x6f, 0x73, 0x68, 0x69, 0x70, 0x70, 0x6f, 0x10, 0xbf, 0x05, 0x12, 0x0b, 0x0a, 0x06, - 0x53, 0x69, 0x67, 0x6f, 0x70, 0x74, 0x10, 0xc0, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x75, 0x67, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x10, 0xc1, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x56, 0x69, 0x65, 0x77, - 0x6e, 0x65, 0x6f, 0x10, 0xc2, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x4e, - 0x6f, 0x74, 0x65, 0x10, 0xc3, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, - 0x6e, 0x44, 0x61, 0x74, 0x61, 0x10, 0xc4, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x76, 0x69, 0x73, 0x74, 0x10, 0xc5, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x67, 0x6f, 0x10, 0xc6, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x7a, 0x65, 0x10, - 0xc7, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x49, 0x4f, 0x10, 0xc8, 0x05, 0x12, - 0x0f, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x10, 0xc9, 0x05, - 0x12, 0x0d, 0x0a, 0x08, 0x47, 0x6f, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x10, 0xca, 0x05, 0x12, - 0x0c, 0x0a, 0x07, 0x4d, 0x61, 0x64, 0x4b, 0x75, 0x64, 0x75, 0x10, 0xcb, 0x05, 0x12, 0x0f, 0x0a, - 0x0a, 0x4e, 0x6f, 0x7a, 0x62, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x10, 0xcc, 0x05, 0x12, 0x0b, - 0x0a, 0x06, 0x50, 0x61, 0x70, 0x79, 0x72, 0x73, 0x10, 0xcd, 0x05, 0x12, 0x12, 0x0a, 0x0d, 0x53, - 0x75, 0x70, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x41, 0x50, 0x49, 0x10, 0xce, 0x05, 0x12, - 0x0c, 0x0a, 0x07, 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x66, 0x79, 0x10, 0xcf, 0x05, 0x12, 0x0e, 0x0a, - 0x09, 0x5a, 0x65, 0x6e, 0x6b, 0x69, 0x74, 0x41, 0x50, 0x49, 0x10, 0xd0, 0x05, 0x12, 0x0f, 0x0a, - 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x10, 0xd1, 0x05, 0x12, 0x0f, - 0x0a, 0x0a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x72, 0x65, 0x10, 0xd2, 0x05, 0x12, - 0x0d, 0x0a, 0x08, 0x42, 0x6f, 0x72, 0x67, 0x62, 0x61, 0x73, 0x65, 0x10, 0xd3, 0x05, 0x12, 0x0e, - 0x0a, 0x09, 0x50, 0x69, 0x70, 0x65, 0x64, 0x72, 0x65, 0x61, 0x6d, 0x10, 0xd4, 0x05, 0x12, 0x09, - 0x0a, 0x04, 0x53, 0x69, 0x72, 0x76, 0x10, 0xd5, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x44, 0x69, 0x66, - 0x66, 0x62, 0x6f, 0x74, 0x10, 0xd6, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x45, 0x69, 0x67, 0x68, 0x74, - 0x78, 0x45, 0x69, 0x67, 0x68, 0x74, 0x10, 0xd7, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x53, 0x65, 0x6e, - 0x64, 0x6f, 0x73, 0x6f, 0x10, 0xd8, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x50, 0x72, 0x69, 0x6e, 0x74, - 0x66, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xd9, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x10, 0xda, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x50, 0x61, - 0x6e, 0x64, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x10, 0xdb, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x50, - 0x61, 0x79, 0x6d, 0x6f, 0x10, 0xdc, 0x05, 0x12, 0x1d, 0x0a, 0x18, 0x41, 0x76, 0x61, 0x7a, 0x61, - 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x10, 0xdd, 0x05, 0x12, 0x14, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x6e, 0x76, 0x69, - 0x65, 0x77, 0x4c, 0x65, 0x61, 0x6e, 0x4b, 0x69, 0x74, 0x10, 0xde, 0x05, 0x12, 0x0e, 0x0a, 0x09, - 0x4c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x6d, 0x10, 0xdf, 0x05, 0x12, 0x0b, 0x0a, 0x06, - 0x4b, 0x75, 0x43, 0x6f, 0x69, 0x6e, 0x10, 0xe0, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x4d, 0x65, 0x74, - 0x61, 0x41, 0x50, 0x49, 0x10, 0xe1, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4e, 0x69, 0x63, 0x65, 0x48, - 0x61, 0x73, 0x68, 0x10, 0xe2, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x65, 0x78, 0x49, 0x4f, 0x10, - 0xe3, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x4b, 0x6c, 0x69, 0x70, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x10, - 0xe4, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x79, 0x6e, 0x61, 0x74, 0x72, 0x61, 0x63, 0x65, 0x10, - 0xe5, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x6f, 0x6c, 0x6c, 0x69, 0x65, 0x41, 0x50, 0x49, 0x4b, - 0x65, 0x79, 0x10, 0xe6, 0x05, 0x12, 0x16, 0x0a, 0x11, 0x4d, 0x6f, 0x6c, 0x6c, 0x69, 0x65, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xe7, 0x05, 0x12, 0x10, 0x0a, - 0x0b, 0x42, 0x61, 0x73, 0x69, 0x73, 0x54, 0x68, 0x65, 0x6f, 0x72, 0x79, 0x10, 0xe8, 0x05, 0x12, - 0x0d, 0x0a, 0x08, 0x4e, 0x6f, 0x72, 0x64, 0x69, 0x67, 0x65, 0x6e, 0x10, 0xe9, 0x05, 0x12, 0x1c, - 0x0a, 0x17, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x45, 0x6e, 0x76, 0x69, 0x72, - 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x10, 0xea, 0x05, 0x12, 0x13, 0x0a, 0x0e, - 0x46, 0x6c, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xeb, - 0x05, 0x12, 0x08, 0x0a, 0x03, 0x4d, 0x75, 0x78, 0x10, 0xec, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x10, 0xed, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, - 0x62, 0x69, 0x72, 0x64, 0x10, 0xee, 0x05, 0x12, 0x1c, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x62, - 0x69, 0x72, 0x64, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x50, 0x49, 0x10, 0xef, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x4d, 0x69, 0x64, 0x69, 0x73, 0x65, 0x10, - 0xf0, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4d, 0x6f, 0x63, 0x6b, 0x61, 0x72, 0x6f, 0x6f, 0x10, 0xf1, - 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x34, 0x10, 0xf2, 0x05, 0x12, 0x0b, - 0x0a, 0x06, 0x50, 0x69, 0x6e, 0x61, 0x74, 0x61, 0x10, 0xf3, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x42, - 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xf4, 0x05, 0x12, 0x18, - 0x0a, 0x13, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x54, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x10, 0xf5, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4c, 0x6f, 0x61, 0x64, - 0x6d, 0x69, 0x6c, 0x6c, 0x10, 0xf6, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x67, 0x42, 0x6f, 0x74, 0x10, 0xf7, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x4b, 0x6e, 0x61, 0x70, - 0x73, 0x61, 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x10, 0xf8, 0x05, 0x12, 0x09, 0x0a, 0x04, 0x51, 0x61, - 0x73, 0x65, 0x10, 0xf9, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x61, 0x72, 0x65, 0x62, 0x6f, 0x6f, - 0x73, 0x74, 0x10, 0xfa, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x47, 0x54, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x78, 0x10, 0xfb, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x48, 0x6f, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x10, 0xfc, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x73, 0x65, 0x72, 0x73, 0x10, 0xfd, - 0x05, 0x12, 0x12, 0x0a, 0x0d, 0x53, 0x63, 0x72, 0x75, 0x74, 0x69, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x43, 0x69, 0x10, 0xfe, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x6f, 0x6e, 0x61, 0x72, 0x43, 0x6c, - 0x6f, 0x75, 0x64, 0x10, 0xff, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x50, 0x49, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x10, 0x80, 0x06, 0x12, 0x14, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x10, 0x81, 0x06, 0x12, 0x0f, - 0x0a, 0x0a, 0x43, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x79, 0x50, 0x44, 0x46, 0x10, 0x82, 0x06, 0x12, - 0x0e, 0x0a, 0x09, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x44, 0x4b, 0x10, 0x83, 0x06, 0x12, - 0x15, 0x0a, 0x0c, 0x47, 0x6c, 0x69, 0x74, 0x74, 0x65, 0x72, 0x6c, 0x79, 0x41, 0x50, 0x49, 0x10, - 0x84, 0x06, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x08, 0x48, 0x79, 0x62, 0x69, 0x73, 0x63, - 0x75, 0x73, 0x10, 0x85, 0x06, 0x12, 0x09, 0x0a, 0x04, 0x4d, 0x69, 0x72, 0x6f, 0x10, 0x86, 0x06, - 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x70, 0x61, 0x67, 0x65, 0x10, 0x87, - 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x70, 0x61, 0x6c, 0x10, 0x88, - 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x65, 0x6c, 0x65, 0x74, 0x79, 0x70, 0x65, 0x10, 0x89, 0x06, - 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x43, 0x61, 0x6d, 0x70, 0x10, 0x8a, 0x06, 0x12, - 0x0d, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0x8b, 0x06, 0x12, 0x0b, - 0x0a, 0x06, 0x57, 0x69, 0x73, 0x74, 0x69, 0x61, 0x10, 0x8c, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x53, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x64, 0x61, 0x72, 0x10, 0x8d, 0x06, 0x12, 0x10, 0x0a, 0x0b, - 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x10, 0x8e, 0x06, 0x12, 0x0e, - 0x0a, 0x09, 0x43, 0x6f, 0x64, 0x65, 0x71, 0x75, 0x69, 0x72, 0x79, 0x10, 0x8f, 0x06, 0x12, 0x11, - 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x50, 0x49, 0x10, 0x90, - 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x91, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x4d, 0x61, 0x67, 0x69, 0x63, 0x42, 0x65, 0x6c, 0x6c, 0x10, 0x92, 0x06, - 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x6d, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x10, 0x93, - 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x70, 0x69, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x10, 0x94, 0x06, - 0x12, 0x0b, 0x0a, 0x06, 0x44, 0x69, 0x73, 0x71, 0x75, 0x73, 0x10, 0x95, 0x06, 0x12, 0x0b, 0x0a, - 0x06, 0x57, 0x6f, 0x6f, 0x70, 0x72, 0x61, 0x10, 0x96, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x50, 0x61, - 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x10, 0x97, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x75, - 0x6d, 0x72, 0x6f, 0x61, 0x64, 0x10, 0x98, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x50, 0x61, 0x79, 0x64, - 0x69, 0x72, 0x74, 0x61, 0x70, 0x70, 0x10, 0x99, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x65, 0x74, - 0x65, 0x63, 0x74, 0x69, 0x66, 0x79, 0x10, 0x9a, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x63, 0x61, 0x6b, 0x65, 0x10, 0x9b, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x4a, 0x75, - 0x6d, 0x70, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x10, 0x9c, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x4c, - 0x75, 0x6e, 0x63, 0x68, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x10, 0x9d, 0x06, 0x12, 0x0c, 0x0a, 0x07, - 0x52, 0x6f, 0x73, 0x65, 0x74, 0x74, 0x65, 0x10, 0x9e, 0x06, 0x12, 0x09, 0x0a, 0x04, 0x59, 0x65, - 0x6c, 0x70, 0x10, 0x9f, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x41, 0x74, 0x65, 0x72, 0x61, 0x10, 0xa0, - 0x06, 0x12, 0x12, 0x0a, 0x0d, 0x45, 0x63, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x78, 0x75, 0x72, 0x65, - 0x49, 0x54, 0x10, 0xa1, 0x06, 0x12, 0x08, 0x0a, 0x03, 0x41, 0x68, 0x61, 0x10, 0xa2, 0x06, 0x12, - 0x0d, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x73, 0x65, 0x68, 0x75, 0x62, 0x10, 0xa3, 0x06, 0x12, 0x11, - 0x0a, 0x0c, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x10, 0xa4, - 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x10, - 0xa5, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x64, 0x61, 0x73, 0x68, 0x10, 0xa6, - 0x06, 0x12, 0x11, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x64, 0x6f, 0x63, 0x6b, 0x10, 0xa7, 0x06, - 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0b, 0x0a, 0x06, 0x46, 0x69, 0x62, 0x65, 0x72, 0x79, 0x10, 0xa8, - 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x74, 0x61, 0x6c, 0x6b, 0x10, 0xa9, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x56, 0x6f, 0x6f, 0x64, 0x6f, 0x6f, 0x53, 0x4d, 0x53, 0x10, 0xaa, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x5a, 0x75, 0x6c, 0x69, 0x70, 0x43, 0x68, 0x61, 0x74, 0x10, 0xab, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x6f, 0x72, 0x6d, 0x63, 0x72, 0x61, 0x66, 0x74, 0x10, 0xac, 0x06, - 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x65, 0x78, 0x61, 0x70, 0x69, 0x73, 0x10, 0xad, 0x06, 0x12, 0x0e, - 0x0a, 0x09, 0x52, 0x65, 0x61, 0x63, 0x68, 0x6d, 0x61, 0x69, 0x6c, 0x10, 0xae, 0x06, 0x12, 0x0f, - 0x0a, 0x0a, 0x43, 0x68, 0x61, 0x72, 0x74, 0x6d, 0x6f, 0x67, 0x75, 0x6c, 0x10, 0xaf, 0x06, 0x12, - 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x64, 0x10, 0xb0, 0x06, - 0x12, 0x08, 0x0a, 0x03, 0x57, 0x69, 0x74, 0x10, 0xb1, 0x06, 0x12, 0x15, 0x0a, 0x10, 0x52, 0x65, - 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x10, 0xb2, - 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x44, 0x69, 0x67, 0x67, 0x65, 0x72, 0x6e, 0x61, 0x75, 0x74, 0x10, - 0xb3, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x6f, 0x6e, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x61, 0x72, - 0x6e, 0x10, 0xb4, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x44, 0x75, 0x70, 0x6c, 0x79, 0x10, 0xb5, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x50, 0x6f, 0x73, 0x74, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x10, 0xb6, 0x06, - 0x12, 0x0d, 0x0a, 0x08, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x32, 0x10, 0xb7, 0x06, 0x12, - 0x0c, 0x0a, 0x07, 0x5a, 0x65, 0x6e, 0x52, 0x6f, 0x77, 0x73, 0x10, 0xb8, 0x06, 0x12, 0x10, 0x0a, - 0x0b, 0x5a, 0x69, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x62, 0x61, 0x73, 0x65, 0x10, 0xb9, 0x06, 0x12, - 0x0b, 0x0a, 0x06, 0x54, 0x65, 0x66, 0x74, 0x65, 0x72, 0x10, 0xba, 0x06, 0x12, 0x0a, 0x0a, 0x05, - 0x54, 0x77, 0x69, 0x73, 0x74, 0x10, 0xbb, 0x06, 0x12, 0x16, 0x0a, 0x11, 0x42, 0x72, 0x61, 0x69, - 0x6e, 0x74, 0x72, 0x65, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x10, 0xbc, 0x06, - 0x12, 0x11, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, - 0x10, 0xbd, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x10, 0xbe, - 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x41, 0x70, 0x69, 0x10, - 0xbf, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x77, 0x69, - 0x73, 0x65, 0x10, 0xc0, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x42, 0x75, 0x6c, 0x6b, 0x73, 0x6d, 0x73, - 0x10, 0xc1, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x62, 0x6f, 0x78, 0x10, 0xc2, - 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x4f, 0x6e, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x10, 0xc3, - 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x65, 0x6e, 0x74, 0x6d, 0x61, 0x6e, 0x10, 0xc4, 0x06, 0x12, - 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x73, 0x65, 0x75, 0x72, 0x10, 0xc5, 0x06, 0x12, 0x0e, 0x0a, - 0x09, 0x44, 0x6f, 0x63, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x10, 0xc6, 0x06, 0x12, 0x0d, 0x0a, - 0x08, 0x46, 0x6f, 0x72, 0x6d, 0x73, 0x69, 0x74, 0x65, 0x10, 0xc7, 0x06, 0x12, 0x11, 0x0a, 0x0c, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x74, 0x61, 0x69, 0x6c, 0x6f, 0x72, 0x10, 0xc8, 0x06, 0x12, - 0x0c, 0x0a, 0x07, 0x4c, 0x65, 0x6d, 0x6c, 0x69, 0x73, 0x74, 0x10, 0xc9, 0x06, 0x12, 0x0c, 0x0a, - 0x07, 0x50, 0x72, 0x6f, 0x64, 0x70, 0x61, 0x64, 0x10, 0xca, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x46, - 0x6f, 0x72, 0x6d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xcb, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x43, - 0x6f, 0x64, 0x65, 0x63, 0x6c, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x10, 0xcc, 0x06, 0x12, 0x0e, 0x0a, - 0x09, 0x43, 0x6f, 0x64, 0x65, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x10, 0xcd, 0x06, 0x12, 0x0a, 0x0a, - 0x05, 0x56, 0x62, 0x6f, 0x75, 0x74, 0x10, 0xce, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x4e, 0x69, 0x67, - 0x68, 0x74, 0x66, 0x61, 0x6c, 0x6c, 0x10, 0xcf, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x46, 0x6c, 0x69, - 0x67, 0x68, 0x74, 0x4c, 0x61, 0x62, 0x73, 0x10, 0xd0, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x53, 0x70, - 0x65, 0x65, 0x63, 0x68, 0x54, 0x65, 0x78, 0x74, 0x41, 0x49, 0x10, 0xd1, 0x06, 0x12, 0x0d, 0x0a, - 0x08, 0x50, 0x6f, 0x6c, 0x6c, 0x73, 0x41, 0x50, 0x49, 0x10, 0xd2, 0x06, 0x12, 0x0b, 0x0a, 0x06, - 0x53, 0x69, 0x6d, 0x46, 0x69, 0x6e, 0x10, 0xd3, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x53, 0x63, 0x61, - 0x6c, 0x72, 0x10, 0xd4, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x4b, 0x61, 0x6e, 0x62, 0x61, 0x6e, 0x74, - 0x6f, 0x6f, 0x6c, 0x10, 0xd5, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x72, 0x69, 0x67, 0x68, 0x74, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x10, 0xd6, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x48, 0x6f, 0x74, 0x77, - 0x69, 0x72, 0x65, 0x10, 0xd7, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x62, - 0x6f, 0x74, 0x10, 0xd8, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6b, 0x69, 0x74, - 0x10, 0xd9, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x6c, 0x6c, - 0x65, 0x72, 0x10, 0xda, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x6f, 0x6a, 0x6f, 0x68, 0x65, 0x6c, - 0x70, 0x64, 0x65, 0x73, 0x6b, 0x10, 0xdb, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x73, 0x65, 0x6e, 0x64, 0x10, 0xdc, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x47, 0x65, 0x74, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x10, 0xdd, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x44, - 0x79, 0x6e, 0x61, 0x64, 0x6f, 0x74, 0x10, 0xde, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x44, 0x65, 0x6d, - 0x69, 0x6f, 0x10, 0xdf, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x54, 0x6f, 0x6b, 0x65, 0x65, 0x74, 0x10, - 0xe0, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x79, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, - 0x6e, 0x74, 0x10, 0xe1, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x6f, 0x70, 0x79, 0x73, 0x63, 0x61, - 0x70, 0x65, 0x10, 0xe2, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x65, 0x73, 0x6e, 0x61, 0x70, 0x70, - 0x79, 0x10, 0xe3, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x61, 0x6c, 0x65, 0x73, 0x6d, 0x61, 0x74, - 0x65, 0x10, 0xe4, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x74, 0x6d, 0x61, 0x70, 0x61, - 0x70, 0x69, 0x10, 0xe5, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, - 0x70, 0x75, 0x6c, 0x73, 0x65, 0x10, 0xe6, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x55, 0x63, 0x6c, 0x61, - 0x73, 0x73, 0x69, 0x66, 0x79, 0x10, 0xe7, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x74, 0x10, 0xe8, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x44, 0x46, 0x6d, 0x79, 0x55, - 0x52, 0x4c, 0x10, 0xe9, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x70, 0x69, 0x32, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x74, 0x10, 0xea, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x4f, 0x70, 0x73, 0x67, 0x65, - 0x6e, 0x69, 0x65, 0x10, 0xeb, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x47, 0x65, 0x6d, 0x69, 0x6e, 0x69, - 0x10, 0xec, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x48, 0x6f, 0x6e, 0x65, 0x79, 0x63, 0x6f, 0x6d, 0x62, - 0x10, 0xed, 0x06, 0x12, 0x14, 0x0a, 0x0f, 0x4b, 0x61, 0x6c, 0x74, 0x75, 0x72, 0x61, 0x41, 0x70, - 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xee, 0x06, 0x12, 0x13, 0x0a, 0x0e, 0x4b, 0x61, 0x6c, - 0x74, 0x75, 0x72, 0x61, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0xef, 0x06, 0x12, 0x0a, - 0x0a, 0x05, 0x42, 0x69, 0x74, 0x47, 0x6f, 0x10, 0xf0, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x4f, 0x70, - 0x74, 0x69, 0x64, 0x61, 0x73, 0x68, 0x10, 0xf1, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x49, 0x6d, 0x67, - 0x69, 0x78, 0x10, 0xf2, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x6f, - 0x54, 0x65, 0x78, 0x74, 0x10, 0xf3, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x50, 0x61, 0x67, 0x65, 0x32, - 0x49, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x10, 0xf4, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x51, 0x75, 0x69, - 0x63, 0x6b, 0x62, 0x61, 0x73, 0x65, 0x10, 0xf5, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x52, 0x65, 0x64, - 0x62, 0x6f, 0x6f, 0x74, 0x68, 0x10, 0xf6, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x4e, 0x75, 0x62, 0x65, - 0x6c, 0x61, 0x10, 0xf7, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x6e, 0x66, 0x6f, 0x62, 0x69, 0x70, - 0x10, 0xf8, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x55, 0x70, 0x72, 0x6f, 0x63, 0x10, 0xf9, 0x06, 0x12, - 0x0f, 0x0a, 0x0a, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x62, 0x65, 0x65, 0x10, 0xfa, 0x06, - 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x66, 0x74, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x10, 0xfb, 0x06, - 0x12, 0x0c, 0x0a, 0x07, 0x45, 0x64, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x10, 0xfc, 0x06, 0x12, 0x0b, - 0x0a, 0x06, 0x54, 0x65, 0x61, 0x6d, 0x75, 0x70, 0x10, 0xfd, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x57, - 0x6f, 0x72, 0x6b, 0x64, 0x61, 0x79, 0x10, 0xfe, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, - 0x67, 0x6f, 0x44, 0x42, 0x10, 0xff, 0x06, 0x12, 0x08, 0x0a, 0x03, 0x4e, 0x47, 0x43, 0x10, 0x80, - 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x4f, 0x63, 0x65, 0x61, - 0x6e, 0x56, 0x32, 0x10, 0x81, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x51, 0x4c, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x10, 0x82, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x46, 0x54, 0x50, 0x10, 0x83, 0x07, - 0x12, 0x0a, 0x0a, 0x05, 0x52, 0x65, 0x64, 0x69, 0x73, 0x10, 0x84, 0x07, 0x12, 0x09, 0x0a, 0x04, - 0x4c, 0x44, 0x41, 0x50, 0x10, 0x85, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x53, 0x68, 0x6f, 0x70, 0x69, - 0x66, 0x79, 0x10, 0x86, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x52, 0x61, 0x62, 0x62, 0x69, 0x74, 0x4d, - 0x51, 0x10, 0x87, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, - 0x67, 0x65, 0x78, 0x10, 0x88, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x45, 0x74, 0x68, 0x65, 0x72, 0x73, - 0x63, 0x61, 0x6e, 0x10, 0x89, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x49, 0x6e, 0x66, 0x75, 0x72, 0x61, - 0x10, 0x8a, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x41, 0x6c, 0x63, 0x68, 0x65, 0x6d, 0x79, 0x10, 0x8b, - 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x10, 0x8c, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4d, 0x6f, 0x72, 0x61, 0x6c, 0x69, 0x73, 0x10, 0x8d, - 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x42, 0x73, 0x63, 0x53, 0x63, 0x61, 0x6e, 0x10, 0x8e, 0x07, 0x12, - 0x12, 0x0a, 0x0d, 0x43, 0x6f, 0x69, 0x6e, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x43, 0x61, 0x70, - 0x10, 0x8f, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x50, 0x65, 0x72, 0x63, 0x79, 0x10, 0x90, 0x07, 0x12, - 0x11, 0x0a, 0x0c, 0x54, 0x69, 0x6e, 0x65, 0x73, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x10, - 0x91, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x50, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x10, 0x92, 0x07, 0x12, - 0x12, 0x0a, 0x0d, 0x53, 0x75, 0x70, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x10, 0x93, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x4e, 0x75, 0x47, 0x65, 0x74, 0x41, 0x70, 0x69, 0x4b, - 0x65, 0x79, 0x10, 0x94, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x41, 0x69, 0x76, 0x65, 0x6e, 0x10, 0x95, - 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x66, 0x65, 0x63, 0x74, 0x10, 0x96, 0x07, 0x12, - 0x0d, 0x0a, 0x08, 0x44, 0x6f, 0x63, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x10, 0x97, 0x07, 0x12, 0x0e, - 0x0a, 0x09, 0x43, 0x6f, 0x75, 0x63, 0x68, 0x62, 0x61, 0x73, 0x65, 0x10, 0x98, 0x07, 0x12, 0x0e, - 0x0a, 0x09, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x68, 0x75, 0x62, 0x10, 0x99, 0x07, 0x12, 0x19, - 0x0a, 0x14, 0x54, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x10, 0x9a, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x45, 0x6e, 0x76, - 0x6f, 0x79, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0x9b, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x47, - 0x69, 0x74, 0x48, 0x75, 0x62, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x10, 0x9c, 0x07, 0x12, 0x0f, - 0x0a, 0x0a, 0x53, 0x61, 0x6c, 0x65, 0x73, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x10, 0x9d, 0x07, 0x12, - 0x10, 0x0a, 0x0b, 0x48, 0x75, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x65, 0x10, 0x9e, - 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x6e, 0x6f, 0x77, 0x66, 0x6c, 0x61, 0x6b, 0x65, 0x10, 0x9f, - 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, - 0x10, 0xa0, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x54, 0x61, 0x69, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x10, 0xa1, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x57, 0x65, 0x62, 0x33, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x10, 0xa2, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x10, 0xa3, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x50, 0x6c, 0x61, 0x6e, - 0x65, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x62, 0x10, 0xa4, 0x07, 0x12, 0x0e, 0x0a, 0x09, - 0x41, 0x6e, 0x74, 0x68, 0x72, 0x6f, 0x70, 0x69, 0x63, 0x10, 0xa5, 0x07, 0x12, 0x09, 0x0a, 0x04, - 0x52, 0x61, 0x6d, 0x70, 0x10, 0xa6, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4b, 0x6c, 0x61, 0x76, 0x69, - 0x79, 0x6f, 0x10, 0xa7, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, - 0x72, 0x61, 0x70, 0x68, 0x43, 0x6f, 0x64, 0x79, 0x10, 0xa8, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x56, - 0x6f, 0x69, 0x63, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0xa9, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x50, - 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x10, 0xaa, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x49, 0x50, 0x49, - 0x6e, 0x66, 0x6f, 0x10, 0xab, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x70, 0x32, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xac, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6d, 0x6f, 0x6a, 0x6f, 0x10, 0xad, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x50, 0x6f, 0x72, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x10, 0xae, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x50, 0x6f, 0x72, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xaf, 0x07, 0x12, 0x0b, 0x0a, - 0x06, 0x4c, 0x6f, 0x67, 0x67, 0x6c, 0x79, 0x10, 0xb0, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4f, 0x70, - 0x65, 0x6e, 0x56, 0x70, 0x6e, 0x10, 0xb1, 0x07, 0x12, 0x1e, 0x0a, 0x19, 0x56, 0x61, 0x67, 0x72, - 0x61, 0x6e, 0x74, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xb2, 0x07, 0x12, 0x10, 0x0a, 0x0b, 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, 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, + 0x61, 0x79, 0x10, 0xf1, 0x04, 0x12, 0x12, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x6e, 0x74, 0x10, 0xf2, 0x04, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, + 0x10, 0xf3, 0x04, 0x12, 0x10, 0x0a, 0x0b, 0x54, 0x65, 0x61, 0x6d, 0x77, 0x6f, 0x72, 0x6b, 0x43, + 0x52, 0x4d, 0x10, 0xf4, 0x04, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x65, 0x61, 0x6d, 0x77, 0x6f, 0x72, + 0x6b, 0x44, 0x65, 0x73, 0x6b, 0x10, 0xf5, 0x04, 0x12, 0x13, 0x0a, 0x0e, 0x54, 0x65, 0x61, 0x6d, + 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0x10, 0xf6, 0x04, 0x12, 0x0f, 0x0a, + 0x0a, 0x54, 0x68, 0x65, 0x4f, 0x64, 0x64, 0x73, 0x41, 0x70, 0x69, 0x10, 0xf7, 0x04, 0x12, 0x0b, + 0x0a, 0x06, 0x41, 0x70, 0x61, 0x63, 0x74, 0x61, 0x10, 0xf8, 0x04, 0x12, 0x0f, 0x0a, 0x0a, 0x47, + 0x65, 0x74, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x10, 0xf9, 0x04, 0x12, 0x0e, 0x0a, 0x05, + 0x48, 0x61, 0x70, 0x70, 0x69, 0x10, 0xfa, 0x04, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0a, 0x0a, 0x05, + 0x4f, 0x61, 0x6e, 0x64, 0x61, 0x10, 0xfb, 0x04, 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x61, 0x73, 0x74, + 0x46, 0x6f, 0x72, 0x65, 0x78, 0x10, 0xfc, 0x04, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x4d, + 0x61, 0x74, 0x69, 0x63, 0x10, 0xfd, 0x04, 0x12, 0x0f, 0x0a, 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x45, 0x79, 0x65, 0x10, 0xfe, 0x04, 0x12, 0x15, 0x0a, 0x10, 0x45, 0x61, 0x67, 0x6c, + 0x65, 0x45, 0x79, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x10, 0xff, 0x04, 0x12, + 0x11, 0x0a, 0x0c, 0x54, 0x68, 0x6f, 0x75, 0x73, 0x61, 0x6e, 0x64, 0x45, 0x79, 0x65, 0x73, 0x10, + 0x80, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x50, 0x44, 0x46, 0x10, + 0x81, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x74, 0x61, 0x74, + 0x73, 0x10, 0x82, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x49, 0x4f, 0x10, 0x83, + 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x10, 0x84, 0x05, + 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x53, 0x63, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x10, 0x85, + 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x53, 0x79, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x10, + 0x86, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x43, 0x61, 0x66, 0x6c, 0x6f, 0x75, 0x10, 0x87, 0x05, 0x12, + 0x0b, 0x0a, 0x06, 0x43, 0x61, 0x73, 0x70, 0x69, 0x6f, 0x10, 0x88, 0x05, 0x12, 0x0e, 0x0a, 0x09, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6c, 0x79, 0x48, 0x51, 0x10, 0x89, 0x05, 0x12, 0x12, 0x0a, 0x0d, + 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x10, 0x8a, 0x05, + 0x12, 0x0c, 0x0a, 0x07, 0x44, 0x72, 0x6f, 0x6e, 0x61, 0x48, 0x51, 0x10, 0x8b, 0x05, 0x12, 0x0c, + 0x0a, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x78, 0x10, 0x8c, 0x05, 0x12, 0x09, 0x0a, 0x04, + 0x46, 0x6d, 0x66, 0x77, 0x10, 0x8d, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x6f, 0x6f, 0x64, 0x44, + 0x61, 0x79, 0x10, 0x8e, 0x05, 0x12, 0x09, 0x0a, 0x04, 0x4c, 0x75, 0x6e, 0x6f, 0x10, 0x8f, 0x05, + 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x65, 0x69, 0x73, 0x74, 0x65, 0x72, 0x74, 0x61, 0x73, 0x6b, 0x10, + 0x90, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x69, 0x6e, 0x64, 0x6d, 0x65, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x10, 0x91, 0x05, 0x12, 0x13, 0x0a, 0x0e, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x4c, 0x61, 0x62, 0x73, 0x10, 0x92, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x63, 0x72, + 0x61, 0x70, 0x65, 0x72, 0x53, 0x69, 0x74, 0x65, 0x10, 0x93, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x53, + 0x63, 0x72, 0x61, 0x70, 0x66, 0x6c, 0x79, 0x10, 0x94, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x69, + 0x6d, 0x70, 0x6c, 0x79, 0x4e, 0x6f, 0x74, 0x65, 0x64, 0x10, 0x95, 0x05, 0x12, 0x12, 0x0a, 0x0d, + 0x54, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x10, 0x96, 0x05, + 0x12, 0x0f, 0x0a, 0x0a, 0x57, 0x65, 0x62, 0x53, 0x63, 0x72, 0x61, 0x70, 0x65, 0x72, 0x10, 0x97, + 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x76, 0x69, 0x65, 0x72, 0x10, 0x98, 0x05, 0x12, + 0x0c, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x10, 0x99, 0x05, 0x12, 0x0a, 0x0a, + 0x05, 0x44, 0x69, 0x74, 0x74, 0x6f, 0x10, 0x9a, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x46, 0x69, 0x6e, + 0x64, 0x6c, 0x10, 0x9b, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4c, 0x65, 0x6e, 0x64, 0x66, 0x6c, 0x6f, + 0x77, 0x10, 0x9c, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x10, 0x9d, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x6f, 0x66, 0x74, 0x10, 0x9e, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x50, 0x6f, 0x64, 0x69, + 0x6f, 0x10, 0x9f, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x65, 0x74, 0x10, + 0xa0, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x52, 0x6f, 0x77, 0x6e, 0x64, 0x10, 0xa1, 0x05, 0x12, 0x0e, + 0x0a, 0x09, 0x53, 0x68, 0x6f, 0x74, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xa2, 0x05, 0x12, 0x0d, + 0x0a, 0x08, 0x53, 0x77, 0x69, 0x66, 0x74, 0x79, 0x70, 0x65, 0x10, 0xa3, 0x05, 0x12, 0x0c, 0x0a, + 0x07, 0x54, 0x77, 0x69, 0x74, 0x74, 0x65, 0x72, 0x10, 0xa4, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x48, + 0x6f, 0x6e, 0x65, 0x79, 0x10, 0xa5, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x72, 0x65, 0x73, 0x68, + 0x64, 0x65, 0x73, 0x6b, 0x10, 0xa6, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x55, 0x70, 0x77, 0x61, 0x76, + 0x65, 0x10, 0xa7, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x10, 0xa8, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x46, 0x72, 0x65, 0x73, 0x68, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x10, 0xa9, 0x05, 0x12, 0x09, 0x0a, 0x04, 0x4d, 0x69, 0x74, 0x65, 0x10, 0xaa, 0x05, 0x12, + 0x0b, 0x0a, 0x06, 0x44, 0x65, 0x70, 0x75, 0x74, 0x79, 0x10, 0xab, 0x05, 0x12, 0x0c, 0x0a, 0x07, + 0x42, 0x65, 0x65, 0x62, 0x6f, 0x6c, 0x65, 0x10, 0xac, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x61, + 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x10, 0xad, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x4b, 0x61, + 0x6e, 0x62, 0x61, 0x6e, 0x10, 0xae, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x10, 0xaf, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x79, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x10, 0xb0, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x10, 0xb1, 0x05, 0x12, 0x0f, 0x0a, 0x0a, + 0x53, 0x68, 0x65, 0x72, 0x70, 0x61, 0x64, 0x65, 0x73, 0x6b, 0x10, 0xb2, 0x05, 0x12, 0x0f, 0x0a, + 0x0a, 0x4d, 0x72, 0x74, 0x69, 0x63, 0x6b, 0x74, 0x6f, 0x63, 0x6b, 0x10, 0xb3, 0x05, 0x12, 0x0d, + 0x0a, 0x08, 0x43, 0x68, 0x61, 0x74, 0x66, 0x75, 0x6c, 0x65, 0x10, 0xb4, 0x05, 0x12, 0x11, 0x0a, + 0x0c, 0x41, 0x65, 0x72, 0x6f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0xb5, 0x05, + 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x6f, 0x63, 0x74, 0x6f, 0x70, 0x75, 0x73, + 0x10, 0xb6, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x75, 0x73, 0x65, 0x62, 0x69, 0x6c, 0x6c, 0x10, + 0xb7, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x47, 0x65, 0x63, 0x6b, 0x6f, 0x62, 0x6f, 0x61, 0x72, 0x64, + 0x10, 0xb8, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x47, 0x6f, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x64, + 0x10, 0xb9, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x4d, 0x6f, 0x6f, 0x6e, 0x63, 0x6c, 0x65, 0x72, 0x6b, + 0x10, 0xba, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x61, 0x79, 0x6d, 0x6f, 0x61, 0x70, 0x70, 0x10, + 0xbb, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x4d, 0x69, 0x78, 0x6d, 0x61, 0x78, 0x10, 0xbc, 0x05, 0x12, + 0x0e, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x73, 0x74, 0x10, 0xbd, 0x05, 0x12, + 0x10, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x68, 0x6f, 0x70, 0x72, 0x10, 0xbe, + 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x47, 0x6f, 0x73, 0x68, 0x69, 0x70, 0x70, 0x6f, 0x10, 0xbf, 0x05, + 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6f, 0x70, 0x74, 0x10, 0xc0, 0x05, 0x12, 0x0d, 0x0a, + 0x08, 0x53, 0x75, 0x67, 0x65, 0x73, 0x74, 0x65, 0x72, 0x10, 0xc1, 0x05, 0x12, 0x0c, 0x0a, 0x07, + 0x56, 0x69, 0x65, 0x77, 0x6e, 0x65, 0x6f, 0x10, 0xc2, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x42, 0x6f, + 0x6f, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x10, 0xc3, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x61, + 0x70, 0x74, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x10, 0xc4, 0x05, 0x12, 0x0e, 0x0a, 0x09, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x76, 0x69, 0x73, 0x74, 0x10, 0xc5, 0x05, 0x12, 0x0c, 0x0a, 0x07, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x67, 0x6f, 0x10, 0xc6, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x6c, + 0x6f, 0x7a, 0x65, 0x10, 0xc7, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x49, 0x4f, + 0x10, 0xc8, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x10, 0xc9, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x47, 0x6f, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x10, 0xca, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x4d, 0x61, 0x64, 0x4b, 0x75, 0x64, 0x75, 0x10, 0xcb, + 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x4e, 0x6f, 0x7a, 0x62, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x10, + 0xcc, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x50, 0x61, 0x70, 0x79, 0x72, 0x73, 0x10, 0xcd, 0x05, 0x12, + 0x12, 0x0a, 0x0d, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x41, 0x50, 0x49, + 0x10, 0xce, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x66, 0x79, 0x10, 0xcf, + 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x5a, 0x65, 0x6e, 0x6b, 0x69, 0x74, 0x41, 0x50, 0x49, 0x10, 0xd0, + 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x10, + 0xd1, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x72, 0x65, + 0x10, 0xd2, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x6f, 0x72, 0x67, 0x62, 0x61, 0x73, 0x65, 0x10, + 0xd3, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x50, 0x69, 0x70, 0x65, 0x64, 0x72, 0x65, 0x61, 0x6d, 0x10, + 0xd4, 0x05, 0x12, 0x09, 0x0a, 0x04, 0x53, 0x69, 0x72, 0x76, 0x10, 0xd5, 0x05, 0x12, 0x0c, 0x0a, + 0x07, 0x44, 0x69, 0x66, 0x66, 0x62, 0x6f, 0x74, 0x10, 0xd6, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x45, + 0x69, 0x67, 0x68, 0x74, 0x78, 0x45, 0x69, 0x67, 0x68, 0x74, 0x10, 0xd7, 0x05, 0x12, 0x0c, 0x0a, + 0x07, 0x53, 0x65, 0x6e, 0x64, 0x6f, 0x73, 0x6f, 0x10, 0xd8, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x50, + 0x72, 0x69, 0x6e, 0x74, 0x66, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xd9, 0x05, 0x12, 0x0e, + 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x10, 0xda, 0x05, 0x12, 0x0f, + 0x0a, 0x0a, 0x50, 0x61, 0x6e, 0x64, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x10, 0xdb, 0x05, 0x12, + 0x0a, 0x0a, 0x05, 0x50, 0x61, 0x79, 0x6d, 0x6f, 0x10, 0xdc, 0x05, 0x12, 0x1d, 0x0a, 0x18, 0x41, + 0x76, 0x61, 0x7a, 0x61, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xdd, 0x05, 0x12, 0x14, 0x0a, 0x0f, 0x50, 0x6c, + 0x61, 0x6e, 0x76, 0x69, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x6e, 0x4b, 0x69, 0x74, 0x10, 0xde, 0x05, + 0x12, 0x0e, 0x0a, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x6d, 0x10, 0xdf, 0x05, + 0x12, 0x0b, 0x0a, 0x06, 0x4b, 0x75, 0x43, 0x6f, 0x69, 0x6e, 0x10, 0xe0, 0x05, 0x12, 0x0c, 0x0a, + 0x07, 0x4d, 0x65, 0x74, 0x61, 0x41, 0x50, 0x49, 0x10, 0xe1, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4e, + 0x69, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x10, 0xe2, 0x05, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x65, + 0x78, 0x49, 0x4f, 0x10, 0xe3, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x4b, 0x6c, 0x69, 0x70, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x10, 0xe4, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x79, 0x6e, 0x61, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x10, 0xe5, 0x05, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x6f, 0x6c, 0x6c, 0x69, 0x65, + 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x10, 0xe6, 0x05, 0x12, 0x16, 0x0a, 0x11, 0x4d, 0x6f, 0x6c, + 0x6c, 0x69, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xe7, + 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x61, 0x73, 0x69, 0x73, 0x54, 0x68, 0x65, 0x6f, 0x72, 0x79, + 0x10, 0xe8, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4e, 0x6f, 0x72, 0x64, 0x69, 0x67, 0x65, 0x6e, 0x10, + 0xe9, 0x05, 0x12, 0x1c, 0x0a, 0x17, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x10, 0xea, 0x05, + 0x12, 0x13, 0x0a, 0x0e, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x10, 0xeb, 0x05, 0x12, 0x08, 0x0a, 0x03, 0x4d, 0x75, 0x78, 0x10, 0xec, 0x05, 0x12, + 0x0b, 0x0a, 0x06, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x10, 0xed, 0x05, 0x12, 0x0d, 0x0a, 0x08, + 0x53, 0x65, 0x6e, 0x64, 0x62, 0x69, 0x72, 0x64, 0x10, 0xee, 0x05, 0x12, 0x1c, 0x0a, 0x17, 0x53, + 0x65, 0x6e, 0x64, 0x62, 0x69, 0x72, 0x64, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x50, 0x49, 0x10, 0xef, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x4d, 0x69, 0x64, + 0x69, 0x73, 0x65, 0x10, 0xf0, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x4d, 0x6f, 0x63, 0x6b, 0x61, 0x72, + 0x6f, 0x6f, 0x10, 0xf1, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x34, 0x10, + 0xf2, 0x05, 0x12, 0x0b, 0x0a, 0x06, 0x50, 0x69, 0x6e, 0x61, 0x74, 0x61, 0x10, 0xf3, 0x05, 0x12, + 0x11, 0x0a, 0x0c, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x10, + 0xf4, 0x05, 0x12, 0x18, 0x0a, 0x13, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x42, 0x72, 0x6f, 0x77, 0x73, + 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x10, 0xf5, 0x05, 0x12, 0x0d, 0x0a, 0x08, + 0x4c, 0x6f, 0x61, 0x64, 0x6d, 0x69, 0x6c, 0x6c, 0x10, 0xf6, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x54, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x6f, 0x74, 0x10, 0xf7, 0x05, 0x12, 0x10, 0x0a, 0x0b, + 0x4b, 0x6e, 0x61, 0x70, 0x73, 0x61, 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x10, 0xf8, 0x05, 0x12, 0x09, + 0x0a, 0x04, 0x51, 0x61, 0x73, 0x65, 0x10, 0xf9, 0x05, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x61, 0x72, + 0x65, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x10, 0xfa, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x47, 0x54, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x78, 0x10, 0xfb, 0x05, 0x12, 0x0d, 0x0a, 0x08, 0x48, 0x6f, 0x6c, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x10, 0xfc, 0x05, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x73, 0x10, 0xfd, 0x05, 0x12, 0x12, 0x0a, 0x0d, 0x53, 0x63, 0x72, 0x75, 0x74, 0x69, 0x6e, + 0x69, 0x7a, 0x65, 0x72, 0x43, 0x69, 0x10, 0xfe, 0x05, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x6f, 0x6e, + 0x61, 0x72, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x10, 0xff, 0x05, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x50, + 0x49, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x10, 0x80, 0x06, 0x12, 0x14, 0x0a, 0x0f, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x10, + 0x81, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x79, 0x50, 0x44, 0x46, + 0x10, 0x82, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x44, 0x4b, + 0x10, 0x83, 0x06, 0x12, 0x15, 0x0a, 0x0c, 0x47, 0x6c, 0x69, 0x74, 0x74, 0x65, 0x72, 0x6c, 0x79, + 0x41, 0x50, 0x49, 0x10, 0x84, 0x06, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x08, 0x48, 0x79, + 0x62, 0x69, 0x73, 0x63, 0x75, 0x73, 0x10, 0x85, 0x06, 0x12, 0x09, 0x0a, 0x04, 0x4d, 0x69, 0x72, + 0x6f, 0x10, 0x86, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x70, 0x61, + 0x67, 0x65, 0x10, 0x87, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x70, + 0x61, 0x6c, 0x10, 0x88, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x65, 0x6c, 0x65, 0x74, 0x79, 0x70, + 0x65, 0x10, 0x89, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x43, 0x61, 0x6d, 0x70, + 0x10, 0x8a, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x10, + 0x8b, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x57, 0x69, 0x73, 0x74, 0x69, 0x61, 0x10, 0x8c, 0x06, 0x12, + 0x0f, 0x0a, 0x0a, 0x53, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x64, 0x61, 0x72, 0x10, 0x8d, 0x06, + 0x12, 0x10, 0x0a, 0x0b, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x10, + 0x8e, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x6f, 0x64, 0x65, 0x71, 0x75, 0x69, 0x72, 0x79, 0x10, + 0x8f, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x41, + 0x50, 0x49, 0x10, 0x90, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x10, 0x91, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x4d, 0x61, 0x67, 0x69, 0x63, 0x42, 0x65, 0x6c, + 0x6c, 0x10, 0x92, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x6d, 0x62, 0x6f, 0x61, + 0x72, 0x64, 0x10, 0x93, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x70, 0x69, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x10, 0x94, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x44, 0x69, 0x73, 0x71, 0x75, 0x73, 0x10, 0x95, + 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x57, 0x6f, 0x6f, 0x70, 0x72, 0x61, 0x10, 0x96, 0x06, 0x12, 0x0e, + 0x0a, 0x09, 0x50, 0x61, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x10, 0x97, 0x06, 0x12, 0x0c, + 0x0a, 0x07, 0x47, 0x75, 0x6d, 0x72, 0x6f, 0x61, 0x64, 0x10, 0x98, 0x06, 0x12, 0x0f, 0x0a, 0x0a, + 0x50, 0x61, 0x79, 0x64, 0x69, 0x72, 0x74, 0x61, 0x70, 0x70, 0x10, 0x99, 0x06, 0x12, 0x0e, 0x0a, + 0x09, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x69, 0x66, 0x79, 0x10, 0x9a, 0x06, 0x12, 0x0f, 0x0a, + 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x63, 0x61, 0x6b, 0x65, 0x10, 0x9b, 0x06, 0x12, 0x0f, + 0x0a, 0x0a, 0x4a, 0x75, 0x6d, 0x70, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x10, 0x9c, 0x06, 0x12, + 0x0f, 0x0a, 0x0a, 0x4c, 0x75, 0x6e, 0x63, 0x68, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x10, 0x9d, 0x06, + 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x6f, 0x73, 0x65, 0x74, 0x74, 0x65, 0x10, 0x9e, 0x06, 0x12, 0x09, + 0x0a, 0x04, 0x59, 0x65, 0x6c, 0x70, 0x10, 0x9f, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x41, 0x74, 0x65, + 0x72, 0x61, 0x10, 0xa0, 0x06, 0x12, 0x12, 0x0a, 0x0d, 0x45, 0x63, 0x6f, 0x53, 0x74, 0x72, 0x75, + 0x78, 0x75, 0x72, 0x65, 0x49, 0x54, 0x10, 0xa1, 0x06, 0x12, 0x08, 0x0a, 0x03, 0x41, 0x68, 0x61, + 0x10, 0xa2, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x73, 0x65, 0x68, 0x75, 0x62, 0x10, + 0xa3, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x6f, + 0x75, 0x64, 0x10, 0xa4, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x6d, + 0x69, 0x74, 0x68, 0x10, 0xa5, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x64, 0x61, + 0x73, 0x68, 0x10, 0xa6, 0x06, 0x12, 0x11, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x64, 0x6f, 0x63, + 0x6b, 0x10, 0xa7, 0x06, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0b, 0x0a, 0x06, 0x46, 0x69, 0x62, 0x65, + 0x72, 0x79, 0x10, 0xa8, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x74, 0x61, 0x6c, + 0x6b, 0x10, 0xa9, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x56, 0x6f, 0x6f, 0x64, 0x6f, 0x6f, 0x53, 0x4d, + 0x53, 0x10, 0xaa, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x5a, 0x75, 0x6c, 0x69, 0x70, 0x43, 0x68, 0x61, + 0x74, 0x10, 0xab, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x46, 0x6f, 0x72, 0x6d, 0x63, 0x72, 0x61, 0x66, + 0x74, 0x10, 0xac, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x65, 0x78, 0x61, 0x70, 0x69, 0x73, 0x10, + 0xad, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x63, 0x68, 0x6d, 0x61, 0x69, 0x6c, 0x10, + 0xae, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x68, 0x61, 0x72, 0x74, 0x6d, 0x6f, 0x67, 0x75, 0x6c, + 0x10, 0xaf, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x64, + 0x64, 0x10, 0xb0, 0x06, 0x12, 0x08, 0x0a, 0x03, 0x57, 0x69, 0x74, 0x10, 0xb1, 0x06, 0x12, 0x15, + 0x0a, 0x10, 0x52, 0x65, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x10, 0xb2, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x44, 0x69, 0x67, 0x67, 0x65, 0x72, 0x6e, + 0x61, 0x75, 0x74, 0x10, 0xb3, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x4d, 0x6f, 0x6e, 0x6b, 0x65, 0x79, + 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x10, 0xb4, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x44, 0x75, 0x70, 0x6c, + 0x79, 0x10, 0xb5, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x50, 0x6f, 0x73, 0x74, 0x62, 0x61, 0x63, 0x6b, + 0x73, 0x10, 0xb6, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x32, + 0x10, 0xb7, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x5a, 0x65, 0x6e, 0x52, 0x6f, 0x77, 0x73, 0x10, 0xb8, + 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x5a, 0x69, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x62, 0x61, 0x73, 0x65, + 0x10, 0xb9, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x54, 0x65, 0x66, 0x74, 0x65, 0x72, 0x10, 0xba, 0x06, + 0x12, 0x0a, 0x0a, 0x05, 0x54, 0x77, 0x69, 0x73, 0x74, 0x10, 0xbb, 0x06, 0x12, 0x16, 0x0a, 0x11, + 0x42, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x72, 0x65, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x10, 0xbc, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x10, 0xbd, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x47, 0x72, 0x61, 0x66, 0x61, + 0x6e, 0x61, 0x10, 0xbe, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, + 0x41, 0x70, 0x69, 0x10, 0xbf, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0x10, 0xc0, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x42, 0x75, 0x6c, + 0x6b, 0x73, 0x6d, 0x73, 0x10, 0xc1, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x62, + 0x6f, 0x78, 0x10, 0xc2, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x4f, 0x6e, 0x65, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x10, 0xc3, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x65, 0x6e, 0x74, 0x6d, 0x61, 0x6e, + 0x10, 0xc4, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x73, 0x65, 0x75, 0x72, 0x10, 0xc5, + 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x6f, 0x63, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x10, 0xc6, + 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x6d, 0x73, 0x69, 0x74, 0x65, 0x10, 0xc7, 0x06, + 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x74, 0x61, 0x69, 0x6c, 0x6f, 0x72, + 0x10, 0xc8, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x4c, 0x65, 0x6d, 0x6c, 0x69, 0x73, 0x74, 0x10, 0xc9, + 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x64, 0x70, 0x61, 0x64, 0x10, 0xca, 0x06, 0x12, + 0x0e, 0x0a, 0x09, 0x46, 0x6f, 0x72, 0x6d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x10, 0xcb, 0x06, 0x12, + 0x10, 0x0a, 0x0b, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x6c, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x10, 0xcc, + 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x6f, 0x64, 0x65, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x10, 0xcd, + 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x56, 0x62, 0x6f, 0x75, 0x74, 0x10, 0xce, 0x06, 0x12, 0x0e, 0x0a, + 0x09, 0x4e, 0x69, 0x67, 0x68, 0x74, 0x66, 0x61, 0x6c, 0x6c, 0x10, 0xcf, 0x06, 0x12, 0x0f, 0x0a, + 0x0a, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x61, 0x62, 0x73, 0x10, 0xd0, 0x06, 0x12, 0x11, + 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x65, 0x63, 0x68, 0x54, 0x65, 0x78, 0x74, 0x41, 0x49, 0x10, 0xd1, + 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x6f, 0x6c, 0x6c, 0x73, 0x41, 0x50, 0x49, 0x10, 0xd2, 0x06, + 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x46, 0x69, 0x6e, 0x10, 0xd3, 0x06, 0x12, 0x0a, 0x0a, + 0x05, 0x53, 0x63, 0x61, 0x6c, 0x72, 0x10, 0xd4, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x4b, 0x61, 0x6e, + 0x62, 0x61, 0x6e, 0x74, 0x6f, 0x6f, 0x6c, 0x10, 0xd5, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x10, 0xd6, 0x06, 0x12, 0x0c, 0x0a, 0x07, + 0x48, 0x6f, 0x74, 0x77, 0x69, 0x72, 0x65, 0x10, 0xd7, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x62, 0x6f, 0x74, 0x10, 0xd8, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x69, 0x6d, + 0x65, 0x6b, 0x69, 0x74, 0x10, 0xd9, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x10, 0xda, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x6f, 0x6a, + 0x6f, 0x68, 0x65, 0x6c, 0x70, 0x64, 0x65, 0x73, 0x6b, 0x10, 0xdb, 0x06, 0x12, 0x0f, 0x0a, 0x0a, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x65, 0x6e, 0x64, 0x10, 0xdc, 0x06, 0x12, 0x10, 0x0a, + 0x0b, 0x47, 0x65, 0x74, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x10, 0xdd, 0x06, 0x12, + 0x0c, 0x0a, 0x07, 0x44, 0x79, 0x6e, 0x61, 0x64, 0x6f, 0x74, 0x10, 0xde, 0x06, 0x12, 0x0a, 0x0a, + 0x05, 0x44, 0x65, 0x6d, 0x69, 0x6f, 0x10, 0xdf, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x54, 0x6f, 0x6b, + 0x65, 0x65, 0x74, 0x10, 0xe0, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x4d, 0x79, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x10, 0xe1, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x6f, 0x70, + 0x79, 0x73, 0x63, 0x61, 0x70, 0x65, 0x10, 0xe2, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x42, 0x65, 0x73, + 0x6e, 0x61, 0x70, 0x70, 0x79, 0x10, 0xe3, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x61, 0x6c, 0x65, + 0x73, 0x6d, 0x61, 0x74, 0x65, 0x10, 0xe4, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x74, + 0x6d, 0x61, 0x70, 0x61, 0x70, 0x69, 0x10, 0xe5, 0x06, 0x12, 0x11, 0x0a, 0x0c, 0x57, 0x65, 0x62, + 0x73, 0x69, 0x74, 0x65, 0x70, 0x75, 0x6c, 0x73, 0x65, 0x10, 0xe6, 0x06, 0x12, 0x0e, 0x0a, 0x09, + 0x55, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x79, 0x10, 0xe7, 0x06, 0x12, 0x0c, 0x0a, 0x07, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x10, 0xe8, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x44, + 0x46, 0x6d, 0x79, 0x55, 0x52, 0x4c, 0x10, 0xe9, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x70, 0x69, + 0x32, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x10, 0xea, 0x06, 0x12, 0x0d, 0x0a, 0x08, 0x4f, + 0x70, 0x73, 0x67, 0x65, 0x6e, 0x69, 0x65, 0x10, 0xeb, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x47, 0x65, + 0x6d, 0x69, 0x6e, 0x69, 0x10, 0xec, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x48, 0x6f, 0x6e, 0x65, 0x79, + 0x63, 0x6f, 0x6d, 0x62, 0x10, 0xed, 0x06, 0x12, 0x14, 0x0a, 0x0f, 0x4b, 0x61, 0x6c, 0x74, 0x75, + 0x72, 0x61, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xee, 0x06, 0x12, 0x13, 0x0a, + 0x0e, 0x4b, 0x61, 0x6c, 0x74, 0x75, 0x72, 0x61, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x10, + 0xef, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x42, 0x69, 0x74, 0x47, 0x6f, 0x10, 0xf0, 0x06, 0x12, 0x0d, + 0x0a, 0x08, 0x4f, 0x70, 0x74, 0x69, 0x64, 0x61, 0x73, 0x68, 0x10, 0xf1, 0x06, 0x12, 0x0a, 0x0a, + 0x05, 0x49, 0x6d, 0x67, 0x69, 0x78, 0x10, 0xf2, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x6d, 0x61, + 0x67, 0x65, 0x54, 0x6f, 0x54, 0x65, 0x78, 0x74, 0x10, 0xf3, 0x06, 0x12, 0x10, 0x0a, 0x0b, 0x50, + 0x61, 0x67, 0x65, 0x32, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x10, 0xf4, 0x06, 0x12, 0x0e, 0x0a, + 0x09, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x62, 0x61, 0x73, 0x65, 0x10, 0xf5, 0x06, 0x12, 0x0d, 0x0a, + 0x08, 0x52, 0x65, 0x64, 0x62, 0x6f, 0x6f, 0x74, 0x68, 0x10, 0xf6, 0x06, 0x12, 0x0b, 0x0a, 0x06, + 0x4e, 0x75, 0x62, 0x65, 0x6c, 0x61, 0x10, 0xf7, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x6e, 0x66, + 0x6f, 0x62, 0x69, 0x70, 0x10, 0xf8, 0x06, 0x12, 0x0a, 0x0a, 0x05, 0x55, 0x70, 0x72, 0x6f, 0x63, + 0x10, 0xf9, 0x06, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x62, 0x65, + 0x65, 0x10, 0xfa, 0x06, 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x66, 0x74, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 0x10, 0xfb, 0x06, 0x12, 0x0c, 0x0a, 0x07, 0x45, 0x64, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x10, + 0xfc, 0x06, 0x12, 0x0b, 0x0a, 0x06, 0x54, 0x65, 0x61, 0x6d, 0x75, 0x70, 0x10, 0xfd, 0x06, 0x12, + 0x0c, 0x0a, 0x07, 0x57, 0x6f, 0x72, 0x6b, 0x64, 0x61, 0x79, 0x10, 0xfe, 0x06, 0x12, 0x0c, 0x0a, + 0x07, 0x4d, 0x6f, 0x6e, 0x67, 0x6f, 0x44, 0x42, 0x10, 0xff, 0x06, 0x12, 0x08, 0x0a, 0x03, 0x4e, + 0x47, 0x43, 0x10, 0x80, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x56, 0x32, 0x10, 0x81, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x51, + 0x4c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x10, 0x82, 0x07, 0x12, 0x08, 0x0a, 0x03, 0x46, 0x54, + 0x50, 0x10, 0x83, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x52, 0x65, 0x64, 0x69, 0x73, 0x10, 0x84, 0x07, + 0x12, 0x09, 0x0a, 0x04, 0x4c, 0x44, 0x41, 0x50, 0x10, 0x85, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x53, + 0x68, 0x6f, 0x70, 0x69, 0x66, 0x79, 0x10, 0x86, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x52, 0x61, 0x62, + 0x62, 0x69, 0x74, 0x4d, 0x51, 0x10, 0x87, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x88, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x45, 0x74, + 0x68, 0x65, 0x72, 0x73, 0x63, 0x61, 0x6e, 0x10, 0x89, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x49, 0x6e, + 0x66, 0x75, 0x72, 0x61, 0x10, 0x8a, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x41, 0x6c, 0x63, 0x68, 0x65, + 0x6d, 0x79, 0x10, 0x8b, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x10, 0x8c, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4d, 0x6f, 0x72, 0x61, 0x6c, + 0x69, 0x73, 0x10, 0x8d, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x42, 0x73, 0x63, 0x53, 0x63, 0x61, 0x6e, + 0x10, 0x8e, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x43, 0x6f, 0x69, 0x6e, 0x4d, 0x61, 0x72, 0x6b, 0x65, + 0x74, 0x43, 0x61, 0x70, 0x10, 0x8f, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x50, 0x65, 0x72, 0x63, 0x79, + 0x10, 0x90, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x69, 0x6e, 0x65, 0x73, 0x57, 0x65, 0x62, 0x68, + 0x6f, 0x6f, 0x6b, 0x10, 0x91, 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x50, 0x75, 0x6c, 0x75, 0x6d, 0x69, + 0x10, 0x92, 0x07, 0x12, 0x12, 0x0a, 0x0d, 0x53, 0x75, 0x70, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0x93, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x4e, 0x75, 0x47, 0x65, 0x74, + 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0x94, 0x07, 0x12, 0x0a, 0x0a, 0x05, 0x41, 0x69, 0x76, + 0x65, 0x6e, 0x10, 0x95, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x66, 0x65, 0x63, 0x74, + 0x10, 0x96, 0x07, 0x12, 0x0d, 0x0a, 0x08, 0x44, 0x6f, 0x63, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x10, + 0x97, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x43, 0x6f, 0x75, 0x63, 0x68, 0x62, 0x61, 0x73, 0x65, 0x10, + 0x98, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x68, 0x75, 0x62, 0x10, + 0x99, 0x07, 0x12, 0x19, 0x0a, 0x14, 0x54, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, + 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x10, 0x9a, 0x07, 0x12, 0x10, 0x0a, + 0x0b, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x10, 0x9b, 0x07, 0x12, + 0x11, 0x0a, 0x0c, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x10, + 0x9c, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x61, 0x6c, 0x65, 0x73, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x10, 0x9d, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x48, 0x75, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x46, 0x61, + 0x63, 0x65, 0x10, 0x9e, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x53, 0x6e, 0x6f, 0x77, 0x66, 0x6c, 0x61, + 0x6b, 0x65, 0x10, 0x9f, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, + 0x72, 0x61, 0x70, 0x68, 0x10, 0xa0, 0x07, 0x12, 0x0e, 0x0a, 0x09, 0x54, 0x61, 0x69, 0x6c, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x10, 0xa1, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x57, 0x65, 0x62, 0x33, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x10, 0xa2, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x41, 0x7a, 0x75, + 0x72, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x10, 0xa3, 0x07, 0x12, 0x12, 0x0a, 0x0d, + 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x62, 0x10, 0xa4, 0x07, + 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x6e, 0x74, 0x68, 0x72, 0x6f, 0x70, 0x69, 0x63, 0x10, 0xa5, 0x07, + 0x12, 0x09, 0x0a, 0x04, 0x52, 0x61, 0x6d, 0x70, 0x10, 0xa6, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x4b, + 0x6c, 0x61, 0x76, 0x69, 0x79, 0x6f, 0x10, 0xa7, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x43, 0x6f, 0x64, 0x79, 0x10, 0xa8, 0x07, 0x12, + 0x0e, 0x0a, 0x09, 0x56, 0x6f, 0x69, 0x63, 0x65, 0x66, 0x6c, 0x6f, 0x77, 0x10, 0xa9, 0x07, 0x12, + 0x0c, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x10, 0xaa, 0x07, 0x12, 0x0b, 0x0a, + 0x06, 0x49, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x10, 0xab, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x49, 0x70, + 0x32, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xac, 0x07, 0x12, 0x0e, 0x0a, 0x09, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6d, 0x6f, 0x6a, 0x6f, 0x10, 0xad, 0x07, 0x12, 0x0e, 0x0a, 0x09, + 0x50, 0x6f, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x10, 0xae, 0x07, 0x12, 0x13, 0x0a, 0x0e, + 0x50, 0x6f, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xaf, + 0x07, 0x12, 0x0b, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x67, 0x6c, 0x79, 0x10, 0xb0, 0x07, 0x12, 0x0c, + 0x0a, 0x07, 0x4f, 0x70, 0x65, 0x6e, 0x56, 0x70, 0x6e, 0x10, 0xb1, 0x07, 0x12, 0x1e, 0x0a, 0x19, + 0x56, 0x61, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x65, 0x72, 0x73, + 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x10, 0xb2, 0x07, 0x12, 0x10, 0x0a, 0x0b, + 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, 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 0d4f75a58668..b9017b2f1b83 100644 --- a/proto/detectors.proto +++ b/proto/detectors.proto @@ -634,7 +634,7 @@ enum DetectorType { NeutrinoApi = 623; Storecove = 624; Shipday = 625; - Sentiment = 626; + Sentiment = 626 [deprecated = true]; StreamChatMessaging = 627; TeamworkCRM = 628; TeamworkDesk = 629; From 4106ce7bf05c9ac8b118f902ebf9c4505003b808 Mon Sep 17 00:00:00 2001 From: dylanTruffle <52866392+dylanTruffle@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:17:01 -0700 Subject: [PATCH 11/14] Detector-Competition-Feat: Adding Azure Container Registry Password Detector (#1958) * implementing azure container registry password detector * Fixing boundry feedback * whoops * update verification code * fix regex --------- Co-authored-by: Chair Co-authored-by: Zachary Rice --- .../azurecontainerregistry.go | 101 ++++++++++++++ .../azurecontainerregistry_test.go | 129 ++++++++++++++++++ pkg/engine/defaults.go | 2 + pkg/pb/detectorspb/detectors.pb.go | 19 ++- proto/detectors.proto | 1 + 5 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 pkg/detectors/azurecontainerregistry/azurecontainerregistry.go create mode 100644 pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go diff --git a/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go b/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go new file mode 100644 index 000000000000..6698c338c2ea --- /dev/null +++ b/pkg/detectors/azurecontainerregistry/azurecontainerregistry.go @@ -0,0 +1,101 @@ +package azurecontainerregistry + +import ( + "context" + "encoding/base64" + "fmt" + "net/http" + "regexp" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "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 ( + defaultClient = common.SaneHttpClient() + // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. + url = regexp.MustCompile(`([a-zA-Z0-9-]{1,100})\.azurecr\.io`) + password = regexp.MustCompile(`\b[A-Za-z0-9+/=]{52}\b`) +) + +// 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{".azurecr.io"} +} + +// FromData will find and optionally verify Azurecontainerregistry 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) + + urlMatches := url.FindAllStringSubmatch(dataStr, -1) + passwordMatches := password.FindAllStringSubmatch(dataStr, -1) + + for _, urlMatch := range urlMatches { + for _, passwordMatch := range passwordMatches { + + endpoint := urlMatch[0] + username := urlMatch[1] + password := passwordMatch[0] + + s1 := detectors.Result{ + DetectorType: detectorspb.DetectorType_AzureContainerRegistry, + Raw: []byte(endpoint), + Redacted: endpoint, + RawV2: []byte(endpoint + password), + } + + if verify { + client := s.client + if client == nil { + client = defaultClient + } + + auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) + url := fmt.Sprintf("https://%s/v2/", endpoint) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + continue + } + + req.Header.Set("Authorization", fmt.Sprintf("Basic %s", auth)) + res, err := client.Do(req) + if err == nil { + defer res.Body.Close() + if res.StatusCode >= 200 && res.StatusCode < 300 { + s1.Verified = true + } else if res.StatusCode == 401 { + // The secret is determinately not verified (nothing to do) + } else { + s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) + } + } else { + s1.VerificationError = err + } + } + + if !s1.Verified && detectors.IsKnownFalsePositive(password, detectors.DefaultFalsePositives, true) { + continue + } + + results = append(results, s1) + if s1.Verified { + break + } + } + } + + return results, nil +} + +func (s Scanner) Type() detectorspb.DetectorType { + return detectorspb.DetectorType_AzureContainerRegistry +} diff --git a/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go b/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go new file mode 100644 index 000000000000..ede6611f771e --- /dev/null +++ b/pkg/detectors/azurecontainerregistry/azurecontainerregistry_test.go @@ -0,0 +1,129 @@ +//go:build detectors +// +build detectors + +package azurecontainerregistry + +import ( + "context" + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "testing" + "time" + + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" +) + +func TestAzureContainerRegistry_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) + } + azureHost := testSecrets.MustGetField("AZURE_CR_HOST") + password := testSecrets.MustGetField("AZURE_CR_PASSWORD") + passwordInactive := testSecrets.MustGetField("AZURE_CR_PASSWORD_INACTIVE") + + + 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 azurecontainerregistry secret %s and %s within", azureHost, password)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_AzureContainerRegistry, + Verified: true, + }, + }, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "found, unverified", + s: Scanner{}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a azurecontainerregistry secret %s and %s within but not valid", azureHost, passwordInactive)), // the secret would satisfy the regex but not pass validation + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_AzureContainerRegistry, + 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, + }, + } + 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("AzureContainerRegistry.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{}, "RawV2", "Raw","Redacted", "VerificationError") + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { + t.Errorf("AzureContainerRegistry.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/engine/defaults.go b/pkg/engine/defaults.go index f7f43396b042..78c7f4ec2d33 100644 --- a/pkg/engine/defaults.go +++ b/pkg/engine/defaults.go @@ -63,6 +63,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/ayrshare" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azure" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azurebatch" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azurecontainerregistry" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/bannerbear" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/baremetrics" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/beamer" @@ -781,6 +782,7 @@ func DefaultDetectors() []detectors.Detector { &alibaba.Scanner{}, aws.New(), &azure.Scanner{}, + &azurecontainerregistry.Scanner{}, &azurebatch.Scanner{}, &slack.Scanner{}, // has 4 secret types &gitlab.Scanner{}, diff --git a/pkg/pb/detectorspb/detectors.pb.go b/pkg/pb/detectorspb/detectors.pb.go index b570f7163a1c..25ba94cebb53 100644 --- a/pkg/pb/detectorspb/detectors.pb.go +++ b/pkg/pb/detectorspb/detectors.pb.go @@ -1036,6 +1036,7 @@ const ( DetectorType_Stripo DetectorType = 955 DetectorType_ReplyIO DetectorType = 956 DetectorType_AzureBatch DetectorType = 957 + DetectorType_AzureContainerRegistry DetectorType = 958 ) // Enum value maps for DetectorType. @@ -1995,6 +1996,7 @@ var ( 955: "Stripo", 956: "ReplyIO", 957: "AzureBatch", + 958: "AzureContainerRegistry", } DetectorType_value = map[string]int32{ "Alibaba": 0, @@ -2951,6 +2953,7 @@ var ( "Stripo": 955, "ReplyIO": 956, "AzureBatch": 957, + "AzureContainerRegistry": 958, } ) @@ -3329,7 +3332,7 @@ var file_detectors_proto_rawDesc = []byte{ 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 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, 0x2a, 0xa1, 0x78, 0x0a, 0x0c, 0x44, + 0x09, 0x0a, 0x05, 0x55, 0x54, 0x46, 0x31, 0x36, 0x10, 0x03, 0x2a, 0xbe, 0x78, 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, @@ -4291,12 +4294,14 @@ var file_detectors_proto_rawDesc = []byte{ 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, 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, + 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, 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 b9017b2f1b83..1d9f2e23f3e5 100644 --- a/proto/detectors.proto +++ b/proto/detectors.proto @@ -966,6 +966,7 @@ enum DetectorType { Stripo = 955; ReplyIO = 956; AzureBatch = 957; + AzureContainerRegistry = 958; } message Result { From b6469f23ac79f931387ee1a67670e4be0115331d Mon Sep 17 00:00:00 2001 From: Ankush Goel Date: Thu, 2 Nov 2023 21:54:37 +0530 Subject: [PATCH 12/14] modified regex (#2033) --- pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go b/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go index f3ce2df5c0f8..4ceed898d62f 100644 --- a/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go +++ b/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go @@ -20,7 +20,7 @@ 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{"clickup"}) + `\b(pk_[0-9]{8}_[0-9A-Z]{32})\b`) + keyPat = regexp.MustCompile(`\b(pk_[0-9]{7,8}_[0-9A-Z]{32})\b`) ) // Keywords are used for efficiently pre-filtering chunks. From 965a274de9216c912543ed25eb9c6ff4594eb00a Mon Sep 17 00:00:00 2001 From: Ankush Goel Date: Thu, 2 Nov 2023 21:56:31 +0530 Subject: [PATCH 13/14] Detector-Competition-Fix: fixed regex for databricks domain and fixed tests (#1965) * fixed regex for domain and fixed tests * fixed regex * fixed an issue with regex subgrouping * made recommended changes * made recommended changed * fixed RawV2 --- .../databrickstoken/databrickstoken.go | 34 ++++---- .../databrickstoken/databrickstoken_test.go | 80 ++++++++++++++----- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/pkg/detectors/databrickstoken/databrickstoken.go b/pkg/detectors/databrickstoken/databrickstoken.go index 7ce0cfc99baa..72e48e6b07c6 100644 --- a/pkg/detectors/databrickstoken/databrickstoken.go +++ b/pkg/detectors/databrickstoken/databrickstoken.go @@ -12,17 +12,19 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -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. - domain = regexp.MustCompile(`\b(https:\/\/[a-z0-9-]+\.cloud\.databricks\.com)\b`) - keyPat = regexp.MustCompile(`\b(dapi[a-z0-9]{32})\b`) + domain = regexp.MustCompile(`\b([a-z0-9-]+(?:\.[a-z0-9-]+)*\.(cloud\.databricks\.com|gcp\.databricks\.com|azurewebsites\.net))\b`) + keyPat = regexp.MustCompile(`\b(dapi[0-9a-f]{32})(-\d)?\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -39,15 +41,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result domainMatches := domain.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { - if len(match) != 2 { - continue - } resMatch := strings.TrimSpace(match[1]) for _, domainmatch := range domainMatches { - if len(domainmatch) != 2 { - continue - } resDomainMatch := strings.TrimSpace(domainmatch[1]) s1 := detectors.Result{ @@ -57,7 +53,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } if verify { - req, err := http.NewRequestWithContext(ctx, "GET", resDomainMatch + "/api/2.0/clusters/list", nil) + client := s.client + if client == nil { + client = defaultClient + } + req, err := http.NewRequestWithContext(ctx, "GET", "https://" + resDomainMatch + "/api/2.0/clusters/list", nil) if err != nil { continue } @@ -67,14 +67,18 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true + } else if res.StatusCode == 403 { + // nothing to do here } else { - // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. - if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { - continue - } + s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } + } else { + s1.VerificationError = err } } + if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { + continue + } results = append(results, s1) } diff --git a/pkg/detectors/databrickstoken/databrickstoken_test.go b/pkg/detectors/databrickstoken/databrickstoken_test.go index c25f21c8921c..92911f9d0e3e 100644 --- a/pkg/detectors/databrickstoken/databrickstoken_test.go +++ b/pkg/detectors/databrickstoken/databrickstoken_test.go @@ -6,17 +6,18 @@ package databrickstoken import ( "context" "fmt" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" - "github.com/kylelemons/godebug/pretty" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" - "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) -func TestDatabrickstoken_FromChunk(t *testing.T) { +func TestDatabricksToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") @@ -25,6 +26,7 @@ func TestDatabrickstoken_FromChunk(t *testing.T) { } secret := testSecrets.MustGetField("DATABRICKSTOKEN") inactiveSecret := testSecrets.MustGetField("DATABRICKSTOKEN_INACTIVE") + domain := testSecrets.MustGetField("DATABRICKSTOKEN_DOMAIN") type args struct { ctx context.Context @@ -32,18 +34,19 @@ func TestDatabrickstoken_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 databrickstoken secret %s within", secret)), + data: []byte(fmt.Sprintf("You can find a databrickstoken secret %s within %s", secret, domain)), verify: true, }, want: []detectors.Result{ @@ -52,14 +55,15 @@ func TestDatabrickstoken_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 databrickstoken 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 databrickstoken secret %s within %s but not valid", inactiveSecret, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ @@ -68,7 +72,8 @@ func TestDatabrickstoken_FromChunk(t *testing.T) { Verified: false, }, }, - wantErr: false, + wantErr: false, + wantVerificationErr: false, }, { name: "not found", @@ -78,14 +83,48 @@ func TestDatabrickstoken_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 databrickstoken secret %s within %s", secret, domain)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_DatabricksToken, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + { + name: "found, verified but unexpected api surface", + s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a databrickstoken secret %s within %s", secret, domain)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detectorspb.DetectorType_DatabricksToken, + 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("Databrickstoken.FromData() error = %v, wantErr %v", err, tt.wantErr) return @@ -94,10 +133,13 @@ func TestDatabrickstoken_FromChunk(t *testing.T) { if len(got[i].Raw) == 0 { t.Fatalf("no raw secret present: \n %+v", got[i]) } - got[i].Raw = nil + if (got[i].VerificationError != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) + } } - if diff := pretty.Compare(got, tt.want); diff != "" { - t.Errorf("Databrickstoken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { + t.Errorf("DatabricksToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } From ab896890b407fd40c83423a668b7313dd00da0cf Mon Sep 17 00:00:00 2001 From: Ankush Goel Date: Fri, 3 Nov 2023 00:50:26 +0530 Subject: [PATCH 14/14] fixed helpscout detector regex and verifier (#2056) --- pkg/detectors/helpscout/helpscout.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/detectors/helpscout/helpscout.go b/pkg/detectors/helpscout/helpscout.go index becc8caa9683..ed139de33597 100644 --- a/pkg/detectors/helpscout/helpscout.go +++ b/pkg/detectors/helpscout/helpscout.go @@ -2,7 +2,6 @@ package helpscout import ( "context" - "fmt" "net/http" "regexp" "strings" @@ -21,7 +20,7 @@ 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{"helpscout"}) + `\b([A-Za-z0-9]{56})\b`) + keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"helpscout"}) + `\b([a-z0-9]{40})\b`) ) // Keywords are used for efficiently pre-filtering chunks. @@ -52,7 +51,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result if err != nil { continue } - req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) + req.SetBasicAuth(resMatch, "X") res, err := client.Do(req) if err == nil { defer res.Body.Close()