Severity: Warning
File: src/Servy.Core/Helpers/ProcessHelper.cs
Lines: 396-413
public string EscapeArgument(string arg)
{
if (string.IsNullOrEmpty(arg)) return \"\\\"\\\"\";
string escaped = arg.Replace(\"\\\"\", \"\\\\\\\"\");
if (escaped.EndsWith(\"\\\\\"))
{
escaped += \"\\\\\";
}
return $\"\\\"{escaped}\\\"\";
}
The Win32 CommandLineToArgvW rule is: any run of N backslashes immediately before a \" must be doubled to 2N so the kernel parser sees 2N literal backslashes followed by an escaped quote. This method only doubles trailing backslashes and only escapes the quote character itself — it does not double backslashes that precede an internal quote.
Failure case: input foo\\\"bar (one backslash, one quote, then bar).
Replace(\"\\\"\", \"\\\\\\\"\") → foo\\\\\"bar (now \\\\\").
- Doesn't end with
\, no trailing fix.
- Final result:
\"foo\\\\\"bar\".
CommandLineToArgvW parses this as: \\ → one literal \, then \" → closing quote. Result: argument is foo\ and bar becomes a separate argument (or junk).
Correct expectation: argument should be foo\\\"bar (the original literal). Required output is \"foo\\\\\\\"bar\" — two backslashes (so the kernel sees one literal \), then \\\" (escaped quote).
The sibling method EscapeProcessArgument (lines 350-393) implements this correctly. The two methods are exposed on the same IProcessHelper interface, so callers can pick either.
The buggy method is in use: ServiceCommands.cs:215 calls _processHelper.EscapeArgument(service.Name). Service names rarely contain \ or \", but other future callers may pass arbitrary text.
Suggested fix: delete EscapeArgument and route all callers to EscapeProcessArgument, or fix the body to mirror the per-character backslash-counting logic from EscapeProcessArgument.
Severity: Warning
File:
src/Servy.Core/Helpers/ProcessHelper.csLines: 396-413
The Win32
CommandLineToArgvWrule is: any run ofNbackslashes immediately before a\"must be doubled to2Nso the kernel parser sees2Nliteral backslashes followed by an escaped quote. This method only doubles trailing backslashes and only escapes the quote character itself — it does not double backslashes that precede an internal quote.Failure case: input
foo\\\"bar(one backslash, one quote, thenbar).Replace(\"\\\"\", \"\\\\\\\"\")→foo\\\\\"bar(now\\\\\").\, no trailing fix.\"foo\\\\\"bar\".CommandLineToArgvWparses this as:\\→ one literal\, then\"→ closing quote. Result: argument isfoo\andbarbecomes a separate argument (or junk).Correct expectation: argument should be
foo\\\"bar(the original literal). Required output is\"foo\\\\\\\"bar\"— two backslashes (so the kernel sees one literal\), then\\\"(escaped quote).The sibling method
EscapeProcessArgument(lines 350-393) implements this correctly. The two methods are exposed on the sameIProcessHelperinterface, so callers can pick either.The buggy method is in use:
ServiceCommands.cs:215calls_processHelper.EscapeArgument(service.Name). Service names rarely contain\or\", but other future callers may pass arbitrary text.Suggested fix: delete
EscapeArgumentand route all callers toEscapeProcessArgument, or fix the body to mirror the per-character backslash-counting logic fromEscapeProcessArgument.