Skip to content

fix: canonicalize and quote paths in reveal_path to prevent explorer argument injection#163

Merged
jmaxdev merged 2 commits intoTrixtyAI:mainfrom
matiaspalmac:fix/reveal-path-quoting
Apr 21, 2026
Merged

fix: canonicalize and quote paths in reveal_path to prevent explorer argument injection#163
jmaxdev merged 2 commits intoTrixtyAI:mainfrom
matiaspalmac:fix/reveal-path-quoting

Conversation

@matiaspalmac
Copy link
Copy Markdown
Contributor

[Fix]: Canonicalize and quote paths in reveal_path

Description

reveal_path on Windows built the explorer argument with
format!("/select,{}", path) and passed it through
std::process::Command::arg. That shape has two problems:

  1. A path containing commas lets Explorer parse the suffix as additional
    arguments — the classic /select,foo.lnk,evil pattern the issue
    calls out. Combined with any caller that forwards externally
    controlled filenames (context menus, drag-and-drop, etc.), that
    becomes a user-confusion vector.
  2. Command::arg on Windows wraps the whole value in outer quotes. For
    most programs that is fine, but explorer.exe doesn't recognize
    "/select,..." — it silently ignores the switch and opens the
    user's home folder.

Change

Fix applied in three layers in apps/desktop/src-tauri/src/lib.rs:

  • Canonicalize first. Path::canonicalize runs on every platform
    before any child process is spawned. It fails loudly if the path
    doesn't exist, so reveal_path can no longer be used to probe the
    filesystem by firing explorer/open/xdg-open at partial or
    speculative arguments. It also collapses ../symlinks into a single
    concrete path the OS agrees with.
  • Windows: raw-arg with inner quotes. Strip the \\?\ verbatim
    prefix that canonicalize emits on Windows (Explorer can't navigate
    to UNC-verbatim paths) and build
    /select,"<canonical>". Hand it to Explorer via
    CommandExt::raw_arg so Rust's auto-quoting doesn't wrap the whole
    thing. The inner double quotes neutralize comma-injection inside
    filenames without needing an allow/deny list of characters.
  • macOS / Linux: plain .arg() with the canonical PathBuf. Both
    use argv-based spawning, so there is no shell parsing to defend
    against — the canonicalize step is the only hardening they need.

Trade-offs

  • reveal_path now returns an error for paths that don't exist instead
    of silently opening the user's home folder. The only in-tree caller
    (GitExplorerComponent.tsx context menu) passes the path of a real
    entry, so this matches what the UI expects.
  • On Linux, canonical.parent() falls back to the canonical path
    itself if parent resolution fails (e.g. root of a drive). Matches the
    previous behavior.
  • Explicit character rejection (the issue's third suggestion) is not
    needed once the path is both canonicalized and inner-quoted —
    Windows forbids the dangerous characters (<, >, ", |, ?,
    *, \0, control chars) in filenames already, so nothing hostile
    can survive canonicalize.

Verification

  • cargo check and cargo clippy -- -D warnings on src-tauri → clean.
  • Manual trace:
    • Non-existent path → early Err("Failed to resolve path for reveal: …").
    • Path with a comma in a real folder name → /select,"C:\…,…\file"
      reaches Explorer as a single argument and selects correctly.
    • Unix paths flow through the argv API unchanged beyond using the
      canonical form.

Related Issue

Fixes #59

Checklist

  • I have tested this on the latest version.
  • I have followed the project's coding guidelines.
  • My changes generate no new warnings or errors.
  • I have verified the fix on:
    • OS: Windows
    • Version: v1.0.10

Copilot AI review requested due to automatic review settings April 21, 2026 02:13
@github-actions github-actions bot added the bug Something isn't working label Apr 21, 2026
@github-actions
Copy link
Copy Markdown

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.

Copy link
Copy Markdown

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

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 PathBuf to open -R / xdg-open (using parent resolution on Linux).

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

Comment thread apps/desktop/src-tauri/src/lib.rs Outdated
Comment thread apps/desktop/src-tauri/src/lib.rs
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.
@matiaspalmac matiaspalmac force-pushed the fix/reveal-path-quoting branch from e3f5658 to 48f1f50 Compare April 21, 2026 03:14
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.
@matiaspalmac matiaspalmac force-pushed the fix/reveal-path-quoting branch from 48f1f50 to 0317855 Compare April 21, 2026 03:16
…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.
@jmaxdev jmaxdev force-pushed the fix/reveal-path-quoting branch from 0317855 to 92e1c4a Compare April 21, 2026 03:22
@jmaxdev jmaxdev merged commit 17bfd32 into TrixtyAI:main Apr 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix]: reveal_path on Windows concatenates path into explorer /select without quoting

3 participants