package targets import ( "testing" "github.com/stretchr/testify/assert" "github.com/theupdateframework/go-tuf/data" "github.com/theupdateframework/go-tuf/verify" ) var ( defaultPathPatterns = []string{"tmp", "*"} noMatchPathPatterns = []string{"vars", "null"} ) func TestDelegationsIterator(t *testing.T) { topTargetsPubKey := &data.PublicKey{ Type: data.KeyTypeEd25519, Scheme: data.KeySchemeEd25519, Algorithms: data.HashAlgorithms, Value: []byte(`{"public":"aaaaec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), } delTargetsPubKey := &data.PublicKey{ Type: data.KeyTypeEd25519, Scheme: data.KeySchemeEd25519, Algorithms: data.HashAlgorithms, Value: []byte(`{"public":"bbbbec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), } defaultKeyIDs := delTargetsPubKey.IDs() var iteratorTests = []struct { testName string roles map[string][]data.DelegatedRole file string resultOrder []string err error }{ { testName: "no delegation", roles: map[string][]data.DelegatedRole{ "targets": {}, }, file: "test.txt", resultOrder: []string{"targets"}, }, { testName: "no termination", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "c": { {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "e": { {Name: "f", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "g", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "g": { {Name: "h", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "i", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "j", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "b", "c", "d", "e", "f", "g", "h", "i", "j"}, }, { testName: "terminated in b", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs, Terminating: true}, {Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "b", "c", "d"}, }, { testName: "path does not match b", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", Paths: noMatchPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "e"}, }, { testName: "path does not match b - path prefixes", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", PathHashPrefixes: []string{"33472a4909"}, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "c", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633"}, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "c": { {Name: "d", PathHashPrefixes: []string{"8baf"}, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "e", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "/e/f/g.txt", resultOrder: []string{"targets", "c", "e"}, }, { testName: "err paths and pathHashPrefixes are set", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", Paths: defaultPathPatterns, PathHashPrefixes: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": {}, }, file: "", resultOrder: []string{"targets"}, err: data.ErrPathsAndPathHashesSet, }, { testName: "cycle avoided 1", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "a": { {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "a", "b", "d", "e"}, }, { testName: "cycle avoided 2", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "a": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "c": { {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "a", "b", "c"}, }, { testName: "diamond delegation", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, {Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "b": { {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "c": { {Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "b", "d", "c"}, }, { testName: "simple cycle", roles: map[string][]data.DelegatedRole{ "targets": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, "a": { {Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs}, }, }, file: "", resultOrder: []string{"targets", "a"}, }, } for _, tt := range iteratorTests { t.Run(tt.testName, func(t *testing.T) { topLevelDB := verify.NewDB() topLevelDB.AddKey(topTargetsPubKey.IDs()[0], topTargetsPubKey) topLevelDB.AddRole("targets", &data.Role{ KeyIDs: topTargetsPubKey.IDs(), Threshold: 1, }) d, err := NewDelegationsIterator(tt.file, topLevelDB) assert.NoError(t, err) var iterationOrder []string for { r, ok := d.Next() if !ok { break } // A delegation should have associated keys. Testing the exact keys // isn't useful in this module since the keys are supplied by the // caller in the arguments to Add(). assert.Greater(t, len(r.Delegatee.KeyIDs), 0) iterationOrder = append(iterationOrder, r.Delegatee.Name) delegations, ok := tt.roles[r.Delegatee.Name] if !ok { continue } db, err := verify.NewDBFromDelegations(&data.Delegations{ Roles: delegations, }) assert.NoError(t, err) err = d.Add(delegations, r.Delegatee.Name, db) assert.Equal(t, tt.err, err) } assert.Equal(t, tt.resultOrder, iterationOrder) }) } } func TestNewDelegationsIteratorError(t *testing.T) { // Empty DB. It is supposed to have at least the top-level targets role and // keys. tldb := verify.NewDB() _, err := NewDelegationsIterator("targets", tldb) assert.ErrorIs(t, err, ErrTopLevelTargetsRoleMissing) }