Sanitize C1 control chars in SetConsoleTitle API (#10847)
When the `SetContoleTitle` API is called with a title containing control characters, we need to filter out those characters before we can forward the title change over conpty as an escape sequence. If we don't do that, the receiving terminal will end up executing the control characters instead of updating the title. We were already filtering out the C0 control characters, but with this PR we're now filtering out C1 controls characters as well. I've simply updated the sanitizing routine in `DoSrvSetConsoleTitleW` to filter our characters in the range `0x80` to `0x9F`. This is in addition to the C0 range (`0x00` to `0x1F`) that was already excluded. ## Validation Steps Performed I've added a conpty unit test that calls `DoSrvSetConsoleTitleW` with titles containing a variety of C0 and C1 controls characters, and which verifies that those characters are stripped from the title forwarded to conpty. I've also confirmed that the test case in issue #10312 is now working correctly in Windows Terminal. Closes #10312
This commit is contained in:
parent
94166942cc
commit
9ba20805ec
|
@ -1931,15 +1931,11 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
|||
// to embed control characters in that string.
|
||||
if (gci.IsInVtIoMode())
|
||||
{
|
||||
std::wstring sanitized;
|
||||
sanitized.reserve(title.size());
|
||||
for (size_t i = 0; i < title.size(); i++)
|
||||
{
|
||||
if (title.at(i) >= UNICODE_SPACE)
|
||||
{
|
||||
sanitized.push_back(title.at(i));
|
||||
}
|
||||
}
|
||||
std::wstring sanitized{ title };
|
||||
sanitized.erase(std::remove_if(sanitized.begin(), sanitized.end(), [](auto ch) {
|
||||
return ch < UNICODE_SPACE || (ch > UNICODE_DEL && ch < UNICODE_NBSP);
|
||||
}),
|
||||
sanitized.end());
|
||||
|
||||
gci.SetTitle({ sanitized });
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ class ConptyOutputTests
|
|||
TEST_METHOD(WriteTwoLinesUsesNewline);
|
||||
TEST_METHOD(WriteAFewSimpleLines);
|
||||
TEST_METHOD(InvalidateUntilOneBeforeEnd);
|
||||
TEST_METHOD(SetConsoleTitleWithControlChars);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, size_t const cch);
|
||||
|
@ -364,3 +365,37 @@ void ConptyOutputTests::InvalidateUntilOneBeforeEnd()
|
|||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyOutputTests::SetConsoleTitleWithControlChars()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:control", L"{0x00, 0x0A, 0x1B, 0x80, 0x9B, 0x9C}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
int control;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"control", control));
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"SetConsoleTitle with a control character (0x%02X) embedded in the text", control));
|
||||
|
||||
std::wstringstream titleText;
|
||||
titleText << L"Hello " << wchar_t(control) << L"World!";
|
||||
VERIFY_SUCCEEDED(DoSrvSetConsoleTitleW(titleText.str()));
|
||||
|
||||
// This is the standard init sequences for the first frame.
|
||||
expectedOutput.push_back("\x1b[2J");
|
||||
expectedOutput.push_back("\x1b[m");
|
||||
expectedOutput.push_back("\x1b[H");
|
||||
|
||||
// The title change is propagated as an OSC 0 sequence.
|
||||
// Control characters are stripped, so it's always "Hello World".
|
||||
expectedOutput.push_back("\x1b]0;Hello World!\a");
|
||||
|
||||
// This is also part of the standard init sequence.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue