Change Read-Host -MaskInput to use existing SecureString path, but return as plain text (#13256)

This commit is contained in:
Steve Lee
2020-07-30 22:16:37 -07:00
committed by GitHub
parent 2eade89f25
commit 99b3bfaa34
8 changed files with 31 additions and 135 deletions
@@ -130,9 +130,9 @@ namespace Microsoft.PowerShell.Commands
}
FieldDescription fd = new FieldDescription(promptString);
if (AsSecureString)
if (AsSecureString || MaskInput)
{
fd.SetParameterType(typeof(System.Security.SecureString));
fd.SetParameterType(typeof(SecureString));
}
else
{
@@ -149,27 +149,37 @@ namespace Microsoft.PowerShell.Commands
{
foreach (PSObject o in result.Values)
{
WriteObject(o);
if (MaskInput && o?.BaseObject is SecureString secureString)
{
WriteObject(Utils.GetStringFromSecureString(secureString));
}
else
{
WriteObject(o);
}
}
}
}
else
{
object result;
if (AsSecureString)
if (AsSecureString || MaskInput)
{
result = Host.UI.ReadLineAsSecureString();
}
else if (MaskInput)
{
result = Host.UI.ReadLineMaskedAsString();
}
else
{
result = Host.UI.ReadLine();
}
WriteObject(result);
if (MaskInput)
{
WriteObject(Utils.GetStringFromSecureString((SecureString)result));
}
else
{
WriteObject(result);
}
}
}
@@ -112,17 +112,6 @@ namespace Microsoft.PowerShell
throw new PSNotImplementedException();
}
/// <summary>
/// Null implementation of ReadLineMaskedAsString.
/// </summary>
/// <returns>
/// It throws an exception.
/// </returns>
public override string ReadLineMaskedAsString()
{
throw new PSNotImplementedException();
}
/// <summary>
/// ReadLineAsSecureString.
/// </summary>
@@ -179,39 +179,6 @@ namespace Microsoft.PowerShell
return ReadLine(false, string.Empty, out unused, true, true);
}
/// <summary>
/// See base class.
/// </summary>
/// <returns>
/// The characters typed by the user.
/// </returns>
/// <exception cref="HostException">
/// If obtaining a handle to the active screen buffer failed
/// OR
/// Win32's setting input buffer mode to disregard window and mouse input failed.
/// OR
/// Win32's ReadConsole failed.
/// </exception>
/// <exception cref="PipelineStoppedException">
/// If Ctrl-C is entered by user.
/// </exception>
public override string ReadLineMaskedAsString()
{
HandleThrowOnReadAndPrompt();
// we lock here so that multiple threads won't interleave the various reads and writes here.
object result = null;
lock (_instanceLock)
{
result = ReadLineSafe(false, PrintToken);
}
StringBuilder resultSb = result as StringBuilder;
Dbg.Assert(resultSb != null, "ReadLineMaskedAsString did not return a stringBuilder");
return resultSb.ToString();
}
/// <summary>
/// See base class.
/// </summary>
@@ -120,47 +120,6 @@ namespace System.Management.Automation.Internal.Host
return result;
}
/// <summary>
/// See base class.
/// </summary>
/// <returns>
/// The characters typed by the user.
/// </returns>
/// <exception cref="HostException">
/// If the UI property of the external host is null, possibly because the PSHostUserInterface is not
/// implemented by the external host.
/// </exception>
public override
string
ReadLineMaskedAsString()
{
if (_externalUI == null)
{
ThrowNotInteractive();
}
string result = null;
try
{
result = _externalUI.ReadLineMaskedAsString();
}
catch (PipelineStoppedException)
{
// PipelineStoppedException is thrown by host when it wants
// to stop the pipeline.
LocalPipeline lpl = (LocalPipeline)((RunspaceBase)_parent.Context.CurrentRunspace).GetCurrentlyRunningPipeline();
if (lpl == null)
{
throw;
}
lpl.Stopper.Stop();
}
return result;
}
/// <summary>
/// See base class.
/// </summary>
@@ -61,29 +61,6 @@ namespace System.Management.Automation.Host
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.Prompt"/>
public abstract string ReadLine();
/// <summary>
/// Same as ReadLine except that the input is not echoed to the user while it is collected
/// or is echoed in some obfuscated way, such as showing a dot for each character.
/// </summary>
/// <returns>
/// The characters typed by the user.
/// </returns>
/// <remarks>
/// Note that credentials (a user name and password) should be gathered with
/// <see cref="System.Management.Automation.Host.PSHostUserInterface.PromptForCredential(string, string, string, string)"/>
/// <see cref="System.Management.Automation.Host.PSHostUserInterface.PromptForCredential(string, string, string, string, System.Management.Automation.PSCredentialTypes, System.Management.Automation.PSCredentialUIOptions)"/>
/// </remarks>
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.ReadLine"/>
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.PromptForCredential(string, string, string, string)"/>
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.PromptForCredential(string, string, string, string, System.Management.Automation.PSCredentialTypes, System.Management.Automation.PSCredentialUIOptions)"/>
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.PromptForChoice"/>
/// <seealso cref="System.Management.Automation.Host.PSHostUserInterface.Prompt"/>
public virtual string ReadLineMaskedAsString()
{
// Default implementation of the function to maintain backwards compatibility of the base class.
throw new PSNotImplementedException();
}
/// <summary>
/// Same as ReadLine, except that the result is a SecureString, and that the input is not echoed to the user while it is
/// collected (or is echoed in some obfuscated way, such as showing a dot for each character).
@@ -197,17 +197,6 @@ namespace System.Management.Automation.Remoting
_serverMethodExecutor.ExecuteVoidMethod(RemoteHostMethodId.WriteWarningLine, new object[] { message });
}
/// <summary>
/// Read line as string masked.
/// </summary>
/// <returns>
/// Not implemented. It throws an exception.
/// </returns>
public override string ReadLineMaskedAsString()
{
throw new PSNotImplementedException();
}
/// <summary>
/// Read line as secure string.
/// </summary>
@@ -12,6 +12,7 @@ Describe "Read-Host Test" -Tag "CI" {
AfterEach {
$ps.Commands.Clear()
$th.UI.Streams.Clear()
}
AfterAll {
@@ -30,17 +31,26 @@ Describe "Read-Host Test" -Tag "CI" {
$prompt = $th.ui.streams.prompt[0]
$prompt | Should -Not -BeNullOrEmpty
$prompt.split(":")[-1] | Should -Be myprompt
$result | Should -BeExactly 'this is a prompt response'
}
It "Read-Host returns a secure string when using -AsSecureString parameter" {
$result = $ps.AddScript("Read-Host -AsSecureString").Invoke() | Select-Object -First 1
$result | Should -BeOfType SecureString
[pscredential]::New("foo",$result).GetNetworkCredential().Password | Should -BeExactly TEST
$result | ConvertFrom-SecureString -AsPlainText | Should -BeExactly 'TEST'
}
It "Read-Host returns a string when using -MaskInput parameter" {
$result = $ps.AddScript("Read-Host -MaskInput").Invoke()
$result | Should -Be $th.UI.ReadLineData
$result | Should -BeExactly 'TEST'
}
It "Read-Host returns a string when using -MaskInput parameter used with -Prompt" {
$result = $ps.AddScript("Read-Host -MaskInput -Prompt Test").Invoke()
$prompt = $th.ui.streams.prompt[0]
$prompt | Should -Not -BeNullOrEmpty
$prompt.split(":")[-1] | Should -BeExactly 'Test'
$result | Should -BeExactly 'this is a prompt response'
}
It "Read-Host throws an error when both -AsSecureString parameter and -MaskInput parameter are used" {
@@ -183,11 +183,6 @@ namespace TestHost
return ReadLineData;
}
public override string ReadLineMaskedAsString()
{
return ReadLineData;
}
public override SecureString ReadLineAsSecureString()
{
SecureString ss = new SecureString();