From c829d4ca548337b515e7f16a6743fda532e013f4 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 12 May 2026 11:15:19 -0500 Subject: [PATCH 1/2] sixel: prevent allocating an absurd amount of memory or writing OOB (#20213) This commit implements two fixes for the integer overflow/out-of-bounds write reported in #20149. First, it catches any exception generated in sixel char processing (which will prevent `out_of_memory` or `bad_alloc` from being ignored sight-unseen, and will prevent the consumption of further DCS content). Second, it prevents us from allocating memory for an image which will never be displayed (because it exceeds the height of the display.) This supersedes prior work in #20153 for the same issues. Closes #20149 Closes #20153 Co-authored-by: James Holderness --- src/terminal/adapter/SixelParser.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/terminal/adapter/SixelParser.cpp b/src/terminal/adapter/SixelParser.cpp index 959c6f2f30..1ffb48e390 100644 --- a/src/terminal/adapter/SixelParser.cpp +++ b/src/terminal/adapter/SixelParser.cpp @@ -90,7 +90,15 @@ std::function SixelParser::DefineImage(const VTInt macroParameter _state = States::Normal; _parameters.clear(); return [&](const auto ch) { - _parseCommandChar(ch); + try + { + _parseCommandChar(ch); + } + catch (...) + { + // Ignore all further content. + return false; + } return true; }; } @@ -234,10 +242,18 @@ void SixelParser::_executeNextLine() _executeCarriageReturn(); _imageLineCount++; _maybeFlushImageBuffer(); - _imageCursor.y += _sixelHeight; - _availablePixelHeight -= _sixelHeight; - _resizeImageBuffer(_sixelHeight); - _fillImageBackgroundWhenScrolled(); + // If we don't have any available pixel height, that means the image has + // extended beyond the bottom of the display and we haven't triggered a + // a scroll (because sixel display mode is enabled). In this state, there + // is no point in extending the image any further, because the additional + // content will never be seen, so we'll just be wasting memory. + if (_availablePixelHeight > 0) + { + _imageCursor.y += _sixelHeight; + _availablePixelHeight -= _sixelHeight; + _resizeImageBuffer(_sixelHeight); + _fillImageBackgroundWhenScrolled(); + } } void SixelParser::_executeMoveToHome() From fb71a0462edaf32a7ac4a5ebb4df3bd05bacb41e Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Tue, 12 May 2026 23:32:56 +0200 Subject: [PATCH 2/2] Add `safeUriSchemes` setting (#20207) This adds a `safeUriSchemes` global setting which lets you define hyperlink URI schemes which the user considers safe. No confirmation dialog will be shown when trying to open hyperlinks which use these schemes. - This solves the root issue, but doesn't introduce any UI or documentation changes. I wanted to validate the approach and implementation with you first. - I closely followed the code handling the `disabledProfileSources` setting, which is of the same type. - This feature does not change the behavior of `http`, `https` and `file` schemes. Validation I ran the dev terminal, and tested the behavior by clicking on `vscode` hyperlinks generated by ripgrep with various `safeUriSchemes` settings: - Setting not defined - asks for confirmation - `["vscode"]` - does not ask for confirmation - `["foo", "vscode"]` - does not ask for confirmation - `["foo"]` - asks for confirmation - `null` - asks for confirmation - `[]` - asks for confirmation - `[""]` - asks for confirmation - `[{"foo": "bar"}]` - fails to deserialize (as expected) A few uinit tests failed, but they seem unrelated to these changes: - `KeyBindingTests` in `UnitTests_SettingsModel`, probably because I use an AZERTY keyboard. - A few `Conhost` tests, but I didn't touch this part Refs #20065 Closes #20191 --- doc/cascadia/profiles.schema.json | 7 +++++++ src/cascadia/TerminalApp/TerminalPage.cpp | 18 +++++++++++++++--- src/cascadia/TerminalApp/TerminalPage.h | 2 +- .../GlobalAppSettings.cpp | 8 ++++++++ .../GlobalAppSettings.idl | 1 + .../TerminalSettingsModel/MTSMSettings.h | 1 + .../SerializationTests.cpp | 1 + 7 files changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index d8d2a684db..2a59887da8 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2472,6 +2472,13 @@ }, "type": "array" }, + "safeUriSchemes": { + "description": "Specifies a list of URI schemes that are considered safe. No confirmation will be required to open URIs with these schemes.", + "items": { + "type": "string" + }, + "type": "array" + }, "rendering.graphicsAPI": { "description": "Direct3D 11 provides a more performant and feature-rich experience, whereas Direct2D is more stable. The default option \"Automatic\" will pick the API that best fits your graphics hardware. If you experience significant issues, consider using Direct2D.", "type": "string", diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index cfffdf9a38..0d4a245fd2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3307,13 +3307,15 @@ namespace winrt::TerminalApp::implementation return true; } - bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) + bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const { - if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https") + const auto& schemeName = parsedUri.SchemeName(); + + if (schemeName == L"http" || schemeName == L"https") { return true; } - if (parsedUri.SchemeName() == L"file") + if (schemeName == L"file") { static const auto pathext{ wil::TryGetEnvironmentVariableW(L"PATHEXT") }; const auto filename = parsedUri.Path(); @@ -3327,6 +3329,16 @@ namespace winrt::TerminalApp::implementation return true; } + if (const auto& safeSchemes = _settings.GlobalSettings().SafeUriSchemes()) + { + for (const auto& scheme : safeSchemes) + { + if (til::equals_insensitive_ascii(schemeName, scheme)) + { + return true; + } + } + } return false; } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 67ff3f2564..72f13bf43d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -438,7 +438,7 @@ namespace winrt::TerminalApp::implementation safe_void_coroutine _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs); static bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri); - static bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri); + bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const; void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri); bool _CopyText(bool dismissSelection, bool singleLine, bool withControlSequences, Microsoft::Terminal::Control::CopyFormat formats); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 3a5cd8cb1d..acbd84411f 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -102,6 +102,14 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_DisabledProfileSources->Append(src); } } + if (_SafeUriSchemes) + { + globals->_SafeUriSchemes = winrt::single_threaded_vector(); + for (const auto& src : *_SafeUriSchemes) + { + globals->_SafeUriSchemes->Append(src); + } + } for (const auto& parent : _parents) { diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index adb29b3395..33d92ed8b8 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -114,6 +114,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic); INHERITABLE_SETTING(Boolean, AllowHeadless); INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl); + INHERITABLE_SETTING(IVector, SafeUriSchemes); Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index ca147c707d..e97e6774bf 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -63,6 +63,7 @@ Author(s): X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \ X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \ X(winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, "disabledProfileSources", nullptr) \ + X(winrt::Windows::Foundation::Collections::IVector, SafeUriSchemes, "safeUriSchemes", nullptr) \ X(bool, ShowAdminShield, "showAdminShield", true) \ X(bool, TrimPaste, "trimPaste", true) \ X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \ diff --git a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp index 98ad8cfcbf..562e923fea 100644 --- a/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/SerializationTests.cpp @@ -467,6 +467,7 @@ namespace SettingsModelUnitTests "$schema" : "https://aka.ms/terminal-profiles-schema", "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", "disabledProfileSources": [ "Windows.Terminal.Wsl" ], + "safeUriSchemes": [ "vscode" ], "newTabMenu": [ {