Skip to content

fix: prevent extension command shadowing#1994

Merged
mnriem merged 3 commits intogithub:mainfrom
afurm:af/fix-extension-command-collisions
Mar 27, 2026
Merged

fix: prevent extension command shadowing#1994
mnriem merged 3 commits intogithub:mainfrom
afurm:af/fix-extension-command-collisions

Conversation

@afurm
Copy link
Copy Markdown
Contributor

@afurm afurm commented Mar 27, 2026

Description

Fixes #1963.

This PR hardens extension installation so extensions cannot silently shadow core Spec Kit commands or collide with commands from other installed extensions.

Specifically, it:

  • adds install-time validation for extension command and alias names
  • rejects extension IDs and command namespaces that overlap with core speckit.* commands
  • rejects malformed short aliases that do not follow speckit.{extension}.{command}
  • rejects command/alias collisions with already-installed extensions
  • updates extension tests and shipped extension docs/examples to use namespaced aliases

This is needed to close a security and reliability gap where an extension could overwrite core command files or another extension's command files during installation.

Testing

  • Ran uv run specify --help successfully and confirmed extension commands are present

  • Ran the full test suite successfully with uv sync --extra test && uv run pytest (876 passed)

  • Also ran uv run --extra test pytest tests/test_extensions.py while developing the fix

  • Tested locally with uv run specify --help

  • Ran existing tests with uv sync --extra test && uv run pytest

  • Tested with a sample project (not applicable for this change)

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Used Codex to inspect the issue, implement the validation changes, update tests/docs, and run verification commands.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR closes a security/reliability gap in the extension system by adding install-time validation that prevents extensions from shadowing core Spec Kit commands or colliding with commands/aliases from other installed extensions.

Changes:

  • Add install-time validation in ExtensionManager to reject core-namespace shadowing, invalid alias shapes, and collisions with installed extensions.
  • Expand extension installation tests to cover core-namespace conflicts, malformed aliases, and cross-extension collisions.
  • Update extension docs/templates/examples to use namespaced aliases instead of legacy short aliases.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/specify_cli/extensions.py Introduces core-command reservation and install-time conflict validation for command/alias names.
tests/test_extensions.py Adds/updates tests asserting the new validation behavior and updated alias naming.
extensions/template/extension.yml Updates template manifest example to use namespaced aliases.
extensions/RFC-EXTENSION-SYSTEM.md Updates examples away from short aliases; needs wording alignment for “backward compatibility”.
extensions/EXTENSION-USER-GUIDE.md Updates user-facing examples to show namespaced alias usage.
extensions/EXTENSION-DEVELOPMENT-GUIDE.md Updates developer guidance to show namespaced alias usage.
extensions/EXTENSION-API-REFERENCE.md Updates manifest reference to note alias restrictions (needs to reflect all new constraints).
Comments suppressed due to low confidence (1)

src/specify_cli/extensions.py:518

  • The install-time validation only checks that names match the speckit.<namespace>.<command> pattern, but it doesn’t enforce that <namespace> matches manifest.id. As written, an extension could publish commands/aliases under another extension’s namespace (namespace squatting/impersonation), and also block the real extension from installing later. Consider rejecting command/alias names where the parsed namespace (match group 1) is not exactly manifest.id.
                namespace = match.group(1)
                if namespace in CORE_COMMAND_NAMES:
                    raise ValidationError(
                        f"{kind.capitalize()} '{name}' conflicts with core command namespace '{namespace}'"
                    )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback. If not applicable, please explain why

@afurm
Copy link
Copy Markdown
Contributor Author

afurm commented Mar 27, 2026

Please address Copilot feedback. If not applicable, please explain why

Addressed in 3571f75.

I fixed all 3 Copilot items:

  • Enforced extension.id namespace ownership.
  • Replaced the hard-coded core command list with discovery from bundled templates, plus a parity test.
  • Updated the docs/RFC wording to match the implemented validation and migration behavior.

Validation:

  • uv run --with '.[test]' pytest tests/test_extensions.py -q
  • 148 passed

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented Mar 27, 2026

@afurm This does not prevent presets from overriding commands?

@afurm
Copy link
Copy Markdown
Contributor Author

afurm commented Mar 27, 2026

@afurm This does not prevent presets from overriding commands?

Correct. This change does not prevent presets from overriding commands.

The new validation only runs in the extension install path (ExtensionManager.install_from_directory() / install_from_zip() in src/specify_cli/extensions.py). Presets still use the separate preset registration flow in src/specify_cli/presets.py, where command overrides are applied by priority and restored on removal.

So the intent here is narrower: prevent extensions from claiming core-command namespaces or another extension’s namespace. It does not change the existing preset override mechanism.

@mnriem mnriem merged commit 796b4f4 into github:main Mar 27, 2026
12 checks passed
@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented Mar 27, 2026

Thank you!

@afurm afurm deleted the af/fix-extension-command-collisions branch March 27, 2026 15:57
@mbachorik
Copy link
Copy Markdown
Contributor

Thank you!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extension commands can shadow core spec-kit commands

4 participants