fix(extensions): reject duplicate provides.templates/scripts names (#4016)

The resolver returns the first entry matching a declared name, so a
later duplicate within provides.templates or provides.scripts was
silently unreachable while still counted by ExtensionManifest
properties. Reject duplicates at manifest-validation time instead.

Also clarify EXTENSION-DEVELOPMENT-GUIDE.md's provides section: hooks
and events are top-level manifest fields, not provides sub-fields, so
the "at least one of ..." wording doesn't imply they can be nested
under provides.
This commit is contained in:
chelsealong
2026-08-10 10:23:23 -05:00
committed by GitHub
parent 16cfab7724
commit 11e3176fd1
3 changed files with 40 additions and 2 deletions
+23
View File
@@ -1162,6 +1162,29 @@ class TestExtensionManifestTemplatesAndScripts:
with pytest.raises(ValidationError, match="must be lowercase alphanumeric with hyphens only"):
ExtensionManifest(manifest_path)
@pytest.mark.parametrize("section", ["templates", "scripts"])
def test_provides_entry_duplicate_name_rejected(self, temp_dir, valid_manifest_data, section):
"""Two entries in the same section sharing a name are rejected.
The resolver (PresetResolver._extension_manifest_declared_template)
returns the first entry matching a name, so a later duplicate would
be silently unreachable while still counted by ExtensionManifest
properties -- reject it up front instead.
"""
import yaml
valid_manifest_data["provides"][section] = [
{"name": "dup", "file": f"{section}/a.txt"},
{"name": "dup", "file": f"{section}/b.txt"},
]
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w', encoding="utf-8") as f:
yaml.dump(valid_manifest_data, f)
with pytest.raises(ValidationError, match=f"Duplicate .* name 'dup' in 'provides.{section}'"):
ExtensionManifest(manifest_path)
@pytest.mark.parametrize("section", ["templates", "scripts"])
def test_provides_entry_path_traversal_rejected(self, temp_dir, valid_manifest_data, section):
"""The 'file' field is checked with the same path-safety policy as commands."""