fix(profiles): reject non-string profile names instead of coercing
A numeric profile id (DB row id or falsy sentinel) handed to normalize_profile_name() was silently str()'d into a real profile name: `0` → `profiles/0/` bootstrap with no config.yaml (phantom profile created on desktop first launch, #88842). normalize_profile_name now raises ValueError for non-string input; callers holding a numeric id must resolve it to an actual profile name first. A literal "0" typed by a user remains a legal explicit name.
This commit is contained in:
@@ -294,10 +294,21 @@ def normalize_profile_name(name: str) -> str:
|
||||
case-insensitively. Dashboards/tools may pass title-cased labels — normalize before
|
||||
validation, assignment, and subprocess spawn.
|
||||
|
||||
Named profiles are stored lowercase under ``profiles/<id>/``. See #18498.
|
||||
Named profiles are stored lowercase under ``profiles/<id>/``. The special
|
||||
alias ``default`` is matched case-insensitively (``Default`` → ``default``).
|
||||
Dashboards and tools may pass title-cased display labels; normalize before
|
||||
validation, assignment, and subprocess spawn (see issue #18498).
|
||||
|
||||
Raises ``ValueError`` for non-string input: a numeric profile id (e.g. a
|
||||
DB row id or a falsy sentinel) silently coerced via ``str()`` becomes a
|
||||
real on-disk profile directory (``profiles/0/``, #88842). Callers that
|
||||
hold a numeric id must resolve it to an actual profile name first.
|
||||
"""
|
||||
if not isinstance(name, str):
|
||||
name = str(name)
|
||||
raise ValueError(
|
||||
"profile name must be a string, got "
|
||||
f"{type(name).__name__}: {name!r}"
|
||||
)
|
||||
stripped = name.strip()
|
||||
if not stripped:
|
||||
raise ValueError("profile name cannot be empty")
|
||||
|
||||
@@ -75,6 +75,21 @@ class TestNormalizeProfileName:
|
||||
assert normalize_profile_name("Jules") == "jules"
|
||||
assert normalize_profile_name(" Librarian ") == "librarian"
|
||||
|
||||
def test_non_string_name_rejected(self):
|
||||
# A numeric profile id (DB row id / falsy sentinel) must not be
|
||||
# silently coerced into a real on-disk profile directory (#88842).
|
||||
with pytest.raises(ValueError):
|
||||
normalize_profile_name(0)
|
||||
with pytest.raises(ValueError):
|
||||
normalize_profile_name(None)
|
||||
with pytest.raises(ValueError):
|
||||
normalize_profile_name(42)
|
||||
|
||||
def test_literal_zero_string_is_a_valid_explicit_name(self):
|
||||
# A literal "0" typed by the user is a legal profile id; only the
|
||||
# non-string coercion created the phantom profile.
|
||||
assert normalize_profile_name("0") == "0"
|
||||
|
||||
|
||||
class TestValidateProfileName:
|
||||
"""Tests for validate_profile_name()."""
|
||||
|
||||
Reference in New Issue
Block a user