You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reject symlinked preset registry files and parent directories when reading or saving registry metadata. Use the existing project-root-scoped FileHelper in deny mode and validate save destinations before file-change observers or directory creation.
Preserve registry JSON formatting, UTF-8 encoding, existing file metadata semantics, and observer notifications for valid paths. Registry metadata remains symlink-denied independently of the operational allow_symlinks setting. Add regression coverage and document this boundary.
Testing
Tested locally with uv run specify --help
Ran existing tests with uv sync && uv run pytest
Tested with a sample project (if applicable)
Commands and results:
uv sync --extra test --quiet — passed; restored this worktree's test environment.
.venv/bin/python -m pytest tests/specify_cli/presets/test_registry.py -q --tb=short — passed: 62 tests, including temporary-project persistence and symlink rejection cases.
The full suite used the worktree virtualenv directly, as recommended in CONTRIBUTING.md, and four pytest workers. Sample-project coverage is automated through temporary projects in the registry tests; no separate interactive sample-project run was performed.
AI Disclosure
I did not use AI assistance for this contribution
I did use AI assistance (fill in the disclosure below)
AI disclosure: GitHub Copilot (GPT-6.1 Sol, default reasoning settings) acted autonomously on behalf of @mnriem to implement the user-selected fix, generate the code, tests, documentation, commit message, and PR description, and run automated validation. A Copilot security-review subagent assisted the initial assessment using runtime-default model/settings. The contributor authorized the scope and publication; no human line-by-line review or manual testing is claimed.
Use a project-scoped deny-mode FileHelper for preset registry reads and writes, validating save destinations before file-change observers. Preserve registry serialization and metadata semantics and cover symlink rejection and normal persistence.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unhandled SymlinkDeniedError exceptions for symlinked preset paths bypass CLI JSON error handling and produce the wrong MCP error code, violating the error-envelope contract.
Hardens Spec Kit’s preset registry persistence by rejecting symlinked registry files and parent directories, regardless of the operational allow_symlinks setting.
Changes:
Routes registry reads and writes through a project-root-scoped, deny-mode FileHelper.
Validates save destinations before observers or directory creation, preserving existing persistence behavior for valid paths.
Adds regression coverage and documents the registry-specific boundary.
Validation was limited to source inspection; before-and-after tests were not independently run.
File
Description
tests/specify_cli/presets/test_registry.py
Tests persistence, observer notifications, I/O errors, and symlink rejection.
src/specify_cli/presets/_registry.py
Uses scoped filesystem operations and preflights save destinations.
design/file-helper.md
Documents unconditional symlink denial for preset registry metadata.
Map FileHelperError into the existing artifact resolution errors for shared list/info operations and contribution lookup. Preserve CLI JSON and MCP error envelopes without weakening registry symlink rejection, with before-and-after regressions across both delivery adapters.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
With a symlinked .specify/presets or .registry, specify preset list now raises an uncaught SymlinkDeniedError during manager construction (src/specify_cli/presets/command_list.py:45–47). JSON preset commands expose the helper's advice to allow symlinks, although registry links are always forbidden. Catch FileHelperError at the preset CLI boundaries, including manager/resolver construction, report registry-specific guidance, and add text-mode regression coverage.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Integration upgrades still follow registry symlinks and treat dangling links as no installed presets, allowing migrations to bypass preset-protection guards while ownership is unknown.
Fail closed when preset registry links are unreadable
design/file-helper.md:11
_installed_presets_affecting_agent() in src/specify_cli/integrations/_command_upgrade_layout.py:121–127 still follows registry links and returns no presets for dangling links, so this guarantee does not hold for integration upgrades. During command-root or command/skills migrations, that false-empty result lets the preset-protection guard pass while ownership is unknown. Use a project-root-scoped deny-mode helper there and map policy failures to _PresetRegistryUnreadableError so migration fails closed.
Translate registry symlink failures into actionable diagnostics
src/specify_cli/presets/_registry.py:36
Outside the preset CLI group, registry symlink failures still tell users to "explicitly allow symlinks", although that setting cannot make registry metadata usable. Integration registration warnings and bundle errors expose this advice; meanwhile, the CLI rewrite in _commands.py:18–23 drops the denied path. Translate symlink failures in _load and _save into a path-preserving "replace linked entries" diagnostic, and have the CLI render it.
Skip symlink tests when link creation is unsupported
tests/specify_cli/presets/test_registry.py:365
On hosts without symlink privileges, these tests fail during setup rather than exercising registry behavior. Skip when link creation raises OSError or NotImplementedError, matching tests/specify_cli/presets/test_commands.py:41–44. Apply the same guard at lines 403 and 435 of this file and in tests/specify_cli/artifacts/conftest.py:47.
Reuse the deny-mode registry reader for integration layout guards while retaining strict ownership validation. Provide path-preserving registry symlink diagnostics at the persistence boundary for CLI, integration warnings, and bundles. Guard symlink test setup on unsupported hosts and cover migration rejection before mutation.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The cross-cutting symlink security policy rejects existing symlinked preset setups even with allow_symlinks=true and still needs maintainer sign-off, before/after validation, and Windows testing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
triage-must-haveVerdict: high-value, important work for Spec Kit — do first
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Reject symlinked preset registry files and parent directories when reading or saving registry metadata. Use the existing project-root-scoped
FileHelperin deny mode and validate save destinations before file-change observers or directory creation.Preserve registry JSON formatting, UTF-8 encoding, existing file metadata semantics, and observer notifications for valid paths. Registry metadata remains symlink-denied independently of the operational
allow_symlinkssetting. Add regression coverage and document this boundary.Testing
uv run specify --helpuv sync && uv run pytestCommands and results:
uv sync --extra test --quiet— passed; restored this worktree's test environment.uv run specify --help— passed.LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q -n 4— passed: 13,699 passed, 130 skipped, 63 warnings..venv/bin/python -m pytest tests/specify_cli/presets/test_registry.py -q --tb=short— passed: 62 tests, including temporary-project persistence and symlink rejection cases.The full suite used the worktree virtualenv directly, as recommended in CONTRIBUTING.md, and four pytest workers. Sample-project coverage is automated through temporary projects in the registry tests; no separate interactive sample-project run was performed.
AI Disclosure
AI disclosure: GitHub Copilot (GPT-6.1 Sol, default reasoning settings) acted autonomously on behalf of @mnriem to implement the user-selected fix, generate the code, tests, documentation, commit message, and PR description, and run automated validation. A Copilot security-review subagent assisted the initial assessment using runtime-default model/settings. The contributor authorized the scope and publication; no human line-by-line review or manual testing is claimed.