From de9a38d024e59e072a03fa14c730884755bc73f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 19:05:41 +0000 Subject: [PATCH 1/3] Initial plan From e679aff78ed3eef62598948e8a5f103a4a3804d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 19:12:30 +0000 Subject: [PATCH 2/3] Fix DSC resource body detection edge cases Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/AvoidAlias.cs | 21 +++++++++++++++++++++ Tests/Rules/AvoidUsingAlias.tests.ps1 | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Rules/AvoidAlias.cs b/Rules/AvoidAlias.cs index ba069453b..53587a723 100644 --- a/Rules/AvoidAlias.cs +++ b/Rules/AvoidAlias.cs @@ -348,6 +348,12 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) && bodyPipeline.PipelineElements.Count == 1 && bodyPipeline.PipelineElements[0] is CommandExpressionAst bodyExpression) { + if (HasStatementTerminator(pipeline, bodyPipeline) + || bodyExpression.Redirections.Count != 0) + { + return null; + } + return bodyExpression.Expression as ScriptBlockExpressionAst; } @@ -358,6 +364,21 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) return null; } + private bool HasStatementTerminator(Ast statement, Ast nextStatement) + { + int separatorStart = statement.Extent.EndOffset; + int separatorLength = nextStatement.Extent.StartOffset - separatorStart; + if (separatorLength <= 0) + { + return false; + } + + string script = statement.Extent.EndScriptPosition.GetFullScript(); + string separator = script.Substring(separatorStart, separatorLength); + Parser.ParseInput(separator, out Token[] tokens, out ParseError[] errors); + return tokens.Any(token => token.Kind == TokenKind.Semi); + } + /// /// For a command like "gci -path c:", returns the extent of "gci" in the command /// diff --git a/Tests/Rules/AvoidUsingAlias.tests.ps1 b/Tests/Rules/AvoidUsingAlias.tests.ps1 index 2bdd55b33..731dc4137 100644 --- a/Tests/Rules/AvoidUsingAlias.tests.ps1 +++ b/Tests/Rules/AvoidUsingAlias.tests.ps1 @@ -232,6 +232,30 @@ Configuration PackageTest { $_.SuggestedCorrections[0].Text | Should -Be 'Get-Verb' } } + + It "does not pair following-line blocks with commands when the block has redirections or a statement terminator intervenes" { + $target = @' +{ + gci . + {} > $null + gci .; {} +} +'@ + $ast = [System.Management.Automation.Language.Parser]::ParseInput($target, [ref]$null, [ref]$null) + $body = $ast.EndBlock.Statements[0].PipelineElements[0].Expression.Copy() + $configuration = [System.Management.Automation.Language.ConfigurationDefinitionAst]::new( + $ast.Extent, $body, 'Resource', + [System.Management.Automation.Language.StringConstantExpressionAst]::new($ast.Extent, 'Test', 'BareWord')) + $rule = [Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.AvoidAlias]::new() + + $violations = @($rule.AnalyzeScript($configuration, $null)) + + $violations.Count | Should -Be 2 + $violations | ForEach-Object { + $_.Extent.Text | Should -Be 'gci' + $_.SuggestedCorrections[0].Text | Should -Be 'Get-ChildItem' + } + } } Context "Settings file provides allowlist" { From c379a3d68e2f29bcc041a2c45a0a14f431f919c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 20:05:54 +0000 Subject: [PATCH 3/3] Optimize DSC separator detection Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/AvoidAlias.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Rules/AvoidAlias.cs b/Rules/AvoidAlias.cs index 53587a723..7b8a46c2f 100644 --- a/Rules/AvoidAlias.cs +++ b/Rules/AvoidAlias.cs @@ -348,7 +348,7 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) && bodyPipeline.PipelineElements.Count == 1 && bodyPipeline.PipelineElements[0] is CommandExpressionAst bodyExpression) { - if (HasStatementTerminator(pipeline, bodyPipeline) + if (HasSemicolonSeparator(pipeline, bodyPipeline) || bodyExpression.Redirections.Count != 0) { return null; @@ -364,7 +364,7 @@ private ScriptBlockExpressionAst GetDscResourceBody(CommandAst cmdAst) return null; } - private bool HasStatementTerminator(Ast statement, Ast nextStatement) + private bool HasSemicolonSeparator(Ast statement, Ast nextStatement) { int separatorStart = statement.Extent.EndOffset; int separatorLength = nextStatement.Extent.StartOffset - separatorStart; @@ -375,6 +375,11 @@ private bool HasStatementTerminator(Ast statement, Ast nextStatement) string script = statement.Extent.EndScriptPosition.GetFullScript(); string separator = script.Substring(separatorStart, separatorLength); + if (separator.IndexOf(';') < 0) + { + return false; + } + Parser.ParseInput(separator, out Token[] tokens, out ParseError[] errors); return tokens.Any(token => token.Kind == TokenKind.Semi); }