From f7b2a485ee73e5c870e62bfb785d465f406419f1 Mon Sep 17 00:00:00 2001 From: Beena Chauhan Date: Fri, 12 Jun 2026 13:58:50 -0700 Subject: [PATCH 1/2] Fix driver and network validation gaps: reject empty driver names, empty driver option keys, and empty primary network in alias guard --- localization/strings/en-US/Resources.resw | 7 +++++ .../wslc/arguments/ArgumentValidation.cpp | 22 +++++++++++++-- .../wslc/services/ContainerService.cpp | 2 +- .../wslc/WSLCCLIOptionsParserUnitTests.cpp | 28 +++++++++++++++++-- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 7e3f675aed..92c9b3c140 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3107,6 +3107,13 @@ On first run, creates the file with all settings commented out at their defaults Specify volume driver name, e.g. 'guest' or 'vhd' (default: guest) {Locked="guest"}{Locked="vhd"}Command line arguments, file names and string inserts should not be translated + + Invalid {} value: driver name cannot be empty or whitespace + {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated + + + Driver option key cannot be empty + Set driver specific options diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp index 7f507ea37a..0a4a01e06b 100644 --- a/src/windows/wslc/arguments/ArgumentValidation.cpp +++ b/src/windows/wslc/arguments/ArgumentValidation.cpp @@ -106,6 +106,17 @@ void Argument::Validate(const ArgMap& execArgs) const break; } + case ArgType::Driver: + { + const auto& value = execArgs.Get(); + if (value.empty() || + std::all_of(value.begin(), value.end(), [](wchar_t c) { return std::iswspace(static_cast(c)); })) + { + throw ArgumentException(Localization::WSLCCLI_DriverEmptyError(m_name)); + } + break; + } + case ArgType::Network: { for (const auto& value : execArgs.GetAll()) @@ -413,13 +424,20 @@ std::pair ParseLabel(const std::wstring& value) std::pair ParseDriverOption(const std::wstring& value) { + std::pair result{}; auto pos = value.find('='); if (pos == std::wstring::npos) { - return {WideToMultiByte(value), std::string{}}; + result.first = WideToMultiByte(value); + } + else + { + result.first = WideToMultiByte(value.substr(0, pos)); + result.second = WideToMultiByte(value.substr(pos + 1)); } - return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))}; + THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::WSLCCLI_DriverOptionKeyEmptyError(), result.first.empty()); + return result; } } // namespace wsl::windows::wslc::validation diff --git a/src/windows/wslc/services/ContainerService.cpp b/src/windows/wslc/services/ContainerService.cpp index 1015762ecc..7ea43b075e 100644 --- a/src/windows/wslc/services/ContainerService.cpp +++ b/src/windows/wslc/services/ContainerService.cpp @@ -72,7 +72,7 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal( THROW_HR_WITH_USER_ERROR_IF( E_INVALIDARG, Localization::MessageWslcAliasRequiresUserDefinedNetwork(), - primary == "bridge" || primary == "host" || primary == "none" || primary.starts_with("container:")); + primary.empty() || primary == "bridge" || primary == "host" || primary == "none" || primary.starts_with("container:")); for (const auto& alias : options.NetworkAliases) { diff --git a/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp b/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp index fda0257e9f..40a3c24c91 100644 --- a/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp +++ b/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp @@ -30,8 +30,6 @@ class WSLCCLIOptionsParserUnitTests {L"SizeBytes=3145728", "SizeBytes", "3145728"}, {L"ReadOnly", "ReadOnly", ""}, {L"key=", "key", ""}, - {L"=value", "", "value"}, - {L"", "", ""}, {L"key=a=b=c", "key", "a=b=c"}, }; @@ -42,6 +40,32 @@ class WSLCCLIOptionsParserUnitTests VERIFY_ARE_EQUAL(expectedValue, result.second); } } + + TEST_METHOD(WSLCCLIOptionsParser_InvalidOptions) + { + std::vector invalidOptions = { + L"", + L"=", + L"=value", + }; + + for (const auto& input : invalidOptions) + { + try + { + (void)validation::ParseDriverOption(input); + VERIFY_FAIL(L"Expected exception"); + } + catch (const wil::ResultException& ex) + { + VERIFY_ARE_EQUAL(E_INVALIDARG, ex.GetErrorCode()); + + const auto raw = ex.GetFailureInfo().pszMessage; + std::wstring message = raw ? raw : L""; + VERIFY_ARE_EQUAL(L"Driver option key cannot be empty", message); + } + } + } }; } // namespace WSLCCLIOptionsParserUnitTests From 7005ef0d9d3281e643f54f65ae9bd4d986e5bd1e Mon Sep 17 00:00:00 2001 From: Beena Chauhan Date: Fri, 12 Jun 2026 14:58:21 -0700 Subject: [PATCH 2/2] Revert ParseDriverOption empty-key rejection: Docker allows empty keys by design --- localization/strings/en-US/Resources.resw | 3 -- .../wslc/arguments/ArgumentValidation.cpp | 11 ++------ .../wslc/WSLCCLIOptionsParserUnitTests.cpp | 28 ++----------------- 3 files changed, 4 insertions(+), 38 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 92c9b3c140..a114b29d02 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3111,9 +3111,6 @@ On first run, creates the file with all settings commented out at their defaults Invalid {} value: driver name cannot be empty or whitespace {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated - - Driver option key cannot be empty - Set driver specific options diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp index 0a4a01e06b..af378cb59b 100644 --- a/src/windows/wslc/arguments/ArgumentValidation.cpp +++ b/src/windows/wslc/arguments/ArgumentValidation.cpp @@ -424,20 +424,13 @@ std::pair ParseLabel(const std::wstring& value) std::pair ParseDriverOption(const std::wstring& value) { - std::pair result{}; auto pos = value.find('='); if (pos == std::wstring::npos) { - result.first = WideToMultiByte(value); - } - else - { - result.first = WideToMultiByte(value.substr(0, pos)); - result.second = WideToMultiByte(value.substr(pos + 1)); + return {WideToMultiByte(value), std::string{}}; } - THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::WSLCCLI_DriverOptionKeyEmptyError(), result.first.empty()); - return result; + return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))}; } } // namespace wsl::windows::wslc::validation diff --git a/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp b/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp index 40a3c24c91..fda0257e9f 100644 --- a/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp +++ b/test/windows/wslc/WSLCCLIOptionsParserUnitTests.cpp @@ -30,6 +30,8 @@ class WSLCCLIOptionsParserUnitTests {L"SizeBytes=3145728", "SizeBytes", "3145728"}, {L"ReadOnly", "ReadOnly", ""}, {L"key=", "key", ""}, + {L"=value", "", "value"}, + {L"", "", ""}, {L"key=a=b=c", "key", "a=b=c"}, }; @@ -40,32 +42,6 @@ class WSLCCLIOptionsParserUnitTests VERIFY_ARE_EQUAL(expectedValue, result.second); } } - - TEST_METHOD(WSLCCLIOptionsParser_InvalidOptions) - { - std::vector invalidOptions = { - L"", - L"=", - L"=value", - }; - - for (const auto& input : invalidOptions) - { - try - { - (void)validation::ParseDriverOption(input); - VERIFY_FAIL(L"Expected exception"); - } - catch (const wil::ResultException& ex) - { - VERIFY_ARE_EQUAL(E_INVALIDARG, ex.GetErrorCode()); - - const auto raw = ex.GetFailureInfo().pszMessage; - std::wstring message = raw ? raw : L""; - VERIFY_ARE_EQUAL(L"Driver option key cannot be empty", message); - } - } - } }; } // namespace WSLCCLIOptionsParserUnitTests