From f880cf1997bce1c730590a1968e693a7ab07e64a Mon Sep 17 00:00:00 2001 From: Quoc Truong Date: Tue, 1 Dec 2015 16:45:28 -0800 Subject: [PATCH 1/2] Add an extra check for CredentialAttribute for the credential rules --- Rules/AvoidUserNameAndPasswordParams.cs | 8 +++++++ Rules/Strings.Designer.cs | 10 ++++----- Rules/Strings.resx | 10 ++++----- Rules/UsePSCredentialType.cs | 13 +++++++++--- .../AvoidUserNameAndPasswordParams.tests.ps1 | 2 +- ...dUserNameAndPasswordParamsNoViolations.ps1 | 21 +++++++++++++++++++ Tests/Rules/PSCredentialType.tests.ps1 | 2 +- Tests/Rules/PSCredentialTypeNoViolations.ps1 | 18 +++++++++++++++- 8 files changed, 68 insertions(+), 16 deletions(-) diff --git a/Rules/AvoidUserNameAndPasswordParams.cs b/Rules/AvoidUserNameAndPasswordParams.cs index 823ca6e3c..de76533de 100644 --- a/Rules/AvoidUserNameAndPasswordParams.cs +++ b/Rules/AvoidUserNameAndPasswordParams.cs @@ -11,6 +11,7 @@ // using System; +using System.Linq; using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Language; @@ -55,11 +56,18 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) TypeInfo paramType = (TypeInfo)paramAst.StaticType; String paramName = paramAst.Name.VariablePath.ToString(); + // if this is pscredential type, skip if (paramType == typeof(PSCredential) || (paramType.IsArray && paramType.GetElementType() == typeof (PSCredential))) { continue; } + // if this has credential attribute, skip + if (paramAst.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + { + continue; + } + foreach (String password in passwords) { if (paramName.IndexOf(password, StringComparison.OrdinalIgnoreCase) != -1) diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 448fb0efe..0a0cd2095 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -421,7 +421,7 @@ internal static string AvoidUsernameAndPasswordParamsCommonName { } /// - /// Looks up a localized string similar to Functions should only take in a credential parameter of type PSCredential instead of username and password parameters.. + /// Looks up a localized string similar to Functions should only take in a credential parameter of type PSCredential or has a CredentialAttribute instead of username and password parameters.. /// internal static string AvoidUsernameAndPasswordParamsDescription { get { @@ -430,7 +430,7 @@ internal static string AvoidUsernameAndPasswordParamsDescription { } /// - /// Looks up a localized string similar to Function '{0}' has both username and password parameters. A credential parameter of type PSCredential should be used.. + /// Looks up a localized string similar to Function '{0}' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used.. /// internal static string AvoidUsernameAndPasswordParamsError { get { @@ -1771,7 +1771,7 @@ internal static string UsePSCredentialTypeCommonName { } /// - /// Looks up a localized string similar to Checks that cmdlets that have a Credential parameter accept PSCredential. This comes from the PowerShell teams best practices.. + /// Looks up a localized string similar to Checks that cmdlets that have a Credential parameter accept PSCredential or has a CredentialAttribute. This comes from the PowerShell teams best practices.. /// internal static string UsePSCredentialTypeDescription { get { @@ -1780,7 +1780,7 @@ internal static string UsePSCredentialTypeDescription { } /// - /// Looks up a localized string similar to The Credential parameter in '{0}' must be of the type PSCredential.. + /// Looks up a localized string similar to The Credential parameter in '{0}' must be of the type PSCredential or has a CredentialAttribute.. /// internal static string UsePSCredentialTypeError { get { @@ -1789,7 +1789,7 @@ internal static string UsePSCredentialTypeError { } /// - /// Looks up a localized string similar to The Credential parameter in a found script block must be of the type PSCredential.. + /// Looks up a localized string similar to The Credential parameter in a found script block must be of the type PSCredential or has a CredentialAttribute.. /// internal static string UsePSCredentialTypeErrorSB { get { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index f297f16b0..b4150441a 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -217,13 +217,13 @@ One Char - Checks that cmdlets that have a Credential parameter accept PSCredential. This comes from the PowerShell teams best practices. + Checks that cmdlets that have a Credential parameter accept PSCredential or has a CredentialAttribute. This comes from the PowerShell teams best practices. - The Credential parameter in '{0}' must be of the type PSCredential. + The Credential parameter in '{0}' must be of the type PSCredential or has a CredentialAttribute. - The Credential parameter in a found script block must be of the type PSCredential. + The Credential parameter in a found script block must be of the type PSCredential or has a CredentialAttribute. PSCredential @@ -511,10 +511,10 @@ Avoid Using Username and Password Parameters - Functions should only take in a credential parameter of type PSCredential instead of username and password parameters. + Functions should only take in a credential parameter of type PSCredential or has a CredentialAttribute instead of username and password parameters. - Function '{0}' has both username and password parameters. A credential parameter of type PSCredential should be used. + Function '{0}' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used. AvoidUsingUserNameAndPassWordParams diff --git a/Rules/UsePSCredentialType.cs b/Rules/UsePSCredentialType.cs index 99ddbd70f..df19a028a 100644 --- a/Rules/UsePSCredentialType.cs +++ b/Rules/UsePSCredentialType.cs @@ -11,6 +11,7 @@ // using System; +using System.Linq; using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Language; @@ -50,7 +51,9 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in funcDefAst.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential)) + if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) + && parameter.StaticType != typeof(PSCredential) + && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -61,7 +64,9 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in funcDefAst.Body.ParamBlock.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential)) + if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) + && parameter.StaticType != typeof(PSCredential) + && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -75,7 +80,9 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in scriptBlockAst.ParamBlock.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential)) + if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) + && parameter.StaticType != typeof(PSCredential) + && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeErrorSB), scriptBlockAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } diff --git a/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 b/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 index 14f567160..0f383927a 100644 --- a/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 +++ b/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 @@ -1,6 +1,6 @@ Import-Module PSScriptAnalyzer -$violationMessage = "Function 'Verb-Noun' has both username and password parameters. A credential parameter of type PSCredential should be used." +$violationMessage = "Function 'Verb-Noun' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used." $violationName = "PSAvoidUsingUserNameAndPasswordParams" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\AvoidUserNameAndPasswordParams.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 b/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 index f964458cf..1ed59f853 100644 --- a/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 +++ b/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 @@ -11,3 +11,24 @@ function MyFunction2 ($param1, $passwords) function MyFunction3 ([PSCredential]$username, $passwords) { } + +function MyFunction4 +{ + [CmdletBinding()] + [Alias()] + [OutputType([int])] + Param + ( + # Param1 help description + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=0)] + [System.Management.Automation.CredentialAttribute()] + $UserName, + + # Param2 help description + [int] + [System.Management.Automation.CredentialAttribute()] + $Password + ) +} diff --git a/Tests/Rules/PSCredentialType.tests.ps1 b/Tests/Rules/PSCredentialType.tests.ps1 index 50a5f083c..8e09ea6db 100644 --- a/Tests/Rules/PSCredentialType.tests.ps1 +++ b/Tests/Rules/PSCredentialType.tests.ps1 @@ -1,5 +1,5 @@ Import-Module PSScriptAnalyzer -$violationMessage = "The Credential parameter in 'Credential' must be of the type PSCredential." +$violationMessage = "The Credential parameter in 'Credential' must be of the type PSCredential or has a CredentialAttribute." $violationName = "PSUsePSCredentialType" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\PSCredentialType.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/Rules/PSCredentialTypeNoViolations.ps1 b/Tests/Rules/PSCredentialTypeNoViolations.ps1 index ebe613883..62368066d 100644 --- a/Tests/Rules/PSCredentialTypeNoViolations.ps1 +++ b/Tests/Rules/PSCredentialTypeNoViolations.ps1 @@ -1,3 +1,19 @@ function Credential([pscredential]$credential) { -} \ No newline at end of file +} + +function Credential2 +{ + [CmdletBinding()] + [Alias()] + [OutputType([int])] + Param + ( + # Param1 help description + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=0)] + [System.Management.Automation.CredentialAttribute()] + $Credential + ) +} From 21269c811a84036d2d96b53f508da8f981fca944 Mon Sep 17 00:00:00 2001 From: Quoc Truong Date: Wed, 2 Dec 2015 10:50:16 -0800 Subject: [PATCH 2/2] Change logic to check for both CredentialAttribute and PSCredential type --- Rules/AvoidUserNameAndPasswordParams.cs | 11 ++----- Rules/Strings.Designer.cs | 10 +++--- Rules/Strings.resx | 10 +++--- Rules/UsePSCredentialType.cs | 31 +++++++++++++------ .../AvoidUserNameAndPasswordParams.tests.ps1 | 2 +- ...dUserNameAndPasswordParamsNoViolations.ps1 | 9 ++---- Tests/Rules/PSCredentialType.tests.ps1 | 2 +- Tests/Rules/PSCredentialTypeNoViolations.ps1 | 9 ++---- 8 files changed, 43 insertions(+), 41 deletions(-) diff --git a/Rules/AvoidUserNameAndPasswordParams.cs b/Rules/AvoidUserNameAndPasswordParams.cs index de76533de..422b58080 100644 --- a/Rules/AvoidUserNameAndPasswordParams.cs +++ b/Rules/AvoidUserNameAndPasswordParams.cs @@ -56,14 +56,9 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) TypeInfo paramType = (TypeInfo)paramAst.StaticType; String paramName = paramAst.Name.VariablePath.ToString(); - // if this is pscredential type, skip - if (paramType == typeof(PSCredential) || (paramType.IsArray && paramType.GetElementType() == typeof (PSCredential))) - { - continue; - } - - // if this has credential attribute, skip - if (paramAst.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + // if this is pscredential type with credential attribute, skip + if ((paramType == typeof(PSCredential) || (paramType.IsArray && paramType.GetElementType() == typeof (PSCredential))) + && paramAst.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) { continue; } diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 0a0cd2095..22b8a9191 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -421,7 +421,7 @@ internal static string AvoidUsernameAndPasswordParamsCommonName { } /// - /// Looks up a localized string similar to Functions should only take in a credential parameter of type PSCredential or has a CredentialAttribute instead of username and password parameters.. + /// Looks up a localized string similar to Functions should only take in a credential parameter of type PSCredential with CredentialAttribute instead of username and password parameters.. /// internal static string AvoidUsernameAndPasswordParamsDescription { get { @@ -430,7 +430,7 @@ internal static string AvoidUsernameAndPasswordParamsDescription { } /// - /// Looks up a localized string similar to Function '{0}' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used.. + /// Looks up a localized string similar to Function '{0}' has both username and password parameters. A credential parameter of type PSCredential with a CredentialAttribute should be used.. /// internal static string AvoidUsernameAndPasswordParamsError { get { @@ -1771,7 +1771,7 @@ internal static string UsePSCredentialTypeCommonName { } /// - /// Looks up a localized string similar to Checks that cmdlets that have a Credential parameter accept PSCredential or has a CredentialAttribute. This comes from the PowerShell teams best practices.. + /// Looks up a localized string similar to Checks that cmdlets that have a Credential parameter accept PSCredential with CredentialAttribute. This comes from the PowerShell teams best practices.. /// internal static string UsePSCredentialTypeDescription { get { @@ -1780,7 +1780,7 @@ internal static string UsePSCredentialTypeDescription { } /// - /// Looks up a localized string similar to The Credential parameter in '{0}' must be of the type PSCredential or has a CredentialAttribute.. + /// Looks up a localized string similar to The Credential parameter in '{0}' must be of the type PSCredential with CredentialAttribute.. /// internal static string UsePSCredentialTypeError { get { @@ -1789,7 +1789,7 @@ internal static string UsePSCredentialTypeError { } /// - /// Looks up a localized string similar to The Credential parameter in a found script block must be of the type PSCredential or has a CredentialAttribute.. + /// Looks up a localized string similar to The Credential parameter in a found script block must be of the type PSCredential with CredentialAttribute.. /// internal static string UsePSCredentialTypeErrorSB { get { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index b4150441a..d27bbb8d7 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -217,13 +217,13 @@ One Char - Checks that cmdlets that have a Credential parameter accept PSCredential or has a CredentialAttribute. This comes from the PowerShell teams best practices. + Checks that cmdlets that have a Credential parameter accept PSCredential with CredentialAttribute. This comes from the PowerShell teams best practices. - The Credential parameter in '{0}' must be of the type PSCredential or has a CredentialAttribute. + The Credential parameter in '{0}' must be of the type PSCredential with CredentialAttribute. - The Credential parameter in a found script block must be of the type PSCredential or has a CredentialAttribute. + The Credential parameter in a found script block must be of the type PSCredential with CredentialAttribute. PSCredential @@ -511,10 +511,10 @@ Avoid Using Username and Password Parameters - Functions should only take in a credential parameter of type PSCredential or has a CredentialAttribute instead of username and password parameters. + Functions should only take in a credential parameter of type PSCredential with CredentialAttribute instead of username and password parameters. - Function '{0}' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used. + Function '{0}' has both username and password parameters. A credential parameter of type PSCredential with a CredentialAttribute should be used. AvoidUsingUserNameAndPassWordParams diff --git a/Rules/UsePSCredentialType.cs b/Rules/UsePSCredentialType.cs index df19a028a..57e4309a0 100644 --- a/Rules/UsePSCredentialType.cs +++ b/Rules/UsePSCredentialType.cs @@ -11,6 +11,7 @@ // using System; +using System.Reflection; using System.Linq; using System.Collections.Generic; using System.Management.Automation; @@ -51,9 +52,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in funcDefAst.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) - && parameter.StaticType != typeof(PSCredential) - && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + if (WrongCredentialUsage(parameter)) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -64,9 +63,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in funcDefAst.Body.ParamBlock.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) - && parameter.StaticType != typeof(PSCredential) - && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + if (WrongCredentialUsage(parameter)) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -80,9 +77,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (ParameterAst parameter in scriptBlockAst.ParamBlock.Parameters) { - if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) - && parameter.StaticType != typeof(PSCredential) - && !parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + if (WrongCredentialUsage(parameter)) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeErrorSB), scriptBlockAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -91,6 +86,24 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) } } + private bool WrongCredentialUsage(ParameterAst parameter) + { + if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase)) + { + TypeInfo paramType = (TypeInfo)parameter.StaticType; + + if ((paramType == typeof(PSCredential) || (paramType.IsArray && paramType.GetElementType() == typeof(PSCredential))) + && parameter.Attributes.Any(paramAttribute => paramAttribute.TypeName.GetReflectionType() == typeof(CredentialAttribute))) + { + return false; + } + + return true; + } + + return false; + } + /// /// GetName: Retrieves the name of this rule. /// diff --git a/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 b/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 index 0f383927a..358997fe8 100644 --- a/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 +++ b/Tests/Rules/AvoidUserNameAndPasswordParams.tests.ps1 @@ -1,6 +1,6 @@ Import-Module PSScriptAnalyzer -$violationMessage = "Function 'Verb-Noun' has both username and password parameters. A credential parameter of type PSCredential or has a CredentialAttribute should be used." +$violationMessage = "Function 'Verb-Noun' has both username and password parameters. A credential parameter of type PSCredential with a CredentialAttribute should be used." $violationName = "PSAvoidUsingUserNameAndPasswordParams" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\AvoidUserNameAndPasswordParams.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 b/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 index 1ed59f853..b909d7b59 100644 --- a/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 +++ b/Tests/Rules/AvoidUserNameAndPasswordParamsNoViolations.ps1 @@ -8,11 +8,7 @@ function MyFunction2 ($param1, $passwords) } -function MyFunction3 ([PSCredential]$username, $passwords) -{ -} - -function MyFunction4 +function MyFunction3 { [CmdletBinding()] [Alias()] @@ -24,10 +20,11 @@ function MyFunction4 ValueFromPipelineByPropertyName=$true, Position=0)] [System.Management.Automation.CredentialAttribute()] + [pscredential] $UserName, # Param2 help description - [int] + [pscredential] [System.Management.Automation.CredentialAttribute()] $Password ) diff --git a/Tests/Rules/PSCredentialType.tests.ps1 b/Tests/Rules/PSCredentialType.tests.ps1 index 8e09ea6db..ccf13b6ef 100644 --- a/Tests/Rules/PSCredentialType.tests.ps1 +++ b/Tests/Rules/PSCredentialType.tests.ps1 @@ -1,5 +1,5 @@ Import-Module PSScriptAnalyzer -$violationMessage = "The Credential parameter in 'Credential' must be of the type PSCredential or has a CredentialAttribute." +$violationMessage = "The Credential parameter in 'Credential' must be of the type PSCredential with CredentialAttribute." $violationName = "PSUsePSCredentialType" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\PSCredentialType.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/Rules/PSCredentialTypeNoViolations.ps1 b/Tests/Rules/PSCredentialTypeNoViolations.ps1 index 62368066d..b907929b8 100644 --- a/Tests/Rules/PSCredentialTypeNoViolations.ps1 +++ b/Tests/Rules/PSCredentialTypeNoViolations.ps1 @@ -1,8 +1,4 @@ -function Credential([pscredential]$credential) { - -} - -function Credential2 +function Credential { [CmdletBinding()] [Alias()] @@ -14,6 +10,7 @@ function Credential2 ValueFromPipelineByPropertyName=$true, Position=0)] [System.Management.Automation.CredentialAttribute()] + [pscredential] $Credential ) -} +} \ No newline at end of file