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
Quoted() does not escape backslashes, corrupting process arguments #2796
StringExtensions.Quoted() (src/App.Extensions.cs) wraps a value in double quotes and escapes embedded ", but leaves backslashes untouched. Its result is assigned to ProcessStartInfo.Arguments, which .NET re-parses into argv using MSVC-style rules on every platform, where a backslash immediately before a double quote is significant.
As a result, a value that ends in a backslash, or contains a backslash directly before a quote, is handed to the child process (git) as a different string than the input.
Impact
A file whose name ends in a backslash is legal on macOS and Linux. Such a path reaches commands that place the filename last with no --, e.g.:
src/Commands/QueryFileContent.cs — show {rev}:{file}
src/Commands/SaveRevisionFile.cs — show {rev}:{file}
Viewing or saving that file from history then operates on the wrong path or fails. Two-argument commands such as Move (mv -v {old} {new}) can have the first path swallow the second.
This is a data-integrity / quoting-correctness bug. It is not a shell: arguments are passed via UseShellExecute = false, so no shell metacharacters are involved.
Reproduction
Round-trip the current Quoted() output through .NET's own argument parser:
input
Quoted() output
parsed back to
trailing\
"trailing\"
trailing"
a\"b.txt
"a\\"b.txt"
a\b.txt
Plain names, names with spaces, and a lone embedded quote round-trip correctly.
Suggested fix
Escape the value the same way .NET itself does when building an argument (System.PasteArguments): double any run of backslashes that precedes a quote or the closing quote. This fixes every call site at once, since they all funnel through Quoted() into Args.
A PR implementing this, with a standalone round-trip test that checks the invariant against .NET's real argument parser, follows.
Activity