diff --git a/pkg/model/feedrules/create.go b/pkg/model/feedrules/create.go index 3480d88..982485b 100644 --- a/pkg/model/feedrules/create.go +++ b/pkg/model/feedrules/create.go @@ -6,7 +6,10 @@ import ( "github.com/airbnb/rudolph/pkg/model/rules" ) -func ConstructFeedRuleFromBaseRule(timeProvider clock.TimeProvider, rule rules.SantaRule) FeedRuleRow { +func ConstructFeedRuleFromBaseRule( + timeProvider clock.TimeProvider, + rule rules.SantaRule, +) *FeedRuleRow { var identifier string // Support backwards compatibility with legacy SHA256 identifier if rule.SHA256 != "" && rule.Identifier == "" { @@ -15,7 +18,10 @@ func ConstructFeedRuleFromBaseRule(timeProvider clock.TimeProvider, rule rules.S identifier = rule.Identifier } - return FeedRuleRow{ + // Morph the identifier back into the rule to start a slow migration + rule.Identifier = identifier + + feedRuleRow := &FeedRuleRow{ PrimaryKey: dynamodb.PrimaryKey{ PartitionKey: feedRulesPK, // With this sort key, all rules will be ordered by the date they are created, @@ -27,6 +33,13 @@ func ConstructFeedRuleFromBaseRule(timeProvider clock.TimeProvider, rule rules.S ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), DataType: GetDataType(), } + + isValid, _ := feedRuleRow.feedRuleRowValidation() + if !isValid { + return nil + } + + return feedRuleRow } func ReconstructFeedSyncLastEvaluatedKeyFromDate(feedSyncCursor string) dynamodb.PrimaryKey { diff --git a/pkg/model/feedrules/validation.go b/pkg/model/feedrules/validation.go new file mode 100644 index 0000000..209a183 --- /dev/null +++ b/pkg/model/feedrules/validation.go @@ -0,0 +1,39 @@ +package feedrules + +import ( + "github.com/airbnb/rudolph/pkg/model/rules" + "github.com/airbnb/rudolph/pkg/types" +) + +func (f *FeedRuleRow) feedRuleRowValidation() (bool, error) { + // RuleType validation + _, err := f.RuleType.MarshalText() + if err != nil { + return false, err + } + + // RulePolicy validation + _, err = f.Policy.MarshalText() + if err != nil { + return false, err + } + + var validRuleIdentifier bool + switch f.RuleType { + case types.RuleTypeBinary: + fallthrough + case types.RuleTypeCertificate: + validRuleIdentifier = rules.ValidSha256(f.Identifier) + case types.RuleTypeTeamID: + validRuleIdentifier = rules.ValidTeamID(f.Identifier) + case types.RuleTypeSigningID: + validRuleIdentifier = rules.ValidSigningID(f.Identifier) + } + + if !validRuleIdentifier { + return false, nil + } + + // All validations have passed + return true, nil +} diff --git a/pkg/model/feedrules/validation_test.go b/pkg/model/feedrules/validation_test.go new file mode 100644 index 0000000..3f7e733 --- /dev/null +++ b/pkg/model/feedrules/validation_test.go @@ -0,0 +1,237 @@ +package feedrules + +import ( + "testing" + "time" + + "github.com/airbnb/rudolph/pkg/clock" + "github.com/airbnb/rudolph/pkg/dynamodb" + "github.com/airbnb/rudolph/pkg/model/rules" + "github.com/airbnb/rudolph/pkg/types" +) + +func Test_globalRuleValidation(t *testing.T) { + timeProvider := clock.FrozenTimeProvider{ + Current: time.Now(), + } + type test struct { + name string + feedRuleRow *FeedRuleRow + isValid bool + expectError bool + } + tests := []test{ + { + name: "Binary#4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + types.RuleTypeBinary, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeBinary, + Policy: types.RulePolicyAllowlist, + Identifier: "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: true, + expectError: false, + }, + { + name: "Certificate#1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + types.RuleTypeCertificate, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeCertificate, + Policy: types.RulePolicyAllowlist, + Identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: true, + expectError: false, + }, + { + name: "Certificate#1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + types.RuleTypeCertificate, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeCertificate, + Policy: types.RulePolicyAllowlist, + Identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: false, + expectError: false, + }, + { + name: "TeamID#EQHXZ8M8AV", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "EQHXZ8M8AV", + types.RuleTypeTeamID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeTeamID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AV", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: true, + expectError: false, + }, + { + name: "TeamID#EQHXZ8M8AVAAAAA", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "EQHXZ8M8AVAAAAA", + types.RuleTypeTeamID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeTeamID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AVAAAAA", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: false, + expectError: false, + }, + { + name: "SigningID#EQHXZ8M8AV:com.google.Chrome", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "EQHXZ8M8AV:com.google.Chrome", + types.RuleTypeSigningID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AV:com.google.Chrome", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: true, + expectError: false, + }, + { + name: "SigningID#EQHXZ8M8AVAAAAA:com.google.Chrome", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "EQHXZ8M8AVAAAAA:com.google.Chrome", + types.RuleTypeSigningID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AVAAAAA:com.google.Chrome", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: false, + expectError: false, + }, + { + name: "SigningID#platform:com.apple.curl", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + "platform:com.apple.curl", + types.RuleTypeSigningID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "platform:com.apple.curl", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: true, + expectError: false, + }, + { + name: "SigningID#:com.apple.curl", + feedRuleRow: &FeedRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: feedRulesPK, + SortKey: feedRulesSK( + timeProvider, + ":com.apple.curl", + types.RuleTypeSigningID, + ), + }, + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: ":com.apple.curl", + }, + ExpiresAfter: GetSyncStateExpiresAfter(timeProvider), + DataType: GetDataType(), + }, + isValid: false, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.feedRuleRow.feedRuleRowValidation() + if (err != nil) != tt.expectError { + t.Errorf("feedRuleRowValidation() error = %v, wantErr %v", err, tt.expectError) + return + } + if got != tt.isValid { + t.Errorf("feedRuleRowValidation() got = %v, want %v", got, tt.isValid) + return + } + }) + } +} diff --git a/pkg/model/globalrules/add.go b/pkg/model/globalrules/add.go index 42c9c14..05274b2 100644 --- a/pkg/model/globalrules/add.go +++ b/pkg/model/globalrules/add.go @@ -19,16 +19,7 @@ func AddNewGlobalRule( policy types.Policy, description string, ) error { - // Input Validation - isValid, err := ruleValidation(ruleType, policy) - if err != nil { - return err - } - if !isValid { - return errors.New("no errors occurred during the rule validation check but the provided rule is not valid") - } - - rule := GlobalRuleRow{ + rule := &GlobalRuleRow{ PrimaryKey: dynamodb.PrimaryKey{ PartitionKey: globalRulesPK, SortKey: globalRulesSK(identifier, ruleType), @@ -41,6 +32,15 @@ func AddNewGlobalRule( }, } + // Input Validation + isValid, err := rule.globalRuleValidation() + if err != nil { + return err + } + if !isValid { + return errors.New("no errors occurred during the rule validation check but the provided rule is not valid") + } + feedRule := feedrules.ConstructFeedRuleFromBaseRule(time, rule.SantaRule) putItem1, err := client.CreateTransactPutItem(rule) @@ -60,23 +60,3 @@ func AddNewGlobalRule( _, err = client.TransactWriteItems(putItems, nil) return err } - -func ruleValidation( - ruleType types.RuleType, - policy types.Policy, -) (bool, error) { - // RuleType validation - _, err := ruleType.MarshalText() - if err != nil { - return false, err - } - - // RulePolicy validation - _, err = policy.MarshalText() - if err != nil { - return false, err - } - - // All validations have passed - return true, nil -} diff --git a/pkg/model/globalrules/validation.go b/pkg/model/globalrules/validation.go new file mode 100644 index 0000000..f1a09e2 --- /dev/null +++ b/pkg/model/globalrules/validation.go @@ -0,0 +1,39 @@ +package globalrules + +import ( + "github.com/airbnb/rudolph/pkg/model/rules" + "github.com/airbnb/rudolph/pkg/types" +) + +func (g *GlobalRuleRow) globalRuleValidation() (bool, error) { + // RuleType validation + _, err := g.RuleType.MarshalText() + if err != nil { + return false, err + } + + // RulePolicy validation + _, err = g.Policy.MarshalText() + if err != nil { + return false, err + } + + var validRuleIdentifier bool + switch g.RuleType { + case types.RuleTypeBinary: + fallthrough + case types.RuleTypeCertificate: + validRuleIdentifier = rules.ValidSha256(g.Identifier) + case types.RuleTypeTeamID: + validRuleIdentifier = rules.ValidTeamID(g.Identifier) + case types.RuleTypeSigningID: + validRuleIdentifier = rules.ValidSigningID(g.Identifier) + } + + if !validRuleIdentifier { + return false, nil + } + + // All validations have passed + return true, nil +} diff --git a/pkg/model/globalrules/validation_test.go b/pkg/model/globalrules/validation_test.go new file mode 100644 index 0000000..08b5c1c --- /dev/null +++ b/pkg/model/globalrules/validation_test.go @@ -0,0 +1,214 @@ +package globalrules + +import ( + "testing" + + "github.com/airbnb/rudolph/pkg/dynamodb" + "github.com/airbnb/rudolph/pkg/model/rules" + "github.com/airbnb/rudolph/pkg/types" +) + +func Test_globalRuleValidation(t *testing.T) { + type test struct { + name string + rule *GlobalRuleRow + isValid bool + expectError bool + } + tests := []test{ + { + name: "Binary#4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + types.RuleTypeBinary, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeBinary, + Policy: types.RulePolicyAllowlist, + Identifier: "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + }, + }, + isValid: true, + expectError: false, + }, + { + name: "Certificate#1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + types.RuleTypeCertificate, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeCertificate, + Policy: types.RulePolicyAllowlist, + Identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + }, + }, + isValid: true, + expectError: false, + }, + { + name: "Certificate#1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + types.RuleTypeCertificate, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeCertificate, + Policy: types.RulePolicyAllowlist, + Identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + }, + }, + isValid: false, + expectError: false, + }, + { + name: "TeamID#EQHXZ8M8AV", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "EQHXZ8M8AV", + types.RuleTypeTeamID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeTeamID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AV", + }, + }, + isValid: true, + expectError: false, + }, + { + name: "TeamID#EQHXZ8M8AVAAAAA", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "EQHXZ8M8AVAAAAA", + types.RuleTypeTeamID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeTeamID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AVAAAAA", + }, + }, + isValid: false, + expectError: false, + }, + { + name: "SigningID#EQHXZ8M8AV:com.google.Chrome", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "EQHXZ8M8AV:com.google.Chrome", + types.RuleTypeSigningID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AV:com.google.Chrome", + }, + }, + isValid: true, + expectError: false, + }, + { + name: "SigningID#EQHXZ8M8AVAAAAA:com.google.Chrome", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "EQHXZ8M8AVAAAAA:com.google.Chrome", + types.RuleTypeSigningID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "EQHXZ8M8AVAAAAA:com.google.Chrome", + }, + }, + isValid: false, + expectError: false, + }, + { + name: "SigningID#platform:com.apple.curl", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + "platform:com.apple.curl", + types.RuleTypeSigningID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: "platform:com.apple.curl", + }, + }, + isValid: true, + expectError: false, + }, + { + name: "SigningID#:com.apple.curl", + rule: &GlobalRuleRow{ + PrimaryKey: dynamodb.PrimaryKey{ + PartitionKey: globalRulesPK, + SortKey: globalRulesSK( + ":com.apple.curl", + types.RuleTypeSigningID, + ), + }, + Description: "", + SantaRule: rules.SantaRule{ + RuleType: types.RuleTypeSigningID, + Policy: types.RulePolicyAllowlist, + Identifier: ":com.apple.curl", + }, + }, + isValid: false, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.rule.globalRuleValidation() + if (err != nil) != tt.expectError { + t.Errorf("globalRuleValidation() error = %v, wantErr %v", err, tt.expectError) + return + } + if got != tt.isValid { + t.Errorf("globalRuleValidation() got = %v, want %v", got, tt.isValid) + return + } + }) + } +} diff --git a/pkg/model/rules/primary_key_test.go b/pkg/model/rules/primary_key_test.go new file mode 100644 index 0000000..4c0c1a2 --- /dev/null +++ b/pkg/model/rules/primary_key_test.go @@ -0,0 +1,77 @@ +package rules + +import ( + "testing" + + "github.com/airbnb/rudolph/pkg/types" +) + +func Test_RuleSortKeyFromTypeIdentifier(t *testing.T) { + type test struct { + identifier string + ruleType types.RuleType + sortKey string + } + tests := []test{ + { + identifier: "61977d6006459c4cefe9b988a453589946224957bfc07b262cd7ca1b7a61e04e", + ruleType: types.RuleTypeBinary, + sortKey: RuleSortKeyFromTypeIdentifier( + "61977d6006459c4cefe9b988a453589946224957bfc07b262cd7ca1b7a61e04e", + types.RuleTypeBinary, + ), + }, + { + identifier: "61977d6006459c4cefe9b988a453589946224957bfc07b262cd7ca1b7a61e04e", + ruleType: types.RuleTypeCertificate, + sortKey: RuleSortKeyFromTypeIdentifier( + "61977d6006459c4cefe9b988a453589946224957bfc07b262cd7ca1b7a61e04e", + types.RuleTypeCertificate, + ), + }, + { + identifier: "EQHXZ8M8AV", + ruleType: types.RuleTypeTeamID, + sortKey: RuleSortKeyFromTypeIdentifier( + "EQHXZ8M8AV", + types.RuleTypeTeamID, + ), + }, + { + identifier: "EQHXZ8M8AV:com.google.Chrome", + ruleType: types.RuleTypeSigningID, + sortKey: RuleSortKeyFromTypeIdentifier( + "EQHXZ8M8AV:com.google.Chrome", + types.RuleTypeSigningID, + ), + }, + { + identifier: "EQHXZ8M8AV:com.google.Chrome", + ruleType: 0, + sortKey: "", + }, + } + + for _, tt := range tests { + t.Run(tt.identifier, func(t *testing.T) { + got := RuleSortKeyFromTypeIdentifier( + tt.identifier, + tt.ruleType, + ) + if got != tt.sortKey { + t.Errorf("RuleSortKeyFromTypeIdentifier() got = %v, want %v", got, tt.sortKey) + return + } + if tt.ruleType == types.RuleTypeBinary || tt.ruleType == types.RuleTypeCertificate { + got = RuleSortKeyFromTypeSHA( + tt.identifier, + tt.ruleType, + ) + if got != tt.sortKey { + t.Errorf("RuleSortKeyFromTypeSHA() got = %v, want %v", got, tt.sortKey) + return + } + } + }) + } +} diff --git a/pkg/model/rules/validation.go b/pkg/model/rules/validation.go new file mode 100644 index 0000000..6a00dbc --- /dev/null +++ b/pkg/model/rules/validation.go @@ -0,0 +1,21 @@ +package rules + +import "regexp" + +var sha256Regexp = regexp.MustCompile(`^[a-f0-9]{64}$`) + +var teamIDRegexp = regexp.MustCompile(`^([A-Z0-9]{1,10})$`) + +var signingIDRegexp = regexp.MustCompile(`^([A-Z0-9]{1,10}|platform)(:[\w\-\.]+)$`) + +func ValidSha256(sha256 string) bool { + return sha256Regexp.MatchString(sha256) +} + +func ValidTeamID(teamID string) bool { + return teamIDRegexp.MatchString(teamID) +} + +func ValidSigningID(signingID string) bool { + return signingIDRegexp.MatchString(signingID) +} diff --git a/pkg/model/rules/validation_test.go b/pkg/model/rules/validation_test.go new file mode 100644 index 0000000..cbdd833 --- /dev/null +++ b/pkg/model/rules/validation_test.go @@ -0,0 +1,130 @@ +package rules + +import ( + "testing" +) + +func Test_ValidSha256(t *testing.T) { + type test struct { + name string + identifier string + isValid bool + } + tests := []test{ + { + name: "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + identifier: "4cd1fce53a8b3e67e174859e6672ca29bc1e16585859c53a116e7f53d04350b7", + isValid: true, + }, + { + name: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb1", + isValid: true, + }, + { + name: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + identifier: "1507564a650077bdc8e155b2a4ba8bd85a55dc347a34ddf4e78a836c17d81bb", + isValid: false, + }, + { + name: "588d84953ae992c5de61d3774ce86e710ed42d29", + identifier: "588d84953ae992c5de61d3774ce86e710ed42d29", + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ValidSha256(tt.identifier) + if got != tt.isValid { + t.Errorf("ValidSha256() got = %v, want %v", got, tt.isValid) + return + } + }) + } +} + +func Test_ValidTeamID(t *testing.T) { + type test struct { + name string + identifier string + isValid bool + } + tests := []test{ + { + name: "EQHXZ8M8AV", + identifier: "EQHXZ8M8AV", + isValid: true, + }, + { + name: "APPLE", + identifier: "APPLE", + isValid: true, + }, + { + name: "EQHXZ8M8AVAAAAA", + identifier: "EQHXZ8M8AVAAAAA", + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ValidTeamID(tt.identifier) + if got != tt.isValid { + t.Errorf("ValidTeamID() got = %v, want %v", got, tt.isValid) + return + } + }) + } +} + +func Test_ValidSigningID(t *testing.T) { + type test struct { + name string + identifier string + isValid bool + } + tests := []test{ + { + name: "EQHXZ8M8AV:com.google.Chrome", + identifier: "EQHXZ8M8AV:com.google.Chrome", + isValid: true, + }, + { + name: "EQHXZ8M8AVAAAAA:com.google.Chrome", + identifier: "EQHXZ8M8AVAAAAA:com.google.Chrome", + isValid: false, + }, + { + name: "com.google.Chrome", + identifier: "com.google.Chrome", + isValid: false, + }, + { + name: "platform:com.apple.curl", + identifier: "platform:com.apple.curl", + isValid: true, + }, + { + name: ":com.apple.curl", + identifier: ":com.apple.curl", + isValid: false, + }, + { + name: "APPLE:com.apple.curl", + identifier: "APPLE:com.apple.curl", + isValid: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ValidSigningID(tt.identifier) + if got != tt.isValid { + t.Errorf("ValidSigningID() got = %v, want %v", got, tt.isValid) + return + } + }) + } +}