From f5b52bb8111ca4515b90f5bce11ad8d54978fe01 Mon Sep 17 00:00:00 2001 From: yotsuda Date: Fri, 24 Oct 2025 18:16:55 +0900 Subject: [PATCH 1/6] Fix #25820: Make Export-Csv -Append and -NoHeader mutually exclusive - Add validation in ExportCsvCommand.BeginProcessing() to throw an error when both -Append and -NoHeader parameters are specified together - Add error message resource: CannotSpecifyAppendAndNoHeader - Add Pester test to verify the error is thrown correctly This prevents undefined behavior when users attempt to append to a CSV file without headers, which could result in data corruption or confusion. --- .../commands/utility/CsvCommands.cs | 8 ++++++++ .../resources/CsvCommandStrings.resx | 5 ++++- .../Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs index fa49c33d2ab..25dd525086e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs @@ -270,6 +270,14 @@ protected override void BeginProcessing() this.ThrowTerminatingError(errorRecord); } + // Validate that Append and NoHeader are not specified together. + if (Append.IsPresent && NoHeader.IsPresent) + { + InvalidOperationException exception = new(CsvCommandStrings.CannotSpecifyAppendAndNoHeader); + ErrorRecord errorRecord = new(exception, "CannotSpecifyAppendAndNoHeader", ErrorCategory.InvalidArgument, null); + this.ThrowTerminatingError(errorRecord); + } + _shouldProcess = ShouldProcess(Path); if (!_shouldProcess) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx index 0eff0d6f84f..ac7697398b5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx @@ -157,4 +157,7 @@ EOF is reached. - + + You cannot specify both the -Append and -NoHeader parameters. When appending to an existing file, headers should already be present. + + \ No newline at end of file diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 index db5c6aff9e3..ca2ce2d49cd 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 @@ -187,6 +187,10 @@ Describe "Export-Csv" -Tags "CI" { $results[1].PSObject.properties.Name | Should -Not -Contain 'third' } + It "Should throw when -Append and -NoHeader are specified together" { + { $P1 | Export-Csv -Path $testCsv -Append -NoHeader -ErrorAction Stop } | Should -Throw -ErrorId "CannotSpecifyAppendAndNoHeader,Microsoft.PowerShell.Commands.ExportCsvCommand" + } + It "First line should be #TYPE if -IncludeTypeInformation used and pstypenames object property is empty" { $object = [PSCustomObject]@{first = 1} $pstypenames = $object.pstypenames | ForEach-Object -Process {$_} From da2b21888211fb1b7111bfa21f8bbb33e847f236 Mon Sep 17 00:00:00 2001 From: yotsuda Date: Tue, 18 Nov 2025 14:09:23 +0900 Subject: [PATCH 2/6] Add newline at end of file --- .../resources/CsvCommandStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx index ac7697398b5..f1f479d147d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx @@ -160,4 +160,4 @@ You cannot specify both the -Append and -NoHeader parameters. When appending to an existing file, headers should already be present. - \ No newline at end of file + From 81d842e1dc20c3f41313b7219ad4678cbd924084 Mon Sep 17 00:00:00 2001 From: yotsuda Date: Wed, 19 Nov 2025 07:43:36 +0900 Subject: [PATCH 3/6] Update src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs Co-authored-by: Ilya --- .../commands/utility/CsvCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs index 25dd525086e..bfa1f189b6d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs @@ -274,7 +274,7 @@ protected override void BeginProcessing() if (Append.IsPresent && NoHeader.IsPresent) { InvalidOperationException exception = new(CsvCommandStrings.CannotSpecifyAppendAndNoHeader); - ErrorRecord errorRecord = new(exception, "CannotSpecifyAppendAndNoHeader", ErrorCategory.InvalidArgument, null); + ErrorRecord errorRecord = new(exception, "CannotSpecifyBothAppendAndNoHeader", ErrorCategory.InvalidArgument, null); this.ThrowTerminatingError(errorRecord); } From 536541883841cbf41653e3ed844eb2d1b62daaa7 Mon Sep 17 00:00:00 2001 From: yotsuda Date: Wed, 19 Nov 2025 17:02:45 +0900 Subject: [PATCH 4/6] Update test to use consistent error ID --- .../Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 index ca2ce2d49cd..ea05d66d392 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 @@ -188,7 +188,7 @@ Describe "Export-Csv" -Tags "CI" { } It "Should throw when -Append and -NoHeader are specified together" { - { $P1 | Export-Csv -Path $testCsv -Append -NoHeader -ErrorAction Stop } | Should -Throw -ErrorId "CannotSpecifyAppendAndNoHeader,Microsoft.PowerShell.Commands.ExportCsvCommand" + { $P1 | Export-Csv -Path $testCsv -Append -NoHeader -ErrorAction Stop } | Should -Throw -ErrorId "CannotSpecifyBothAppendAndNoHeader,Microsoft.PowerShell.Commands.ExportCsvCommand" } It "First line should be #TYPE if -IncludeTypeInformation used and pstypenames object property is empty" { From 8a1a1b0aef5bbb0cb6e3c2db19c19937a1c3d67b Mon Sep 17 00:00:00 2001 From: yotsuda Date: Fri, 21 Nov 2025 23:16:52 +0900 Subject: [PATCH 5/6] Update src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../resources/CsvCommandStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx index f1f479d147d..8c5ded13465 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/CsvCommandStrings.resx @@ -158,6 +158,6 @@ EOF is reached. - You cannot specify both the -Append and -NoHeader parameters. When appending to an existing file, headers should already be present. + You must specify either the -Append or -NoHeader parameters, but not both. From 061a6fc6b07a2520d636d24c6806521ec23a82d7 Mon Sep 17 00:00:00 2001 From: yotsuda Date: Sat, 22 Nov 2025 08:56:28 +0900 Subject: [PATCH 6/6] Address PR feedback: simplify condition and use InvalidData for consistency --- .../commands/utility/CsvCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs index bfa1f189b6d..e84a79b99b6 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs @@ -271,10 +271,10 @@ protected override void BeginProcessing() } // Validate that Append and NoHeader are not specified together. - if (Append.IsPresent && NoHeader.IsPresent) + if (Append && NoHeader) { InvalidOperationException exception = new(CsvCommandStrings.CannotSpecifyAppendAndNoHeader); - ErrorRecord errorRecord = new(exception, "CannotSpecifyBothAppendAndNoHeader", ErrorCategory.InvalidArgument, null); + ErrorRecord errorRecord = new(exception, "CannotSpecifyBothAppendAndNoHeader", ErrorCategory.InvalidData, null); this.ThrowTerminatingError(errorRecord); }