From 6450a3a8ffed260dd6f7a9b29279392d75a9e712 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Sun, 27 Nov 2022 20:58:32 +0200 Subject: [PATCH] ngclient: Fail gracefully on missing role If role is delegated but missing from snapshot, we currently raise a undocumented KeyError: a generic RepositoryError seems better as callers are expected to handle it (and adding a more specific error seems useless as this is a repository software bug, not just expired metadata or something). The same check is also done later in TrustedMetadataSet but I think keeping the check in both is clearest. Fixes #2195 Signed-off-by: Jussi Kukkonen --- tests/test_updater_fetch_target.py | 14 ++++++++++++++ tuf/ngclient/updater.py | 9 ++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_updater_fetch_target.py b/tests/test_updater_fetch_target.py index 84caccfd29..7207d0fd7f 100644 --- a/tests/test_updater_fetch_target.py +++ b/tests/test_updater_fetch_target.py @@ -16,6 +16,7 @@ from tests import utils from tests.repository_simulator import RepositorySimulator from tuf.api.exceptions import RepositoryError +from tuf.api.metadata import DelegatedRole, Delegations from tuf.ngclient import Updater @@ -209,6 +210,19 @@ def test_invalid_target_cache(self) -> None: with open(path, "rb") as f: self.assertEqual(f.read(), target.content) + def test_meta_missing_delegated_role(self) -> None: + """Test a delegation where the role is not part of the snapshot""" + + # Add new delegation, update snapshot. Do not add the actual role + role = DelegatedRole("role1", [], 1, True, ["*"]) + self.sim.targets.delegations = Delegations({}, roles={role.name: role}) + self.sim.update_snapshot() + + # assert that RepositoryError is raised when role1 is needed + updater = self._init_updater() + with self.assertRaises(RepositoryError): + updater.get_targetinfo("") + if __name__ == "__main__": if "--dump" in sys.argv: diff --git a/tuf/ngclient/updater.py b/tuf/ngclient/updater.py index 54118268a3..737002b614 100644 --- a/tuf/ngclient/updater.py +++ b/tuf/ngclient/updater.py @@ -389,7 +389,14 @@ def _load_targets(self, role: str, parent_role: str) -> Metadata[Targets]: logger.debug("Failed to load local %s: %s", role, e) assert self._trusted_set.snapshot is not None # nosec - metainfo = self._trusted_set.snapshot.signed.meta[f"{role}.json"] + + snapshot = self._trusted_set.snapshot.signed + metainfo = snapshot.meta.get(f"{role}.json") + if metainfo is None: + raise exceptions.RepositoryError( + f"Role {role} was delegated but is not part of snapshot" + ) + length = metainfo.length or self.config.targets_max_length version = None if self._trusted_set.root.signed.consistent_snapshot: