Skip to content

fix: escape backslashes when quoting process arguments - #2797

Open
drewster99 wants to merge 2 commits into
sourcegit-scm:masterfrom
drewster99:fix/quoted-argument-escaping
Open

drewster99 wants to merge 2 commits into
sourcegit-scm:masterfrom
drewster99:fix/quoted-argument-escaping

Conversation

@drewster99

Copy link
Copy Markdown

Fixes #2796.

Problem

StringExtensions.Quoted() wraps a value in double quotes and escapes embedded ", but leaves backslashes untouched. The result is assigned to ProcessStartInfo.Arguments, which .NET re-parses into argv using MSVC-style rules on every platform, where a backslash before a double quote is significant. A value ending in a backslash, or containing a backslash immediately before a quote, is therefore handed to git as a different string.

In practice a file whose name ends in a backslash (valid on macOS and Linux) reaches commands such as show {rev}:{file} and cat-file -s {rev}:{file} as the wrong path, so viewing or saving that file from history operates on the wrong target or fails.

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, and escape embedded quotes. Because every git call funnels through Quoted() into Args, this one change covers all call sites.

Test

Adds tools/quote-roundtrip-test, a standalone console check that asserts the invariant "Quoted(name) parses back into exactly one argv entry equal to name" against .NET's real argument parser, using only benign filenames (no git, no payload). It fails on the old implementation and passes on the new one.

Verification

  • dotnet build -c Debug src/SourceGit.csproj — succeeds, no new warnings.
  • dotnet format --verify-no-changes src/SourceGit.csproj — clean.
  • dotnet run -c Release in tools/quote-roundtrip-test — all inputs pass.

StringExtensions.Quoted() wrapped a value in double quotes and escaped
embedded quotes, but left backslashes untouched. The result is assigned
to ProcessStartInfo.Arguments, which .NET re-parses into argv using
MSVC-style rules on every platform, where a backslash before a double
quote is significant. A value ending in a backslash, or containing a
backslash immediately before a quote, was therefore handed to git as a
different string.

In practice a file whose name ends in a backslash (valid on macOS and
Linux) reached commands such as `show {rev}:{file}` and
`cat-file -s {rev}:{file}` as the wrong path, so viewing or saving that
file from history operated on the wrong target or failed.

Apply the same escaping .NET itself uses (System.PasteArguments) so the
value round-trips verbatim. Add tools/quote-roundtrip-test, which checks
the invariant against .NET's real argument parser.
Quoted() no longer delegates to Escaped(), and nothing else references
it, so drop the dead method. EscapeForBRE() is unrelated and retained.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Quoted() does not escape backslashes, corrupting process arguments

1 participant