mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
fix(presets): start fresh on a non-UTF-8 preset registry (#3955)
PresetRegistry._load() catches json.JSONDecodeError and FileNotFoundError to start fresh on a corrupted or missing registry, but a registry file with invalid UTF-8 bytes raised UnicodeDecodeError before JSON parsing began, crashing every preset command. Catch UnicodeDecodeError in the same clause: undecodable bytes are the same corruption class as unparseable JSON. OSError stays uncaught on purpose — the data may be intact on disk, and starting fresh would let a later _save() wipe it (same fail-closed reasoning as the workflow catalog cache loader). Assisted-by: GitHub Copilot (model: claude-fable-5, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -550,7 +550,12 @@ class PresetRegistry:
|
||||
if not isinstance(data.get("presets"), dict):
|
||||
data["presets"] = {}
|
||||
return data
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
|
||||
# Corrupted or missing registry, start fresh. A registry whose
|
||||
# bytes cannot be decoded as UTF-8 is the same corruption class
|
||||
# as malformed JSON — only the exception type differs. OSError is
|
||||
# deliberately not caught: the data may be intact on disk, and
|
||||
# starting fresh would let a later _save() wipe it.
|
||||
return {
|
||||
"schema_version": self.SCHEMA_VERSION,
|
||||
"presets": {}
|
||||
|
||||
@@ -515,6 +515,27 @@ class TestPresetRegistry:
|
||||
assert registry.list() == {}
|
||||
assert not registry.is_installed("test-pack")
|
||||
|
||||
def test_load_starts_fresh_for_non_utf8_registry(self, temp_dir):
|
||||
"""A registry file with undecodable bytes must start fresh, not raise.
|
||||
|
||||
``_load()`` already treats malformed JSON as "corrupted registry,
|
||||
start fresh", but a registry whose *bytes* cannot be decoded as UTF-8
|
||||
raised a raw ``UnicodeDecodeError`` from the same boundary — the same
|
||||
corruption class reaching a different exception type.
|
||||
"""
|
||||
packs_dir = temp_dir / "packs"
|
||||
packs_dir.mkdir()
|
||||
(packs_dir / PresetRegistry.REGISTRY_FILE).write_bytes(
|
||||
b"\xff\xfe not utf-8 \xc3\x28"
|
||||
)
|
||||
|
||||
registry = PresetRegistry(packs_dir)
|
||||
|
||||
assert registry.data == {
|
||||
"schema_version": PresetRegistry.SCHEMA_VERSION,
|
||||
"presets": {},
|
||||
}
|
||||
|
||||
def test_add_and_get(self, temp_dir):
|
||||
"""Test adding and retrieving a pack."""
|
||||
packs_dir = temp_dir / "packs"
|
||||
|
||||
Reference in New Issue
Block a user