Rules/PSAvoidAmbiguousPipelineInput.psm1
|
Set-StrictMode -Version Latest $script:RuleName = 'PSAvoidAmbiguousPipelineInput' $script:RuleMessage = 'Avoid exposing multiple ValueFromPipeline parameters on the same pipeline-facing command. Keep one clear pipeline-by-value intake path so pipeline binding stays predictable.' function Measure-PSAvoidAmbiguousPipelineInput { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) $functions = $ScriptBlockAst.FindAll({ param($Ast) $Ast -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) foreach ($functionAst in $functions) { if (-not (Test-IsPipelineFacingCommand -FunctionAst $functionAst)) { continue } $pipelineByValueParameters = @(Get-PipelineByValueParameters -FunctionAst $functionAst) if ($pipelineByValueParameters.Count -le 1) { continue } [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]::new( $script:RuleMessage, $functionAst.Extent, $script:RuleName, [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticSeverity]::Warning, $null, $null, $null ) } } function Test-IsPipelineFacingCommand { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Language.FunctionDefinitionAst] $FunctionAst ) if (-not $FunctionAst.Body.ParamBlock) { return $false } $hasCmdletBinding = $FunctionAst.Body.ParamBlock.Attributes | Where-Object { $_.TypeName.GetReflectionType().FullName -eq 'System.Management.Automation.CmdletBindingAttribute' } if (-not $hasCmdletBinding) { return $false } return $FunctionAst.Body.ProcessBlock -ne $null } function Get-PipelineByValueParameters { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Language.FunctionDefinitionAst] $FunctionAst ) @( $FunctionAst.Body.ParamBlock.Parameters | Where-Object { Test-ParameterAcceptsPipelineByValue -ParameterAst $_ } ) } function Test-ParameterAcceptsPipelineByValue { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Language.ParameterAst] $ParameterAst ) foreach ($attribute in $ParameterAst.Attributes) { if ($attribute.TypeName.FullName -ne 'Parameter') { continue } foreach ($namedArgument in $attribute.NamedArguments) { if ($namedArgument.ArgumentName -eq 'ValueFromPipeline') { return $true } } } return $false } Export-ModuleMember -Function Measure-PSAvoidAmbiguousPipelineInput |