diff --git a/tests/test_metadata_serialization.py b/tests/test_metadata_serialization.py index c7457482cc..c10798f611 100644 --- a/tests/test_metadata_serialization.py +++ b/tests/test_metadata_serialization.py @@ -490,6 +490,11 @@ def test_invalid_succinct_roles_serialization(self, test_data: str) -> None: {"keyids": ["keyid1"], "name": "b", "terminating": true, "paths": ["fn1"], "threshold": 3}, \ {"keyids": ["keyid2"], "name": "root", "terminating": true, "paths": ["fn2"], "threshold": 4} ] \ }', + "using top-level role name in a different case": '{"keys": { \ + "keyid1" : {"keytype": "rsa", "scheme": "rsassa-pss-sha256", "keyval": {"public": "foo"}}}, \ + "roles": [ \ + {"keyids": ["keyid1"], "name": "Root", "terminating": true, "paths": ["fn1"], "threshold": 3}] \ + }', "roles and succinct_roles set": '{"keys": { \ "keyid1" : {"keytype": "rsa", "scheme": "rsassa-pss-sha256", "keyval": {"public": "foo"}}, \ "keyid2" : {"keytype": "ed25519", "scheme": "ed25519", "keyval": {"public": "bar"}}}, \ diff --git a/tuf/api/_payload.py b/tuf/api/_payload.py index 7b63cf5e01..82e2d402b5 100644 --- a/tuf/api/_payload.py +++ b/tuf/api/_payload.py @@ -1428,7 +1428,11 @@ def __init__( if roles is not None: for role in roles: - if not role or role in TOP_LEVEL_ROLE_NAMES: + # Compare case-insensitively: the client stores delegated + # metadata as ".json", and quoting leaves + # ASCII letters alone, so "Root" and "root" are the same file + # on a case-insensitive filesystem. + if not role or role.lower() in TOP_LEVEL_ROLE_NAMES: raise ValueError( "Delegated roles cannot be empty or use top-level " "role names"