Only open File Explorer from OpenCWD when CWD is a real directory (#20697)

OpenCWD now no-ops unless the terminal working directory is an existing
directory, then opens that folder. It no longer passes the raw OSC
string to `explorer.exe` as parameters.

Refs #12859 (the original action asked to do nothing when no CWD is set)

OpenCWD was introduced in #18013.

`ControlCore::OpenCWD` is the command-palette action `Terminal.OpenCWD`.
It used:

```cpp
ShellExecute(nullptr, nullptr, L"explorer", workingDirectory.c_str(), nullptr, SW_SHOW);
```

The working-directory string comes from OSC 7 (`file:` URI, then
`PathCreateFromUrlW`) or OSC 9;9. Both only require
`til::is_legal_path`, which allows `:` and `/`. So a remote host can set
CWD to `https://example.com`, `shell:...`, or `/select,...`. Invoking
Open CWD then ran Explorer with that text as `lpParameters`.

Duplicate-tab and related paths already call `Utils::IsValidDirectory`
before trusting this string (`TerminalPage.cpp`,
`TerminalPaneContent.cpp`). Open CWD now does the same, then
`ShellExecute`s the directory path as `lpFile` so Explorer switches
cannot be supplied as parameters.

If the CWD is empty or not a directory, the action does nothing.

(cherry picked from commit 4e2b8bd926)
Service-Card-Id: PVTI_lADOAF3p4s4BlIihzg9jO10
Service-Version: 1.26
This commit is contained in:
Sebastien Tardif
2026-09-29 17:14:35 -07:00
committed by Dustin L. Howett
parent f3c602702a
commit dafb04fb62
2 changed files with 26 additions and 1 deletions

View File

@@ -1745,7 +1745,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::OpenCWD()
{
const auto workingDirectory = WorkingDirectory();
ShellExecute(nullptr, nullptr, L"explorer", workingDirectory.c_str(), nullptr, SW_SHOW);
if (!Utils::IsValidDirectory(workingDirectory.c_str()))
{
return;
}
ShellExecute(nullptr, nullptr, workingDirectory.c_str(), nullptr, nullptr, SW_SHOW);
}
void ControlCore::ClearQuickFix()

View File

@@ -34,6 +34,7 @@ class UtilsTests
TEST_METHOD(TestDontTrimTrailingWhitespace);
TEST_METHOD(TestEvaluateStartingDirectory);
TEST_METHOD(TestIsValidDirectory);
void _VerifyXTermColorResult(const std::wstring_view wstr, DWORD colorValue);
void _VerifyXTermColorInvalid(const std::wstring_view wstr);
@@ -618,3 +619,23 @@ void UtilsTests::TestEvaluateStartingDirectory()
test(L"/dev", cwd, L"/dev");
}
}
void UtilsTests::TestIsValidDirectory()
{
VERIFY_IS_FALSE(IsValidDirectory(nullptr));
VERIFY_IS_FALSE(IsValidDirectory(L""));
// OSC 7 / 9;9 can store these. They are not directories.
VERIFY_IS_FALSE(IsValidDirectory(L"https://example.com"));
VERIFY_IS_FALSE(IsValidDirectory(L"/select,C:\\Windows"));
VERIFY_IS_FALSE(IsValidDirectory(L"shell:AppsFolder\\Foo"));
wchar_t tempPath[MAX_PATH]{};
VERIFY_IS_GREATER_THAN(GetTempPathW(ARRAYSIZE(tempPath), tempPath), 0u);
VERIFY_IS_TRUE(IsValidDirectory(tempPath));
wchar_t tempFile[MAX_PATH]{};
VERIFY_ARE_NOT_EQUAL(0u, GetTempFileNameW(tempPath, L"ut", 0, tempFile));
VERIFY_IS_FALSE(IsValidDirectory(tempFile));
DeleteFileW(tempFile);
}