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
{{ message }}
Repository navigation
fix: isolate self-upgrade installer lookup from project paths - #5175
Resolve uv and pipx installers from absolute PATH directories outside the current working directory and Git worktree. Use the same lookup for registry detection and self-upgrades, bypassing Windows' implicit current-directory search while preserving eligible PATH ordering and PATHEXT handling.
Add regression coverage for unsafe search paths, symlinked directories, absolute installer execution, missing-installer errors, and normal user-local installations.
Testing
Tested locally with uv run specify --help
Ran existing tests with uv sync && uv run pytest
Tested with a sample project (if applicable)
The full suite used the documented worktree-local interpreter instead of the generic command above:
Run on macOS; native Windows validation was not performed. Sample-project testing is not applicable because this change does not affect scaffolding or generated project assets.
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 using GPT-6.1 Sol in autonomous mode generated the implementation, tests, commit message, and pull request body, and ran validation. The initial alert assessment used a security-review subagent whose model was not reported by the runtime. No documentation changes are included.
Resolve uv and pipx through absolute PATH directories outside the working directory and Git worktree for registry probes and self-upgrades. Preserve Windows PATHEXT handling and eligible PATH ordering, with regression coverage for unsafe search paths and normal installations.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This PR centralizes installer lookup for Specify CLI’s registry detection and self-upgrade workflow to reduce execution of project-controlled uv and pipx binaries.
Changes:
Filters installer searches to eligible absolute PATH directories, preserving ordering and Windows PATHEXT handling.
Shares lookup between registry probes and upgrade commands.
Adds regression coverage for unsafe paths, symlinks, absolute execution, and missing installers.
AI-assisted review for the requester: GitHub Copilot; model unknown; autonomous analysis, suggested fixes, and summary.
File
Description
tests/specify_cli/test_version_execution.py
Updates execution-test mocking to use the shared installer lookup.
tests/specify_cli/test_version_detection.py
Adds lookup security regressions and adapts existing mocks.
src/specify_cli/_version.py
Adds filtered installer discovery and uses it for detection and upgrades.
Validate directory ancestors and intermediate symlink targets, and reject installer executables resolving into the working directory or Git worktree. Execute the validated absolute target and use absolute installer fixtures in lookup mocks. Add regression coverage for alias, file, and combined symlink bypasses across discovery and upgrades.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Addressed both findings in ee20e70: installer lookup now checks directory ancestors and intermediate symlink targets, rejects executables resolving into the working directory or Git worktree, and executes the validated absolute target. Lookup mocks now return absolute installer paths, with regression coverage for alias, file, and combined symlink bypasses across lookup, registry detection, and upgrades. LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q -n 2 passed: 13,756 passed, 130 skipped. Validation ran on macOS; native Windows validation was not performed. Posted on behalf of @mnriem by GitHub Copilot (GPT-6.1 Sol, autonomous mode), which authored the implementation, tests, and this summary and ran validation.
Reuse the existing comparison-only extended-path normalization for installer cwd and worktree checks. Preserve original filesystem access paths while recognizing equivalent Windows drive and UNC namespace spellings, including intermediate symlink targets. Add portable containment regressions for plain and extended roots and targets.
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
Critical symlink, junction, and platform-specific path-comparison bypasses still allow project- or CWD-controlled paths to influence installer selection.
Inspect intermediate executable links even without a Git root, compare filesystem identities for cwd and worktree boundaries, and reject unsupported Windows reparse points. Warn and continue after per-entry inspection failures while preserving the selected absolute installer shim spelling. Add case-alias, CWD-chain, dispatcher, failure-recovery, and native Windows namespace/junction regressions.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Addressed this review in cd1e7d2 and b98bc93. Installer lookup now inspects intermediate executable links even without a Git root, uses filesystem identities for CWD and worktree boundaries, recognizes equivalent Windows namespace spellings, rejects unsupported reparse points including junctions, warns and continues after per-entry inspection failures, and executes the selected absolute shim spelling rather than its dispatcher target. Added regressions for CWD link chains, case-insensitive aliases, inspection failures, differently named dispatchers, and native Windows namespace/junction paths. The focused local tests passed: 489 passed, 66 skipped. CI has passed on Linux and macOS with Python 3.13/3.14 and Windows with Python 3.14; Windows Python 3.13 is still running. Posted on behalf of @mnriem by GitHub Copilot (GPT-6.1 Sol, autonomous mode), which authored the implementation, tests, and this summary and ran validation.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Installer validation still permits working-directory-controlled symlink chains and project-controlled Windows junctions, while returning resolved targets breaks symlink-based dispatch.
No open issues 🎉
3 previously missed issues
In code that hasn't changed since last review
Detect Windows junctions in installer symlink validation
src/specify_cli/_version.py:502
Windows junctions are not detected by is_symlink(), so a chain such as C:\alias -> C:\project\bin -> C:\external passes these checks despite traversing a project-controlled junction. Registry probes and upgrades can therefore use an installer selected through the worktree. Inspect junction targets or reject non-symlink reparse points as src/specify_cli/file_helper.py:79-88 does, and add a native Windows regression.
Validate intermediate symlinks without a Git ancestor
src/specify_cli/_version.py:508
Without a Git ancestor, this early return accepts an external bin/uv -> $PWD/uv -> /outside/uv chain because only the endpoint parents are checked. The working directory therefore still controls installer selection. Traverse intermediate symlinks even without a worktree and reject links located directly in cwd, while preserving allowed user-local descendant directories; add regressions for both installers.
Return the validated entrypoint for symlink dispatch
src/specify_cli/_version.py:545
Returning the resolved target changes the executable name for symlink-based dispatchers, so a valid bin/uv -> dispatcher runs in the wrong mode and breaks registry detection and self-upgrade. Return the validated absolute entrypoint instead, and update the external-file-symlink test to check dispatch behavior rather than require the target path.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Installer resolution still allows CWD-controlled directory-symlink chains to override trusted installers when no Git ancestor exists, while blanket junction rejection also blocks legitimate Windows installations.
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
Resolve
uvandpipxinstallers from absolutePATHdirectories outside the current working directory and Git worktree. Use the same lookup for registry detection and self-upgrades, bypassing Windows' implicit current-directory search while preserving eligiblePATHordering andPATHEXThandling.Add regression coverage for unsafe search paths, symlinked directories, absolute installer execution, missing-installer errors, and normal user-local installations.
Testing
uv run specify --helpuv sync && uv run pytestThe full suite used the documented worktree-local interpreter instead of the generic command above:
uv sync --extra test --quiet— passed.LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q— passed: 13,702 passed, 130 skipped.LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests/specify_cli/test_version_*.py tests/specify_cli/selfs tests/specify_cli/test_root_version_callback.py -q --tb=short— passed: 212 passed, 1 skipped.uv run specify --help— passed.Run on macOS; native Windows validation was not performed. Sample-project testing is not applicable because this change does not affect scaffolding or generated project assets.
AI Disclosure
AI disclosure: GitHub Copilot using GPT-6.1 Sol in autonomous mode generated the implementation, tests, commit message, and pull request body, and ran validation. The initial alert assessment used a security-review subagent whose model was not reported by the runtime. No documentation changes are included.