Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions client/delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) {
if err != nil {
return data.TargetFileMeta{}, err
}
verifiers := map[string]verify.DelegationsVerifier{"root": verify.DelegationsVerifier{c.db}}

verifiers := map[string]verify.DelegationsVerifier{
"root": {DB: c.db},
}

// delegationsIterator covers 5.6.7
// - pre-order depth-first search starting with the top targets
Expand All @@ -26,16 +29,19 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) {
if !ok {
return data.TargetFileMeta{}, ErrUnknownTarget{file, snapshot.Version}
}
verifier := verifiers[d.parent]

// covers 5.6.{1,2,3,4,5,6}
verifier := verifiers[d.parent]
target, err := c.loadDelegatedTargets(snapshot, d.child.Name, verifier)
if err != nil {
return data.TargetFileMeta{}, err
}

// stop when the searched TargetFileMeta is found
if m, ok := target.Targets[file]; ok {
return m, nil
}

if target.Delegations != nil {
delegations.add(target.Delegations.Roles, d.child.Name)
targetVerifier, err := verify.NewDelegationsVerifier(target.Delegations)
Expand All @@ -45,6 +51,7 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) {
verifiers[d.child.Name] = targetVerifier
}
}

return data.TargetFileMeta{}, ErrMaxDelegations{
File: file,
MaxDelegations: c.MaxDelegations,
Expand All @@ -56,10 +63,12 @@ func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) {
if err := c.getLocalMeta(); err != nil {
return nil, err
}

rawS, ok := c.localMeta["snapshot.json"]
if !ok {
return nil, ErrNoLocalSnapshot
}

snapshot := &data.Snapshot{}
if err := c.db.Unmarshal(rawS, snapshot, "snapshot", c.snapshotVer); err != nil {
return nil, ErrDecodeFailed{"snapshot.json", err}
Expand All @@ -75,6 +84,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri
if !ok {
return nil, ErrRoleNotInSnapshot{role, snapshot.Version}
}

// 5.6.1 download target if not in the local store
// 5.6.2 check against snapshot hash
raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta)
Expand All @@ -84,6 +94,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri
return nil, err
}
}

target := &data.Targets{}
// 5.6.3 verify signature with parent public keys
// 5.6.5 verify that the targets is not expired
Expand Down Expand Up @@ -114,15 +125,17 @@ func (c *Client) rootTargetDelegation() data.DelegatedRole {
if r == nil {
return data.DelegatedRole{}
}

keyIDs := make([]string, 0, len(r.KeyIDs))
for id, _ := range r.KeyIDs {
keyIDs = append(keyIDs, id)
}

return data.DelegatedRole{
Name: role,
KeyIDs: keyIDs,
Threshold: r.Threshold,
PathMatchers: []string{"*"},
Name: role,
KeyIDs: keyIDs,
Threshold: r.Threshold,
Paths: []string{"*"},
}
}

Expand All @@ -148,7 +161,9 @@ func newDelegationsIterator(role data.DelegatedRole, parent string, file string)
stack: make([]delegation, 0, 1),
visited: make(map[delegationID]struct{}),
}

i.add([]data.DelegatedRole{role}, parent)

return i
}

Expand Down
164 changes: 94 additions & 70 deletions client/delegations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
)

var (
defaultPathMatchers = []string{"tmp", "*"}
notMatchingPathMatchers = []string{"vars", "null"}
defaultPathPatterns = []string{"tmp", "*"}
noMatchPathPatterns = []string{"vars", "null"}
)

func TestDelegationsIterator(t *testing.T) {
Expand All @@ -30,44 +30,68 @@ func TestDelegationsIterator(t *testing.T) {
resultOrder []string
}{
{
"no termination",
map[string][]data.DelegatedRole{
"a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}},
"b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}},
testName: "no termination",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "b", Paths: defaultPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
},
},
data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers},
"",
[]string{"a", "b", "c", "d", "e"},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "c", "d", "e"},
},
{
"terminated in b",
map[string][]data.DelegatedRole{
"a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}},
"b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}},
testName: "terminated in b",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "b", Paths: defaultPathPatterns, Terminating: true},
{Name: "e", Paths: defaultPathPatterns},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
},
},
data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers},
"",
[]string{"a", "b", "c", "d"},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "c", "d"},
},
{
"path does not match b",
map[string][]data.DelegatedRole{
"a": []data.DelegatedRole{{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}},
"b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}},
testName: "path does not match b",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "b", Paths: noMatchPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
},
},
data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers},
"",
[]string{"a", "e"},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "e"},
},
{
"cycle avoided",
map[string][]data.DelegatedRole{
"a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}},
"b": []data.DelegatedRole{{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}},
testName: "cycle avoided",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "b", Paths: defaultPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
},
"b": {
{Name: "a", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
},
},
data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers},
"",
[]string{"a", "b", "a", "e", "d"},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "a", "e", "d"},
},
}

Expand All @@ -87,9 +111,9 @@ func TestDelegationsIterator(t *testing.T) {
}
d.add(delegations, r.child.Name)
}
assert.Equal(t, len(iterationOrder), len(tt.resultOrder))
assert.Equal(t, len(tt.resultOrder), len(iterationOrder))
for i, role := range iterationOrder {
assert.Equal(t, role, tt.resultOrder[i])
assert.Equal(t, tt.resultOrder[i], role)
}
})
}
Expand All @@ -104,7 +128,7 @@ func TestGetTargetMeta(t *testing.T) {

f, err := c.getTargetFileMeta("f.txt")
assert.Nil(t, err)
assert.Equal(t, f.Length, int64(15))
assert.Equal(t, int64(15), f.Length)
}

func TestMaxDelegations(t *testing.T) {
Expand All @@ -115,7 +139,7 @@ func TestMaxDelegations(t *testing.T) {
assert.Nil(t, err)
c.MaxDelegations = 2
_, err = c.getTargetFileMeta("c.txt")
assert.Equal(t, err, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2})
assert.Equal(t, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}, err)
}

func TestMetaNotFound(t *testing.T) {
Expand All @@ -125,7 +149,7 @@ func TestMetaNotFound(t *testing.T) {
_, err := c.Update()
assert.Nil(t, err)
_, err = c.getTargetFileMeta("unknown.txt")
assert.Equal(t, err, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2})
assert.Equal(t, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}, err)
}

type fakeRemote struct {
Expand Down Expand Up @@ -209,69 +233,69 @@ func TestPersistedMeta(t *testing.T) {
fileContent string
}{
{
"unknown",
[]expectedTargets{
file: "unknown",
targets: []expectedTargets{
{
"targets.json",
2,
name: "targets.json",
version: 2,
},
},
ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2},
"",
downloadError: ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2},
fileContent: "",
},
{
"b.txt",
[]expectedTargets{
file: "b.txt",
targets: []expectedTargets{
{
"targets.json",
2,
name: "targets.json",
version: 2,
},
{
"a.json",
1,
name: "a.json",
version: 1,
},
{
"b.json",
1,
name: "b.json",
version: 1,
},
},
nil,
"Contents: b.txt",
downloadError: nil,
fileContent: "Contents: b.txt",
},
{
"f.txt",
[]expectedTargets{
file: "f.txt",
targets: []expectedTargets{
{
"targets.json",
2,
name: "targets.json",
version: 2,
},
{
"a.json",
1,
name: "a.json",
version: 1,
},
{
"b.json",
1,
name: "b.json",
version: 1,
},
{
"c.json",
1,
name: "c.json",
version: 1,
},
{
"d.json",
1,
name: "d.json",
version: 1,
},
{
"e.json",
1,
name: "e.json",
version: 1,
},
{
"f.json",
1,
name: "f.json",
version: 1,
},
},
nil,
"Contents: f.txt",
downloadError: nil,
fileContent: "Contents: f.txt",
},
}

Expand All @@ -291,7 +315,7 @@ func TestPersistedMeta(t *testing.T) {
}
for _, targets := range tt.targets {
storedVersion, err := versionOfStoredTargets(targets.name, persisted)
assert.Equal(t, storedVersion, targets.version)
assert.Equal(t, targets.version, storedVersion)
assert.Nil(t, err)
delete(persisted, targets.name)
}
Expand Down
Loading