diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 95d8ea17f..e7ebfbedb 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..55a46e6d4 --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#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(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")); + + VERIFY_IS_FALSE(trust(L"cmd.exe")); + VERIFY_IS_FALSE(trust(L"powershell.exe")); + } + + void TrustCommandlineTests::TestCommandlineWithArgs() + { + 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() + { + VERIFY_IS_TRUE(false, L"TODO! implement me."); + } + void TrustCommandlineTests::WslTests() + { + VERIFY_IS_TRUE(false, L"TODO! implement me."); + } + void TrustCommandlineTests::TestPwshLocation() + { + VERIFY_IS_TRUE(false, L"TODO! implement me."); + } +} diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 70cd3497a..f47d199fd 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1506,7 +1506,7 @@ namespace winrt::TerminalApp::implementation // - C:\windows\system32\cmd.exe /k echo sneaky sneak -> returns false // - %SystemRoot%\System32\cmd.exe -> returns true // - %SystemRoot%\System32\wsl.exe -d -> returns true - static bool _isTrustedCommandline(std::wstring_view commandLine) + bool TerminalPage::_isTrustedCommandline(std::wstring_view commandLine) { // use C++11 magic statics to make sure we only do this once. static std::wstring systemDirectory = []() -> std::wstring { @@ -1544,6 +1544,13 @@ namespace winrt::TerminalApp::implementation } } + // TODO! Remove the WSL allowing. it's trivial to insert some malicious + // stuff into WSL, via .bash_profile, so we're not giving them the (y) + + // TODO! CommandlineToArgv to get the executable from the commandline. + // If there's one argc, and it's parent path is %ProgramFiles%, and it + // ends in pwsh.exe, then it's fine. + // Also, if the path is literally // %SystemRoot%\System32\wsl.exe -d // then allow it. @@ -1601,27 +1608,33 @@ namespace winrt::TerminalApp::implementation else if (executableFilename == L"pwsh" || executableFilename == L"pwsh.exe") { // is does executablePath start with %ProgramFiles%\\PowerShell? - const std::filesystem::path powershellCoreRoot + const std::vector powershellCoreRoots { - wil::ExpandEnvironmentStringsW( + // Always look in "%ProgramFiles% + { wil::ExpandEnvironmentStringsW(L"%ProgramFiles%\\PowerShell") }, + #if defined(_M_AMD64) || defined(_M_ARM64) // No point in looking for WOW if we're not somewhere it exists - L"%ProgramFiles(x86)%\\PowerShell" -#elif defined(_M_ARM64) // same with ARM - L"%ProgramFiles(Arm)%\\PowerShell" -#else - L"%ProgramFiles%\\PowerShell" + { wil::ExpandEnvironmentStringsW(L"%ProgramFiles(x86)%\\PowerShell") }, +#endif + +#if defined(_M_ARM64) // same with ARM + { + wil::ExpandEnvironmentStringsW(L"%ProgramFiles(Arm)%\\PowerShell") + } #endif - ) }; - // Is the path to the commandline actually exactly one of the - // versions that exists in this directory? - for (const auto& versionedDir : std::filesystem::directory_iterator(powershellCoreRoot)) + for (const auto& pwshRoot : powershellCoreRoots) { - const auto versionedPath = versionedDir.path(); - if (executablePath.parent_path() == versionedPath) + // Is the path to the commandline actually exactly one of the + // versions that exists in this directory? + for (const auto& versionedDir : std::filesystem::directory_iterator(pwshRoot)) { - return true; + const auto versionedPath = versionedDir.path(); + if (executablePath.parent_path() == versionedPath) + { + return true; + } } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 2d3efb9c0..0689743c4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -19,6 +19,7 @@ namespace TerminalAppLocalTests { class TabTests; class SettingsTests; + class TrustCommandlineTests; }; namespace winrt::TerminalApp::implementation @@ -417,6 +418,7 @@ namespace winrt::TerminalApp::implementation void _SetAsDefaultOpenSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); + static bool _isTrustedCommandline(std::wstring_view commandLine); #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp @@ -427,6 +429,7 @@ namespace winrt::TerminalApp::implementation friend class TerminalAppLocalTests::TabTests; friend class TerminalAppLocalTests::SettingsTests; + friend class TerminalAppLocalTests::TrustCommandlineTests; }; }