Skip to content

Commit

Permalink
[Feature] Update FeedRules logic to factor in TeamID and `SigningID…
Browse files Browse the repository at this point in the history
…` rules + validation checks (#48)

* update feedrules creation

* create global rules and feed rules validation

* add global and feed rule validation

* add primary key type testing
  • Loading branch information
radsec authored Apr 4, 2024
1 parent 7c425c1 commit 8ecc610
Show file tree
Hide file tree
Showing 9 changed files with 782 additions and 32 deletions.
17 changes: 15 additions & 2 deletions pkg/model/feedrules/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "" {
Expand All @@ -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,
Expand All @@ -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 {
Expand Down
39 changes: 39 additions & 0 deletions pkg/model/feedrules/validation.go
Original file line number Diff line number Diff line change
@@ -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
}
237 changes: 237 additions & 0 deletions pkg/model/feedrules/validation_test.go
Original file line number Diff line number Diff line change
@@ -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
}
})
}
}
40 changes: 10 additions & 30 deletions pkg/model/globalrules/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)
Expand All @@ -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
}
Loading

0 comments on commit 8ecc610

Please sign in to comment.