diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 95d8ea17f..1e00c19c8 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -43,7 +43,8 @@ - + + Create diff --git a/src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp b/src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp new file mode 100644 index 000000000..cafa368f3 --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// A series of tests for the TerminalPage::_isTrustedCommandline function. +// That's a heuristic function for deciding if we should automatically trust +// certain commandlines by default. The logic in there is weird and there are +// lots of edge cases, so it's easier to just write a unit test. + +#include "pch.h" + +#include "../TerminalApp/TerminalPage.h" + +using namespace Microsoft::Console; +using namespace TerminalApp; +using namespace winrt::TerminalApp; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +using namespace WEX::Logging; +using namespace WEX::TestExecution; +using namespace WEX::Common; + +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::System; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Text; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +namespace TerminalAppLocalTests +{ + class TrustCommandlineTests + { + BEGIN_TEST_CLASS(TrustCommandlineTests) + END_TEST_CLASS() + + TEST_METHOD(SimpleTests); + TEST_METHOD(TestCommandlineWithArgs); + TEST_METHOD(TestCommandlineWithSpaces); + TEST_METHOD(TestCommandlineWithEnvVars); + TEST_METHOD(WslTests); + TEST_METHOD(TestPwshLocation); + + bool trust(std::wstring_view cmdline); + }; + + bool TrustCommandlineTests::trust(std::wstring_view cmdline) + { + return implementation::TerminalPage::_isTrustedCommandline(cmdline); + } + + void TrustCommandlineTests::SimpleTests() + { + VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\cmd.exe")); + VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe")); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\i-definitely-don't-exist.exe")); + + Log::Comment(L"These are not fully qualified, and _shouldn't_ be trusted"); + VERIFY_IS_FALSE(trust(L"cmd.exe")); + VERIFY_IS_FALSE(trust(L"powershell.exe")); + } + + void TrustCommandlineTests::TestCommandlineWithArgs() + { + Log::Comment(L"These are sneaky things that _shouldn't_ be trusted"); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /k echo Boo!")); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /k echo Boo! & cmd.exe")); + } + + void TrustCommandlineTests::TestCommandlineWithSpaces() + { + Log::Comment(L"This is a valid place for powershell to live, and the space can be tricky"); + VERIFY_IS_TRUE(trust(L"C:\\Program Files\\PowerShell\\7\\pwsh.exe")); + + Log::Comment(L"These are sneaky things that _shouldn't_ be trusted"); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System 32\\cmd.exe")); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\ cmd.exe")); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /c cmd.exe")); + } + + void TrustCommandlineTests::TestCommandlineWithEnvVars() + { + Log::Comment(L"Make sure we auto-expand environment variables"); + + VERIFY_IS_TRUE(trust(L"%WINDIR%\\system32\\cmd.exe")); + VERIFY_IS_TRUE(trust(L"%WINDIR%\\system32\\WindowsPowerShell\\v1.0\\powershell.exe")); + VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe")); + } + + void TrustCommandlineTests::WslTests() + { + Log::Comment(L"We are explicitly deciding to not auto-approve " + L"`wsl.exe -d distro`-like commandlines. If we change this" + L" policy, remove this test."); + + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\wsl")); + VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\wsl.exe"), L"This we will trust though, since it's an exe in system32"); + VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\wsl.exe -d Ubuntu")); + VERIFY_IS_FALSE(trust(L"wsl.exe")); + } + + void TrustCommandlineTests::TestPwshLocation() + { + Log::Comment(L"Test various locations that pwsh.exe can be in"); + + VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe")); + VERIFY_IS_TRUE(trust(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\pwsh.exe")); + VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\10\\pwsh.exe")); + VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7.1.5\\pwsh.exe")); + + Log::Comment(L"These are sneaky things that _shouldn't_ be trusted"); + VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff pwsh.exe")); + VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff c:\\pwsh.exe")); + VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff c:\\ %ProgramFiles%\\PowerShell\\7\\pwsh.exe")); + } +} diff --git a/src/cascadia/TerminalApp/AdminWarningPlaceholder.cpp b/src/cascadia/TerminalApp/AdminWarningPlaceholder.cpp new file mode 100644 index 000000000..e2f53992d --- /dev/null +++ b/src/cascadia/TerminalApp/AdminWarningPlaceholder.cpp @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "pch.h" +#include "AdminWarningPlaceholder.h" +#include "AdminWarningPlaceholder.g.cpp" +#include +using namespace winrt::Windows::UI::Xaml; + +namespace winrt::TerminalApp::implementation +{ + AdminWarningPlaceholder::AdminWarningPlaceholder(const winrt::Microsoft::Terminal::Control::TermControl& control, const winrt::hstring& cmdline) : + _control{ control }, + _Commandline{ cmdline } + { + InitializeComponent(); + // If the content we're hosting is a TermControl, then use the control's + // BG as our BG, to give the impression that it's there, under the + // dialog. + if (const auto termControl{ control.try_as() }) + { + RootGrid().Background(termControl.BackgroundBrush()); + } + } + void AdminWarningPlaceholder::_primaryButtonClick(winrt::Windows::Foundation::IInspectable const& /*sender*/, + RoutedEventArgs const& e) + { + _PrimaryButtonClickedHandlers(*this, e); + } + void AdminWarningPlaceholder::_cancelButtonClick(winrt::Windows::Foundation::IInspectable const& /*sender*/, + RoutedEventArgs const& e) + { + _CancelButtonClickedHandlers(*this, e); + } + winrt::Windows::UI::Xaml::Controls::UserControl AdminWarningPlaceholder::Control() + { + return _control; + } + + // Method Description: + // - Move the focus to the cancel button by default. This has the LOAD + // BEARING side effect of also triggering Narrator to read out the + // contents of the dialog. It's unclear why doing this works, but it does. + // - Using a LayoutUpdated event to trigger the focus change when we're + // added to the UI tree did not seem to work. + // - Whoever is adding us to the UI tree is responsible for calling this! + // Arguments: + // - + // Return Value: + // - + void AdminWarningPlaceholder::FocusOnLaunch() + { + CancelButton().Focus(FocusState::Programmatic); + } + + winrt::hstring AdminWarningPlaceholder::ControlName() + { + return RS_(L"AdminWarningPlaceholderName"); + } + + void AdminWarningPlaceholder::_keyUpHandler(IInspectable const& /*sender*/, + Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) + { + // If the user presses escape, close the dialog (without confirming) + const auto key = e.OriginalKey(); + if (key == winrt::Windows::System::VirtualKey::Escape) + { + _CancelButtonClickedHandlers(*this, e); + e.Handled(true); + } + } +} diff --git a/src/cascadia/TerminalApp/AdminWarningPlaceholder.h b/src/cascadia/TerminalApp/AdminWarningPlaceholder.h new file mode 100644 index 000000000..a46e6c1cc --- /dev/null +++ b/src/cascadia/TerminalApp/AdminWarningPlaceholder.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- AdminWarningPlaceholder + +Abstract: +- The AdminWarningPlaceholder is a control used to fill space in a pane and + present a warning to the user. It holds on to a real control that it should be + replaced with. It looks just like a ContentDialog, except it exists per-pane, + whereas a ContentDialog can only be added to take up the whole window. +- The caller should make sure to bind our PrimaryButtonClicked and + CancelButtonClicked events, to be informed as to which was pressed. + +Author(s): +- Mike Griese - September 2021 + +--*/ + +#pragma once + +#include "AdminWarningPlaceholder.g.h" +#include "../../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::TerminalApp::implementation +{ + struct AdminWarningPlaceholder : AdminWarningPlaceholderT + { + AdminWarningPlaceholder(const winrt::Microsoft::Terminal::Control::TermControl& control, const winrt::hstring& cmdline); + void FocusOnLaunch(); + winrt::Windows::UI::Xaml::Controls::UserControl Control(); + winrt::hstring ControlName(); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Commandline, _PropertyChangedHandlers); + TYPED_EVENT(PrimaryButtonClicked, TerminalApp::AdminWarningPlaceholder, winrt::Windows::UI::Xaml::RoutedEventArgs); + TYPED_EVENT(CancelButtonClicked, TerminalApp::AdminWarningPlaceholder, winrt::Windows::UI::Xaml::RoutedEventArgs); + + private: + friend struct AdminWarningPlaceholderT; // friend our parent so it can bind private event handlers + + winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; + + void _primaryButtonClick(winrt::Windows::Foundation::IInspectable const& sender, + winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void _cancelButtonClick(winrt::Windows::Foundation::IInspectable const& sender, + winrt::Windows::UI::Xaml::RoutedEventArgs const& e); + void _keyUpHandler(Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e); + }; +} diff --git a/src/cascadia/TerminalApp/AdminWarningPlaceholder.idl b/src/cascadia/TerminalApp/AdminWarningPlaceholder.idl new file mode 100644 index 000000000..5cb315473 --- /dev/null +++ b/src/cascadia/TerminalApp/AdminWarningPlaceholder.idl @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + [default_interface] runtimeclass AdminWarningPlaceholder : Windows.UI.Xaml.Controls.UserControl, + Windows.UI.Xaml.Data.INotifyPropertyChanged + { + String Commandline { get; }; + String ControlName { get; }; + } +} diff --git a/src/cascadia/TerminalApp/AdminWarningPlaceholder.xaml b/src/cascadia/TerminalApp/AdminWarningPlaceholder.xaml new file mode 100644 index 000000000..ba8510188 --- /dev/null +++ b/src/cascadia/TerminalApp/AdminWarningPlaceholder.xaml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + +