Skip to content
Permalink
Browse files Browse the repository at this point in the history
Merge pull request from GHSA-wjw6-2cqr-j4qr
Fix client issue with rolenames as filenames
  • Loading branch information
joshuagl committed Oct 19, 2021
2 parents 4d8cbc7 + 6773778 commit 4ad7ae4
Show file tree
Hide file tree
Showing 12 changed files with 424 additions and 236 deletions.
15 changes: 15 additions & 0 deletions tests/repository_data/fishy_rolenames/1.a.json
@@ -0,0 +1,15 @@
{
"signatures": [
{
"keyid": "056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d",
"sig": "6550a087bd0f01648f57e02a275f20c8e38974271d73739c446f53a028c4118e070b1d37224bc022ab6e0500c8051494f276365868ed6039ec49c7ecd8b9f602"
}
],
"signed": {
"_type": "targets",
"expires": "2021-10-22T11:21:56Z",
"spec_version": "1.0.19",
"targets": {},
"version": 1
}
}
15 changes: 15 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/1...json
@@ -0,0 +1,15 @@
{
"signatures": [
{
"keyid": "c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602",
"sig": "c0266de0724c2ab9c14e679b258033fe3aff8ce3c99419479456170975bb43de9e8539caed437cccc8e6c6068252a921f7badc5384149dab18261a7f157ae406"
}
],
"signed": {
"_type": "targets",
"expires": "2021-10-22T11:21:56Z",
"spec_version": "1.0.19",
"targets": {},
"version": 1
}
}
71 changes: 71 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/1.root.json
@@ -0,0 +1,71 @@
{
"signatures": [
{
"keyid": "b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6",
"sig": "1d3b9cebfdab388db500d01cb2cd499f016320029df17bf2f1196d8f83f12d041832dc165f23667e537d8a8aa66c716d19835bd2bcd55d4c18bbbd0c6eaf4b06"
}
],
"signed": {
"_type": "root",
"consistent_snapshot": true,
"expires": "2021-10-22T11:21:56Z",
"keys": {
"965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c": {
"keytype": "ed25519",
"keyval": {
"public": "d98dace51d795525971342b9f7317cea0d743710dca932543fedb92bb083c2c0"
},
"scheme": "ed25519"
},
"b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6": {
"keytype": "ed25519",
"keyval": {
"public": "46d386175220afd55ad9b09b6b18fa96cd69e25bc29c97ed7024a522e7e7938c"
},
"scheme": "ed25519"
},
"c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef": {
"keytype": "ed25519",
"keyval": {
"public": "a7beb72fb686a645f5ffd52e246a55d2914411853c70a5b47d837ed7b4c40734"
},
"scheme": "ed25519"
},
"e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095": {
"keytype": "ed25519",
"keyval": {
"public": "efdf10805063c1b7356f40ede43d2c5c6d2d11d79e350887ce96fe5d1e44901a"
},
"scheme": "ed25519"
}
},
"roles": {
"root": {
"keyids": [
"b24fc41c37a5e3c7b504516351633494e462137338182d8f701dc889acbd2eb6"
],
"threshold": 1
},
"snapshot": {
"keyids": [
"e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095"
],
"threshold": 1
},
"targets": {
"keyids": [
"c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef"
],
"threshold": 1
},
"timestamp": {
"keyids": [
"965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c"
],
"threshold": 1
}
},
"spec_version": "1.0.19",
"version": 1
}
}
75 changes: 75 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/1.targets.json
@@ -0,0 +1,75 @@
{
"signatures": [
{
"keyid": "c808865e701882b89c075941ca158034d8c47bde97f1dcdb2afd854334a3ffef",
"sig": "ffa055ab5108f9d22f309fecd0160b02971d7a454c8d48db4f99cdaf114b329a401b756a11e42630bff6667ad897fb05f501e3299d25fe786d12651cb0db6c06"
}
],
"signed": {
"_type": "targets",
"delegations": {
"keys": {
"056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d": {
"keytype": "ed25519",
"keyval": {
"public": "45d4d9ee28ef61506695130fe600d637e5f2de0de72473c280b02b89467d7aab"
},
"scheme": "ed25519"
},
"c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602": {
"keytype": "ed25519",
"keyval": {
"public": "abe021d7594f04467627c2be390c665b311dceb83cceb685edc9b90a6e229d08"
},
"scheme": "ed25519"
},
"e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8": {
"keytype": "ed25519",
"keyval": {
"public": "52b790190bccf730fad4b769e7073c1551938101483ff8612534eb9105426dce"
},
"scheme": "ed25519"
}
},
"roles": [
{
"keyids": [
"056a036ef6f15c1dbff1f3d61dfadfc9e92699f6b66a2e21513698b576cc498d"
],
"name": "../a",
"paths": [
"*"
],
"terminating": false,
"threshold": 1
},
{
"keyids": [
"c4f5b1013293e01cedb1680fc3aa670278fd46277c62d0bfa24ffff5f0ad0602"
],
"name": ".",
"paths": [
"*"
],
"terminating": false,
"threshold": 1
},
{
"keyids": [
"e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8"
],
"name": "\u00f6",
"paths": [
"*"
],
"terminating": false,
"threshold": 1
}
]
},
"expires": "2021-10-22T11:21:56Z",
"spec_version": "1.0.19",
"targets": {},
"version": 1
}
}
15 changes: 15 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/1.ö.json
@@ -0,0 +1,15 @@
{
"signatures": [
{
"keyid": "e38fb1b3a2dea12551541bbb205f09609d9386e147207182c8b900bc0a25e2b8",
"sig": "854fdccea623c33bf968c7ef5abea6e5e5f7c390a691ae0ae5ad87a7580fc00910b566d5dbdbfcaa948f2d8fe4348eecd5a12710d05f576aecf83fbec32c580b"
}
],
"signed": {
"_type": "targets",
"expires": "2021-10-22T11:21:56Z",
"spec_version": "1.0.19",
"targets": {},
"version": 1
}
}
28 changes: 28 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/2.snapshot.json
@@ -0,0 +1,28 @@
{
"signatures": [
{
"keyid": "e1f4f87b77838c39ec348fc6e74a10e28272fb6bf3f45bff09cd694148150095",
"sig": "f00f4b0040dc6879e7ad69867ba611d52bd5e9993cbfd27e6d8073449356c716b4277093c67ae70eba90ab0367a070e69be750284e70e1135615832efda54008"
}
],
"signed": {
"_type": "snapshot",
"expires": "2021-10-22T11:21:56Z",
"meta": {
"../a.json": {
"version": 1
},
"..json": {
"version": 1
},
"targets.json": {
"version": 1
},
"\u00f6.json": {
"version": 1
}
},
"spec_version": "1.0.19",
"version": 2
}
}
19 changes: 19 additions & 0 deletions tests/repository_data/fishy_rolenames/metadata/timestamp.json
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "965e45aad2af966bafe3719a99152fa34576a07b61742e6501c0b235fd3b8f9c",
"sig": "5a0040f56454f2f338acb8a81b4c2e170e0bc61219a7cd823f635dfc9faeefcf30dfe9c792f148a25949cc9594f8ac1bfffe436b737eff140d236eba57fe9e08"
}
],
"signed": {
"_type": "timestamp",
"expires": "2021-10-22T11:21:56Z",
"meta": {
"snapshot.json": {
"version": 2
}
},
"spec_version": "1.0.19",
"version": 2
}
}
81 changes: 59 additions & 22 deletions tests/repository_simulator.py
Expand Up @@ -59,6 +59,8 @@
from tuf.api.serialization.json import JSONSerializer
from tuf.exceptions import FetcherHTTPError
from tuf.api.metadata import (
DelegatedRole,
Delegations,
Key,
Metadata,
MetaFile,
Expand Down Expand Up @@ -106,6 +108,9 @@ def __init__(self):
self.dump_dir = None
self.dump_version = 0

now = datetime.utcnow()
self.safe_expiry = now.replace(microsecond=0) + timedelta(days=30)

self._initialize()

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

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

targets = Targets(1, SPEC_VER, expiry, {}, None)
targets = Targets(1, SPEC_VER, self.safe_expiry, {}, None)
self.md_targets = Metadata(targets, OrderedDict())

meta = {"targets.json": MetaFile(targets.version)}
snapshot = Snapshot(1, SPEC_VER, expiry, meta)
snapshot = Snapshot(1, SPEC_VER, self.safe_expiry, meta)
self.md_snapshot = Metadata(snapshot, OrderedDict())

snapshot_meta = MetaFile(snapshot.version)
timestamp = Timestamp(1, SPEC_VER, expiry, snapshot_meta)
timestamp = Timestamp(1, SPEC_VER, self.safe_expiry, snapshot_meta)
self.md_timestamp = Metadata(timestamp, OrderedDict())

root = Root(1, SPEC_VER, expiry, {}, {}, True)
root = Root(1, SPEC_VER, self.safe_expiry, {}, {}, True)
for role in ["root", "timestamp", "snapshot", "targets"]:
key, signer = self.create_key()
root.roles[role] = Role([], 1)
Expand All @@ -172,27 +176,27 @@ def publish_root(self):
def fetch(self, url: str) -> Iterator[bytes]:
if not self.root.consistent_snapshot:
raise NotImplementedError("non-consistent snapshot not supported")

spliturl = parse.urlparse(url)
if spliturl.path.startswith("/metadata/"):
parts = spliturl.path[len("/metadata/") :].split(".")
if len(parts) == 3:
version: Optional[int] = int(parts[0])
role = parts[1]
else:
path = parse.urlparse(url).path
if path.startswith("/metadata/") and path.endswith(".json"):
ver_and_name = path[len("/metadata/") :][: -len(".json")]
# only consistent_snapshot supported ATM: timestamp is special case
if ver_and_name == "timestamp":
version = None
role = parts[0]
role = "timestamp"
else:
version, _, role = ver_and_name.partition(".")
version = int(version)
yield self._fetch_metadata(role, version)
elif spliturl.path.startswith("/targets/"):
elif path.startswith("/targets/"):
# figure out target path and hash prefix
path = spliturl.path[len("/targets/") :]
dir_parts, sep , prefixed_filename = path.rpartition("/")
target_path = path[len("/targets/") :]
dir_parts, sep , prefixed_filename = target_path.rpartition("/")
prefix, _, filename = prefixed_filename.partition(".")
target_path = f"{dir_parts}{sep}{filename}"

yield self._fetch_target(target_path, prefix)
else:
raise FetcherHTTPError(f"Unknown path '{spliturl.path}'", 404)
raise FetcherHTTPError(f"Unknown path '{path}'", 404)

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

def update_snapshot(self):
for role, delegate in self.all_targets():
self.snapshot.meta[f"{role}.json"].version = delegate.version

hashes = None
length = None
if self.compute_metafile_hashes_length:
hashes, length = self._compute_hashes_and_length(role)
self.snapshot.meta[f"{role}.json"].hashes = hashes
self.snapshot.meta[f"{role}.json"].length = length

self.snapshot.meta[f"{role}.json"] = MetaFile(
delegate.version, length, hashes
)

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

def add_delegation(
self,
delegator_name: str,
name: str,
targets: Targets,
terminating: bool,
paths: Optional[List[str]],
hash_prefixes: Optional[List[str]],
):
if delegator_name == "targets":
delegator = self.targets
else:
delegator = self.md_delegates[delegator_name].signed

# Create delegation
role = DelegatedRole(name, [], 1, terminating, paths, hash_prefixes)
if delegator.delegations is None:
delegator.delegations = Delegations({}, {})
# put delegation last by default
delegator.delegations.roles[role.name] = role

# By default add one new key for the role
key, signer = self.create_key()
delegator.add_key(role.name, key)
if role.name not in self.signers:
self.signers[role.name] = []
self.signers[role.name].append(signer)

# Add metadata for the role
self.md_delegates[role.name] = Metadata(targets, OrderedDict())

def write(self):
"""Dump current repository metadata to self.dump_dir
Expand Down

0 comments on commit 4ad7ae4

Please sign in to comment.