Skip to content

Commit 4ad7ae4

Browse files
authored
Merge pull request from GHSA-wjw6-2cqr-j4qr
Fix client issue with rolenames as filenames
2 parents 4d8cbc7 + 6773778 commit 4ad7ae4

File tree

12 files changed

+424
-236
lines changed

12 files changed

+424
-236
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d",
5+
"sig": "6550a087bd0f01648f57e02a275f20c8e38974271d73739c446f53a028c4118e070b1d37224bc022ab6e0500c8051494f276365868ed6039ec49c7ecd8b9f602"
6+
}
7+
],
8+
"signed": {
9+
"_type": "targets",
10+
"expires": "2021-10-22T11:21:56Z",
11+
"spec_version": "1.0.19",
12+
"targets": {},
13+
"version": 1
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602",
5+
"sig": "c0266de0724c2ab9c14e679b258033fe3aff8ce3c99419479456170975bb43de9e8539caed437cccc8e6c6068252a921f7badc5384149dab18261a7f157ae406"
6+
}
7+
],
8+
"signed": {
9+
"_type": "targets",
10+
"expires": "2021-10-22T11:21:56Z",
11+
"spec_version": "1.0.19",
12+
"targets": {},
13+
"version": 1
14+
}
15+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6",
5+
"sig": "1d3b9cebfdab388db500d01cb2cd499f016320029df17bf2f1196d8f83f12d041832dc165f23667e537d8a8aa66c716d19835bd2bcd55d4c18bbbd0c6eaf4b06"
6+
}
7+
],
8+
"signed": {
9+
"_type": "root",
10+
"consistent_snapshot": true,
11+
"expires": "2021-10-22T11:21:56Z",
12+
"keys": {
13+
"965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c": {
14+
"keytype": "ed25519",
15+
"keyval": {
16+
"public": "d98dace51d795525971342b9f7317cea0d743710dca932543fedb92bb083c2c0"
17+
},
18+
"scheme": "ed25519"
19+
},
20+
"b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6": {
21+
"keytype": "ed25519",
22+
"keyval": {
23+
"public": "46d386175220afd55ad9b09b6b18fa96cd69e25bc29c97ed7024a522e7e7938c"
24+
},
25+
"scheme": "ed25519"
26+
},
27+
"c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef": {
28+
"keytype": "ed25519",
29+
"keyval": {
30+
"public": "a7beb72fb686a645f5ffd52e246a55d2914411853c70a5b47d837ed7b4c40734"
31+
},
32+
"scheme": "ed25519"
33+
},
34+
"e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095": {
35+
"keytype": "ed25519",
36+
"keyval": {
37+
"public": "efdf10805063c1b7356f40ede43d2c5c6d2d11d79e350887ce96fe5d1e44901a"
38+
},
39+
"scheme": "ed25519"
40+
}
41+
},
42+
"roles": {
43+
"root": {
44+
"keyids": [
45+
"b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6"
46+
],
47+
"threshold": 1
48+
},
49+
"snapshot": {
50+
"keyids": [
51+
"e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095"
52+
],
53+
"threshold": 1
54+
},
55+
"targets": {
56+
"keyids": [
57+
"c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef"
58+
],
59+
"threshold": 1
60+
},
61+
"timestamp": {
62+
"keyids": [
63+
"965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c"
64+
],
65+
"threshold": 1
66+
}
67+
},
68+
"spec_version": "1.0.19",
69+
"version": 1
70+
}
71+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef",
5+
"sig": "ffa055ab5108f9d22f309fecd0160b02971d7a454c8d48db4f99cdaf114b329a401b756a11e42630bff6667ad897fb05f501e3299d25fe786d12651cb0db6c06"
6+
}
7+
],
8+
"signed": {
9+
"_type": "targets",
10+
"delegations": {
11+
"keys": {
12+
"056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d": {
13+
"keytype": "ed25519",
14+
"keyval": {
15+
"public": "45d4d9ee28ef61506695130fe600d637e5f2de0de72473c280b02b89467d7aab"
16+
},
17+
"scheme": "ed25519"
18+
},
19+
"c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602": {
20+
"keytype": "ed25519",
21+
"keyval": {
22+
"public": "abe021d7594f04467627c2be390c665b311dceb83cceb685edc9b90a6e229d08"
23+
},
24+
"scheme": "ed25519"
25+
},
26+
"e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8": {
27+
"keytype": "ed25519",
28+
"keyval": {
29+
"public": "52b790190bccf730fad4b769e7073c1551938101483ff8612534eb9105426dce"
30+
},
31+
"scheme": "ed25519"
32+
}
33+
},
34+
"roles": [
35+
{
36+
"keyids": [
37+
"056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d"
38+
],
39+
"name": "../a",
40+
"paths": [
41+
"*"
42+
],
43+
"terminating": false,
44+
"threshold": 1
45+
},
46+
{
47+
"keyids": [
48+
"c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602"
49+
],
50+
"name": ".",
51+
"paths": [
52+
"*"
53+
],
54+
"terminating": false,
55+
"threshold": 1
56+
},
57+
{
58+
"keyids": [
59+
"e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8"
60+
],
61+
"name": "\u00f6",
62+
"paths": [
63+
"*"
64+
],
65+
"terminating": false,
66+
"threshold": 1
67+
}
68+
]
69+
},
70+
"expires": "2021-10-22T11:21:56Z",
71+
"spec_version": "1.0.19",
72+
"targets": {},
73+
"version": 1
74+
}
75+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8",
5+
"sig": "854fdccea623c33bf968c7ef5abea6e5e5f7c390a691ae0ae5ad87a7580fc00910b566d5dbdbfcaa948f2d8fe4348eecd5a12710d05f576aecf83fbec32c580b"
6+
}
7+
],
8+
"signed": {
9+
"_type": "targets",
10+
"expires": "2021-10-22T11:21:56Z",
11+
"spec_version": "1.0.19",
12+
"targets": {},
13+
"version": 1
14+
}
15+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095",
5+
"sig": "f00f4b0040dc6879e7ad69867ba611d52bd5e9993cbfd27e6d8073449356c716b4277093c67ae70eba90ab0367a070e69be750284e70e1135615832efda54008"
6+
}
7+
],
8+
"signed": {
9+
"_type": "snapshot",
10+
"expires": "2021-10-22T11:21:56Z",
11+
"meta": {
12+
"../a.json": {
13+
"version": 1
14+
},
15+
"..json": {
16+
"version": 1
17+
},
18+
"targets.json": {
19+
"version": 1
20+
},
21+
"\u00f6.json": {
22+
"version": 1
23+
}
24+
},
25+
"spec_version": "1.0.19",
26+
"version": 2
27+
}
28+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c",
5+
"sig": "5a0040f56454f2f338acb8a81b4c2e170e0bc61219a7cd823f635dfc9faeefcf30dfe9c792f148a25949cc9594f8ac1bfffe436b737eff140d236eba57fe9e08"
6+
}
7+
],
8+
"signed": {
9+
"_type": "timestamp",
10+
"expires": "2021-10-22T11:21:56Z",
11+
"meta": {
12+
"snapshot.json": {
13+
"version": 2
14+
}
15+
},
16+
"spec_version": "1.0.19",
17+
"version": 2
18+
}
19+
}

tests/repository_simulator.py

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
from tuf.api.serialization.json import JSONSerializer
6060
from tuf.exceptions import FetcherHTTPError
6161
from tuf.api.metadata import (
62+
DelegatedRole,
63+
Delegations,
6264
Key,
6365
Metadata,
6466
MetaFile,
@@ -106,6 +108,9 @@ def __init__(self):
106108
self.dump_dir = None
107109
self.dump_version = 0
108110

111+
now = datetime.utcnow()
112+
self.safe_expiry = now.replace(microsecond=0) + timedelta(days=30)
113+
109114
self._initialize()
110115

111116
@property
@@ -135,20 +140,19 @@ def create_key(self) -> Tuple[Key, SSlibSigner]:
135140

136141
def _initialize(self):
137142
"""Setup a minimal valid repository"""
138-
expiry = datetime.utcnow().replace(microsecond=0) + timedelta(days=30)
139143

140-
targets = Targets(1, SPEC_VER, expiry, {}, None)
144+
targets = Targets(1, SPEC_VER, self.safe_expiry, {}, None)
141145
self.md_targets = Metadata(targets, OrderedDict())
142146

143147
meta = {"targets.json": MetaFile(targets.version)}
144-
snapshot = Snapshot(1, SPEC_VER, expiry, meta)
148+
snapshot = Snapshot(1, SPEC_VER, self.safe_expiry, meta)
145149
self.md_snapshot = Metadata(snapshot, OrderedDict())
146150

147151
snapshot_meta = MetaFile(snapshot.version)
148-
timestamp = Timestamp(1, SPEC_VER, expiry, snapshot_meta)
152+
timestamp = Timestamp(1, SPEC_VER, self.safe_expiry, snapshot_meta)
149153
self.md_timestamp = Metadata(timestamp, OrderedDict())
150154

151-
root = Root(1, SPEC_VER, expiry, {}, {}, True)
155+
root = Root(1, SPEC_VER, self.safe_expiry, {}, {}, True)
152156
for role in ["root", "timestamp", "snapshot", "targets"]:
153157
key, signer = self.create_key()
154158
root.roles[role] = Role([], 1)
@@ -172,27 +176,27 @@ def publish_root(self):
172176
def fetch(self, url: str) -> Iterator[bytes]:
173177
if not self.root.consistent_snapshot:
174178
raise NotImplementedError("non-consistent snapshot not supported")
175-
176-
spliturl = parse.urlparse(url)
177-
if spliturl.path.startswith("/metadata/"):
178-
parts = spliturl.path[len("/metadata/") :].split(".")
179-
if len(parts) == 3:
180-
version: Optional[int] = int(parts[0])
181-
role = parts[1]
182-
else:
179+
path = parse.urlparse(url).path
180+
if path.startswith("/metadata/") and path.endswith(".json"):
181+
ver_and_name = path[len("/metadata/") :][: -len(".json")]
182+
# only consistent_snapshot supported ATM: timestamp is special case
183+
if ver_and_name == "timestamp":
183184
version = None
184-
role = parts[0]
185+
role = "timestamp"
186+
else:
187+
version, _, role = ver_and_name.partition(".")
188+
version = int(version)
185189
yield self._fetch_metadata(role, version)
186-
elif spliturl.path.startswith("/targets/"):
190+
elif path.startswith("/targets/"):
187191
# figure out target path and hash prefix
188-
path = spliturl.path[len("/targets/") :]
189-
dir_parts, sep , prefixed_filename = path.rpartition("/")
192+
target_path = path[len("/targets/") :]
193+
dir_parts, sep , prefixed_filename = target_path.rpartition("/")
190194
prefix, _, filename = prefixed_filename.partition(".")
191195
target_path = f"{dir_parts}{sep}{filename}"
192196

193197
yield self._fetch_target(target_path, prefix)
194198
else:
195-
raise FetcherHTTPError(f"Unknown path '{spliturl.path}'", 404)
199+
raise FetcherHTTPError(f"Unknown path '{path}'", 404)
196200

197201
def _fetch_target(self, target_path: str, hash: Optional[str]) -> bytes:
198202
"""Return data for 'target_path', checking 'hash' if it is given.
@@ -268,12 +272,14 @@ def update_timestamp(self):
268272

269273
def update_snapshot(self):
270274
for role, delegate in self.all_targets():
271-
self.snapshot.meta[f"{role}.json"].version = delegate.version
272-
275+
hashes = None
276+
length = None
273277
if self.compute_metafile_hashes_length:
274278
hashes, length = self._compute_hashes_and_length(role)
275-
self.snapshot.meta[f"{role}.json"].hashes = hashes
276-
self.snapshot.meta[f"{role}.json"].length = length
279+
280+
self.snapshot.meta[f"{role}.json"] = MetaFile(
281+
delegate.version, length, hashes
282+
)
277283

278284
self.snapshot.version += 1
279285
self.update_timestamp()
@@ -288,6 +294,37 @@ def add_target(self, role: str, data: bytes, path: str):
288294
targets.targets[path] = target
289295
self.target_files[path] = RepositoryTarget(data, target)
290296

297+
def add_delegation(
298+
self,
299+
delegator_name: str,
300+
name: str,
301+
targets: Targets,
302+
terminating: bool,
303+
paths: Optional[List[str]],
304+
hash_prefixes: Optional[List[str]],
305+
):
306+
if delegator_name == "targets":
307+
delegator = self.targets
308+
else:
309+
delegator = self.md_delegates[delegator_name].signed
310+
311+
# Create delegation
312+
role = DelegatedRole(name, [], 1, terminating, paths, hash_prefixes)
313+
if delegator.delegations is None:
314+
delegator.delegations = Delegations({}, {})
315+
# put delegation last by default
316+
delegator.delegations.roles[role.name] = role
317+
318+
# By default add one new key for the role
319+
key, signer = self.create_key()
320+
delegator.add_key(role.name, key)
321+
if role.name not in self.signers:
322+
self.signers[role.name] = []
323+
self.signers[role.name].append(signer)
324+
325+
# Add metadata for the role
326+
self.md_delegates[role.name] = Metadata(targets, OrderedDict())
327+
291328
def write(self):
292329
"""Dump current repository metadata to self.dump_dir
293330

0 commit comments

Comments
 (0)