Skip to content

Explicit encode role names #1759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
2 changes: 2 additions & 0 deletions tests/repository_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ def fetch_metadata(self, role: str, version: Optional[int] = None) -> bytes:
If version is None, non-versioned metadata is being requested.
"""
self.fetch_tracker.metadata.append((role, version))
# decode role for the metadata
role = parse.unquote(role, encoding="utf-8")

if role == Root.type:
# return a version previously serialized in publish_root()
Expand Down
21 changes: 15 additions & 6 deletions tests/test_updater_delegation_graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,10 @@ def test_invalid_metadata(self, test_data: DelegationsTestCase) -> None:
finally:
self.teardown_subtest()

def test_fishy_rolenames(self) -> None:
# Test that delegated roles filenames are safely encoded
# when persisted to the file system.
def test_safely_encoded_rolenames(self) -> None:
"""Test that delegated roles names are safely encoded in the filenames
and URLs.
"""

roles_to_filenames = {
"../a": "..%2Fa.json",
Expand All @@ -343,16 +344,24 @@ def test_fishy_rolenames(self) -> None:
for rolename in roles_to_filenames:
delegations.append(TestDelegation("targets", rolename))

fishy_rolenames = DelegationsTestCase(delegations)
self._init_repo(fishy_rolenames)
delegated_rolenames = DelegationsTestCase(delegations)
self._init_repo(delegated_rolenames)
updater = self._init_updater()
updater.refresh()

# trigger updater to fetch the delegated metadata, check filenames
# trigger updater to fetch the delegated metadata
self.sim.fetch_tracker.metadata.clear()
updater.get_targetinfo("anything")

# assert that local delegated metadata filenames are expected
local_metadata = os.listdir(self.metadata_dir)
for fname in roles_to_filenames.values():
self.assertTrue(fname in local_metadata)

# assert that requested URLs are quoted without extension
exp_calls = [(quoted[:-5], 1) for quoted in roles_to_filenames.values()]
self.assertListEqual(self.sim.fetch_tracker.metadata, exp_calls)


class TestTargetFileSearch(TestDelegations):
r"""
Expand Down
5 changes: 3 additions & 2 deletions tuf/ngclient/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,11 @@ def _download_metadata(
self, rolename: str, length: int, version: Optional[int] = None
) -> bytes:
"""Download a metadata file and return it as bytes"""
encoded_name = parse.quote(rolename, "")
if version is None:
url = f"{self._metadata_base_url}{rolename}.json"
url = f"{self._metadata_base_url}{encoded_name}.json"
else:
url = f"{self._metadata_base_url}{version}.{rolename}.json"
url = f"{self._metadata_base_url}{version}.{encoded_name}.json"
return self._fetcher.download_bytes(url, length)

def _load_local_metadata(self, rolename: str) -> bytes:
Expand Down