fix: canonicalize and quote paths in reveal_path to prevent explorer argument injection#163
Merged
jmaxdev merged 2 commits intoTrixtyAI:mainfrom Apr 21, 2026
Merged
Conversation
|
Thanks for the contribution! I'll review it as soon as possible. If you have still changes, please mark this PR as draft and all reviews will be cancelled. Tests reviews will be re-run only when the PR is marked as ready for review. |
There was a problem hiding this comment.
Pull request overview
Hardens the reveal_path Tauri command to prevent Windows Explorer argument-injection (notably comma-based /select, parsing issues) by resolving paths up-front and adjusting Windows argument handling.
Changes:
- Canonicalize the input path before spawning any platform-specific “reveal” helper.
- Windows: switch from
.arg("/select,<path>")to a raw argument/select,"<path>"and strip the\\?\prefix from canonical paths. - macOS/Linux: pass the canonical
PathBuftoopen -R/xdg-open(using parent resolution on Linux).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
matiaspalmac
added a commit
to matiaspalmac/ide
that referenced
this pull request
Apr 21, 2026
… on Windows Addresses review feedback on TrixtyAI#163: - On Windows, `canonicalize()` returns verbatim paths in two shapes: `\?\C:\...` for drive letters and `\?\UNC\server\share\...` for UNC shares. Previously only the `\?\ (e.g. the root of a drive) produced `/select,"C:\"` — the trailing backslash escaped the closing quote and Explorer saw a malformed argument. Trim trailing backslashes before wrapping in the inner quotes; Explorer selects the directory just fine without the separator.
e3f5658 to
48f1f50
Compare
matiaspalmac
added a commit
to matiaspalmac/ide
that referenced
this pull request
Apr 21, 2026
… on Windows Addresses review feedback on TrixtyAI#163: - On Windows, canonicalize() returns verbatim paths in two shapes: \\?\C:\... for drive letters and \\?\UNC\server\share\... for UNC shares. Previously only the \\?\ prefix was stripped, which turned the UNC form into UNC\server\share\... — not a valid Windows path, so reveal on a network-mounted file silently broke. Now we map \\?\UNC\... back to \\server\share\... and keep the simple strip for drive-letter paths. - raw_arg writes the literal bytes we hand it, so a clean path ending in \ (e.g. the root of a drive) produced /select,"C:\" — the trailing backslash escaped the closing quote and Explorer saw a malformed argument. Trim trailing backslashes before wrapping in the inner quotes; Explorer selects the directory just fine without the separator.
48f1f50 to
0317855
Compare
…argument injection reveal_path on Windows built /select,<path> with a format string and passed the whole thing through std::process::Command::arg. Two problems: 1. A crafted path containing commas lets explorer parse the suffix as additional arguments — the classic /select,foo.lnk,evil shape the issue describes. 2. Command::arg wraps the argument in outer quotes on Windows, which Explorer doesn't recognize; explorer "/select,..." typically just falls back to opening the user's home folder instead of selecting the file. Fix in three layers: - Canonicalize the caller-supplied path up front on every platform. This fails loudly if the path doesn't exist, so reveal_path can no longer be used to probe the filesystem with speculative arguments. - On Windows, strip the \\?\ verbatim prefix that canonicalize emits (Explorer can't navigate to it) and build the argument as /select,"<path>" using CommandExt::raw_arg, which hands the slice to CreateProcess verbatim without Rust's auto-quoting. The inner quotes neutralize comma-injection in filenames. - On macOS and Linux pass the canonical PathBuf through the regular argv API, which has no shell-parsing concerns.
… on Windows Addresses review feedback on TrixtyAI#163: - On Windows, canonicalize() returns verbatim paths in two shapes: \\?\C:\... for drive letters and \\?\UNC\server\share\... for UNC shares. Previously only the \\?\ prefix was stripped, which turned the UNC form into UNC\server\share\... — not a valid Windows path, so reveal on a network-mounted file silently broke. Now we map \\?\UNC\... back to \\server\share\... and keep the simple strip for drive-letter paths. - raw_arg writes the literal bytes we hand it, so a clean path ending in \ (e.g. the root of a drive) produced /select,"C:\" — the trailing backslash escaped the closing quote and Explorer saw a malformed argument. Trim trailing backslashes before wrapping in the inner quotes; Explorer selects the directory just fine without the separator.
0317855 to
92e1c4a
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
[Fix]: Canonicalize and quote paths in
reveal_pathDescription
reveal_pathon Windows built the explorer argument withformat!("/select,{}", path)and passed it throughstd::process::Command::arg. That shape has two problems:arguments — the classic
/select,foo.lnk,evilpattern the issuecalls out. Combined with any caller that forwards externally
controlled filenames (context menus, drag-and-drop, etc.), that
becomes a user-confusion vector.
Command::argon Windows wraps the whole value in outer quotes. Formost programs that is fine, but
explorer.exedoesn't recognize"/select,..."— it silently ignores the switch and opens theuser's home folder.
Change
Fix applied in three layers in
apps/desktop/src-tauri/src/lib.rs:Path::canonicalizeruns on every platformbefore any child process is spawned. It fails loudly if the path
doesn't exist, so
reveal_pathcan no longer be used to probe thefilesystem by firing
explorer/open/xdg-openat partial orspeculative arguments. It also collapses
../symlinks into a singleconcrete path the OS agrees with.
\\?\verbatimprefix that
canonicalizeemits on Windows (Explorer can't navigateto UNC-verbatim paths) and build
/select,"<canonical>". Hand it to Explorer viaCommandExt::raw_argso Rust's auto-quoting doesn't wrap the wholething. The inner double quotes neutralize comma-injection inside
filenames without needing an allow/deny list of characters.
.arg()with the canonicalPathBuf. Bothuse argv-based spawning, so there is no shell parsing to defend
against — the canonicalize step is the only hardening they need.
Trade-offs
reveal_pathnow returns an error for paths that don't exist insteadof silently opening the user's home folder. The only in-tree caller
(
GitExplorerComponent.tsxcontext menu) passes the path of a realentry, so this matches what the UI expects.
canonical.parent()falls back to the canonical pathitself if parent resolution fails (e.g. root of a drive). Matches the
previous behavior.
needed once the path is both canonicalized and inner-quoted —
Windows forbids the dangerous characters (
<,>,",|,?,*,\0, control chars) in filenames already, so nothing hostilecan survive
canonicalize.Verification
cargo checkandcargo clippy -- -D warningsonsrc-tauri→ clean.Err("Failed to resolve path for reveal: …")./select,"C:\…,…\file"reaches Explorer as a single argument and selects correctly.
canonical form.
Related Issue
Fixes #59
Checklist