diff --git a/wsl-pro-service/internal/system/landscape.go b/wsl-pro-service/internal/system/landscape.go index 16f5d67d0..b99fa540e 100644 --- a/wsl-pro-service/internal/system/landscape.go +++ b/wsl-pro-service/internal/system/landscape.go @@ -170,6 +170,11 @@ func overrideSSLCertificate(ctx context.Context, s *System, section *ini.Section pathWindows := k.String() + if len(pathWindows) == 0 { + // Empty paths are translated by wslpath as the current working directory, which is not what we want. + return nil + } + cmd := s.backend.WslpathExecutable(ctx, "-ua", pathWindows) out, err := runCommand(cmd) if err != nil { diff --git a/wsl-pro-service/internal/system/system.go b/wsl-pro-service/internal/system/system.go index e38cc5d3d..132b94771 100644 --- a/wsl-pro-service/internal/system/system.go +++ b/wsl-pro-service/internal/system/system.go @@ -184,16 +184,22 @@ func (s *System) UserProfileDir(ctx context.Context) (wslPath string, err error) return wslPath, err } - cmd := s.backend.CmdExe(ctx, cmdExe, "/C", "echo %UserProfile%") + // Using the 'echo.' syntax instead of 'echo ' because if %USERPROFILE% was set to empty string it would cause the output to be 'ECHO is on'. + // With 'echo.%UserProfile%' it correctly prints empty line in that case. + cmd := s.backend.CmdExe(ctx, cmdExe, "/C", "echo.%UserProfile%") winHome, err := runCommand(cmd) if err != nil { return wslPath, err } + trimmed := strings.TrimSpace(string(winHome)) + if len(trimmed) == 0 { + return wslPath, errors.New("%UserProfile% value is empty") + } // We have the path from Windows' perspective ( C:\Users\... ) // It must be converted to linux ( /mnt/c/Users/... ) - cmd = s.backend.WslpathExecutable(ctx, "-ua", string(winHome)) + cmd = s.backend.WslpathExecutable(ctx, "-ua", trimmed) winHomeLinux, err := runCommand(cmd) if err != nil { return wslPath, err diff --git a/wsl-pro-service/internal/system/system_test.go b/wsl-pro-service/internal/system/system_test.go index ad65860e8..2e27c01ee 100644 --- a/wsl-pro-service/internal/system/system_test.go +++ b/wsl-pro-service/internal/system/system_test.go @@ -199,12 +199,13 @@ func TestUserProfileDir(t *testing.T) { t.Parallel() testCases := map[string]struct { - cachedCmdExe bool - cmdExeNotExist bool - cmdExeErr bool - wslpathErr bool - wslpathBadOutput bool - overrideProcMount bool + cachedCmdExe bool + cmdExeNotExist bool + cmdExeErr bool + emptyUserprofileEnvVar bool + wslpathErr bool + wslpathBadOutput bool + overrideProcMount bool wantErr bool }{ @@ -218,6 +219,7 @@ func TestUserProfileDir(t *testing.T) { "Error finding cmd.exe because there is no Windows FS in /proc/mounts": {wantErr: true, overrideProcMount: true}, "Error when cmd.exe does not exist": {cmdExeNotExist: true, overrideProcMount: true, wantErr: true}, "Error on cmd.exe error": {cmdExeErr: true, wantErr: true}, + "Error when UserProfile env var is empty": {emptyUserprofileEnvVar: true, wantErr: true}, "Error on wslpath error": {wslpathErr: true, wantErr: true}, "Error when wslpath returns a bad path": {wslpathBadOutput: true, wantErr: true}, } @@ -230,6 +232,9 @@ func TestUserProfileDir(t *testing.T) { if tc.cmdExeErr { mock.SetControlArg(testutils.CmdExeErr) } + if tc.emptyUserprofileEnvVar { + mock.SetControlArg(testutils.EmptyUserprofileEnvVar) + } if tc.wslpathErr { mock.SetControlArg(testutils.WslpathErr) } @@ -251,7 +256,7 @@ func TestUserProfileDir(t *testing.T) { got, err := system.UserProfileDir(context.Background()) if tc.wantErr { - require.Error(t, err, "Expected UserProfile to return an error") + require.Error(t, err, "Expected UserProfile to return an error, but returned %s intead", got) return } require.NoError(t, err, "Expected UserProfile to return no errors") @@ -669,6 +674,7 @@ func TestEnsureValidLandscapeConfig(t *testing.T) { "Appends any required fields - no ssl": {systemLandscapeConfigFile: "minimal.conf"}, "Transform Windows SSL certificate path": {systemLandscapeConfigFile: "windows_ssl_only.conf"}, "Transform Windows SSL certificate path with forward slash": {systemLandscapeConfigFile: "windows_ssl_only_forward_slash.conf"}, + "Do not transform Windows SSL certificate empty path": {systemLandscapeConfigFile: "windows_ssl_empty.conf", wantNoLandscapeConfigCmd: true}, "Refresh computer_title if changed": {systemLandscapeConfigFile: "old_computer_title.conf"}, "Regular with additional keys": {systemLandscapeConfigFile: "regular.conf"}, diff --git a/wsl-pro-service/internal/system/testdata/TestEnsureValidLandscapeConfig/golden/do_not_transform_windows_ssl_certificate_empty_path b/wsl-pro-service/internal/system/testdata/TestEnsureValidLandscapeConfig/golden/do_not_transform_windows_ssl_certificate_empty_path new file mode 100644 index 000000000..51110144a --- /dev/null +++ b/wsl-pro-service/internal/system/testdata/TestEnsureValidLandscapeConfig/golden/do_not_transform_windows_ssl_certificate_empty_path @@ -0,0 +1,4 @@ +[client] +hostagent_uid = landscapeUID1234 +ssl_public_key = +computer_title = TEST_DISTRO diff --git a/wsl-pro-service/internal/system/testdata/landscape.conf.d/windows_ssl_empty.conf b/wsl-pro-service/internal/system/testdata/landscape.conf.d/windows_ssl_empty.conf new file mode 100644 index 000000000..51110144a --- /dev/null +++ b/wsl-pro-service/internal/system/testdata/landscape.conf.d/windows_ssl_empty.conf @@ -0,0 +1,4 @@ +[client] +hostagent_uid = landscapeUID1234 +ssl_public_key = +computer_title = TEST_DISTRO diff --git a/wsl-pro-service/internal/testutils/mock_executables.go b/wsl-pro-service/internal/testutils/mock_executables.go index a04f14265..aced47103 100644 --- a/wsl-pro-service/internal/testutils/mock_executables.go +++ b/wsl-pro-service/internal/testutils/mock_executables.go @@ -86,8 +86,9 @@ const ( LandscapeEnableErr = "UP4W_LANDSCAPE_ENABLE_ERR" LandscapeDisableErr = "UP4W_LANDSCAPE_DISABLE_ERR" - WslpathErr = "UP4W_WSLPATH_ERR" - WslpathBadOutput = "UP4W_WSLPATH_BAD_OUTPUT" + WslpathErr = "UP4W_WSLPATH_ERR" + WslpathBadOutput = "UP4W_WSLPATH_BAD_OUTPUT" + EmptyUserprofileEnvVar = "UP4W_EMPTY_USERPROFILE_ENV_VAR" CmdExeErr = "UP4W_CMDEXE_ERR" @@ -511,12 +512,18 @@ func WslPathMock(t *testing.T) { if envExists(WslpathErr) { return exitError } + // That's what wslpath returns when it's called with -ua followed by an empty string. + cwd, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "Could not get current working directory: %v", err) + } stdout, ok := map[string]string{ windowsUserProfileDir: linuxUserProfileDir, `D:\Users\TestUser\certificate`: filepath.Join(defaultWindowsMount, "Users/TestUser/certificate"), - `D:/Users/TestUser/certificate`: filepath.Join(defaultWindowsMount, "Users/TestUser/certificate"), - `/idempotent/path/to/linux/certificate`: `/idempotent/path/to/linux/certificate`, + "D:/Users/TestUser/certificate": filepath.Join(defaultWindowsMount, "Users/TestUser/certificate"), + "/idempotent/path/to/linux/certificate": "/idempotent/path/to/linux/certificate", + "": cwd, }[argv[1]] if !ok { @@ -602,7 +609,7 @@ func CmdExeMock(t *testing.T) { return exitBadUsage } - if argv[1] != "echo %UserProfile%" { + if argv[1] != "echo.%UserProfile%" { fmt.Fprintf(os.Stderr, "Mock not implemented for args %q\n", argv) return exitBadUsage } @@ -610,6 +617,10 @@ func CmdExeMock(t *testing.T) { if envExists(CmdExeErr) { return exitError } + if envExists(EmptyUserprofileEnvVar) { + fmt.Print("\r\n") + return exitOk + } fmt.Fprintln(os.Stdout, windowsUserProfileDir) return exitOk