Skip to content

Commit 9a54272

Browse files
Add more test cases and refine escaping logic.
1 parent 59fa719 commit 9a54272

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

src/ModelContextProtocol.Core/Client/StdioClientTransport.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,17 +237,23 @@ internal static bool HasExited(Process process)
237237
}
238238

239239
private static string EscapeArgumentString(string argument) =>
240-
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
240+
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !ContainsWhitespaceRegex.IsMatch(argument) ?
241241
WindowsCliSpecialArgumentsRegex.Replace(argument, static match => "^" + match.Value) :
242242
argument;
243243

244244
private const string WindowsCliSpecialArgumentsRegexString = "[&^><|]";
245+
245246
#if NET
246247
private static Regex WindowsCliSpecialArgumentsRegex => GetWindowsCliSpecialArgumentsRegex();
248+
private static Regex ContainsWhitespaceRegex => GetContainsWhitespaceRegex();
249+
247250
[GeneratedRegex(WindowsCliSpecialArgumentsRegexString, RegexOptions.CultureInvariant)]
248251
private static partial Regex GetWindowsCliSpecialArgumentsRegex();
252+
[GeneratedRegex(@"\s", RegexOptions.CultureInvariant)]
253+
private static partial Regex GetContainsWhitespaceRegex();
249254
#else
250255
private static Regex WindowsCliSpecialArgumentsRegex { get; } = new(WindowsCliSpecialArgumentsRegexString, RegexOptions.Compiled | RegexOptions.CultureInvariant);
256+
private static Regex ContainsWhitespaceRegex { get; } = new(@"\s", RegexOptions.Compiled | RegexOptions.CultureInvariant);
251257
#endif
252258

253259
[LoggerMessage(Level = LogLevel.Information, Message = "{EndpointName} connecting.")]

tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,33 @@ public async Task CreateAsync_ValidProcessInvalidServer_StdErrCallbackInvoked()
5656
[Theory]
5757
[InlineData(null)]
5858
[InlineData("argument with spaces")]
59-
[InlineData("let rec Y f x = f (Y f) x")]
6059
[InlineData("&")]
60+
[InlineData("|")]
61+
[InlineData(">")]
62+
[InlineData("<")]
63+
[InlineData("^")]
64+
[InlineData(" & ")]
65+
[InlineData(" | ")]
66+
[InlineData(" > ")]
67+
[InlineData(" < ")]
68+
[InlineData(" ^ ")]
69+
[InlineData("& ")]
70+
[InlineData("| ")]
71+
[InlineData("> ")]
72+
[InlineData("< ")]
73+
[InlineData("^ ")]
74+
[InlineData(" &")]
75+
[InlineData(" |")]
76+
[InlineData(" >")]
77+
[InlineData(" <")]
78+
[InlineData(" ^")]
6179
[InlineData("^&<>|")]
80+
[InlineData("^&<>| ")]
81+
[InlineData(" ^&<>|")]
82+
[InlineData("\t^&<>")]
83+
[InlineData("^&\t<>")]
84+
[InlineData("ls /tmp | grep foo.txt > /dev/null")]
85+
[InlineData("let rec Y f x = f (Y f) x")]
6286
[InlineData("value with \"quotes\" and spaces")]
6387
[InlineData("C:\\Program Files\\Test App\\app.dll")]
6488
[InlineData("C:\\EndsWithBackslash\\")]
@@ -78,13 +102,13 @@ public async Task EscapesCliArgumentsCorrectly(string? cliArgumentValue)
78102
Command = (PlatformDetection.IsMonoRuntime, PlatformDetection.IsWindows) switch
79103
{
80104
(true, _) => "mono",
81-
(false, true) => "TestServer.exe",
105+
(_, true) => "TestServer.exe",
82106
_ => "dotnet",
83107
},
84108
Arguments = (PlatformDetection.IsMonoRuntime, PlatformDetection.IsWindows) switch
85109
{
86110
(true, _) => ["TestServer.exe", cliArgument],
87-
(false, true) => [cliArgument],
111+
(_, true) => [cliArgument],
88112
_ => ["TestServer.dll", cliArgument],
89113
},
90114
};

0 commit comments

Comments
 (0)