From fb71a0462edaf32a7ac4a5ebb4df3bd05bacb41e Mon Sep 17 00:00:00 2001 From: Lucas Trzesniewski Date: Tue, 12 May 2026 23:32:56 +0200 Subject: [PATCH] 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": [ {