Skip to content

fix(extensions): reject aliases that shadow core commands - #4558

Open
chelsealong wants to merge 1 commit into
github:mainfrom
chelsealong:fix/4555-alias-shadows-core-command
Open

fix(extensions): reject aliases that shadow core commands#4558
chelsealong wants to merge 1 commit into
github:mainfrom
chelsealong:fix/4555-alias-shadows-core-command

Conversation

@chelsealong

Copy link
Copy Markdown
Contributor

Summary

Fixes #4555.

_validate_install_conflicts is documented as rejecting installs that "would shadow core or installed extension commands", but _get_installed_command_name_map only walks self.registry — installed extensions. Core command names were never in the map it consults, so an extension could declare a core command's fully-qualified name (e.g. speckit.taskstoissues) as an alias and install successfully, shadowing the core command.

Primary command names are already namespace-checked against CORE_COMMAND_NAMES in _collect_manifest_command_names (a primary name like speckit.taskstoissues is correctly rejected — it fails the required two-segment speckit.{extension}.{command} pattern). Aliases are intentionally free-form (existing community extensions use short aliases like speckit.verify), so they can't be pattern-checked the same way — the only safe fix is to compare declared alias names directly against the fully-qualified core command names in the install-conflict check.

Fix

In _validate_install_conflicts (src/specify_cli/extensions/__init__.py), build core_command_names from CORE_COMMAND_NAMES (qualified as speckit.<name>) and reject any declared command/alias name that matches, in addition to the existing installed-extension check.

Test plan

  • Added test_install_rejects_alias_shadowing_core_command to tests/test_extensions.py, reproducing the exact scenario from the issue: a manifest declaring speckit.probe.taskstoissues with alias speckit.taskstoissues (a live core command).
  • Fail-before / pass-after: reverted just the source change (git checkout HEAD~1 -- src/specify_cli/extensions/__init__.py), confirmed the new test fails (Failed: DID NOT RAISE ValidationError), restored the fix, confirmed it passes.
  • Full tests/test_extensions.py: 536 passed.
  • Full suite (python -m pytest tests -q): 8054 passed, 12 skipped, no failures.

AI Disclosure

  • I did use AI assistance (describe below)

This fix was implemented with Claude Code (Claude Sonnet 5), based on the reproduction and root-cause analysis in the linked issue. I reviewed the code paths involved (_collect_manifest_command_names, _get_installed_command_name_map, _validate_install_conflicts), confirmed the fail-before/pass-after test behavior described above, and ran the full test suite myself before opening this PR.

🤖 Generated with Claude Code

_validate_install_conflicts only compared declared command/alias names
against installed extensions, never against core command names, so an
extension could claim a core command's fully-qualified name (e.g.
'speckit.taskstoissues') as an alias and shadow it silently. Primary
names are already namespace-checked against CORE_COMMAND_NAMES, but
aliases are intentionally free-form, so this can only be caught in the
install-conflict check by comparing against the qualified core names
directly.

Fixes github#4555
@chelsealong
chelsealong requested a review from mnriem as a code owner September 12, 2026 11:24
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An extension alias can shadow a core command; _validate_install_conflicts only checks installed extensions

2 participants