Skip to content

Commit

Permalink
Fixes CSV output of multiple lines microsoft#1627 (microsoft#1834)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored May 24, 2024
1 parent c601336 commit 229f4f6
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/analyze.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
prerelease: true
outputFormat: Sarif
outputPath: reports/ps-rule-results.sarif
env:
PSRULE_INPUT_FILEOBJECTS: true

- name: Upload results to security tab
uses: github/codeql-action/upload-sarif@v3
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ What's changed since pre-release v3.0.0-B0198:
- Bug fixes:
- Fixed reason reported for `startsWith` by @BernieWhite.
[#1818](https://github.com/microsoft/PSRule/issues/1818)
- Fixes CSV output of multiple lines by @BernieWhite.
[#1627](https://github.com/microsoft/PSRule/issues/1627)

## v3.0.0-B0198 (pre-release)

Expand Down
2 changes: 1 addition & 1 deletion pipeline.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ task Rules {
As = 'Summary'
}
Import-Module (Join-Path -Path $PWD -ChildPath out/modules/PSRule) -Force;
Assert-PSRule @assertParams -OutputPath reports/ps-rule-file.xml -InputPath $PWD -ErrorAction Stop;
Assert-PSRule @assertParams -OutputPath reports/ps-rule-file.xml -InputPath $PWD -ErrorAction Stop -Option @{ 'Input.FileObjects' = $True };
}

task Benchmark {
Expand Down
2 changes: 1 addition & 1 deletion schemas/PSRule-options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@
"type": "boolean",
"title": "File objects",
"description": "Determines if file objects are processed by rules.",
"markdownDescription": "Determines if file objects are processed by rules. [See help](https://microsoft.github.io/PSRule/v3/concepts/PSRule/en-US/about_PSRule_Options/#fileobjects)",
"markdownDescription": "Determines if file objects are processed by rules. [See help](https://microsoft.github.io/PSRule/v3/concepts/PSRule/en-US/about_PSRule_Options/#inputfileobjects)",
"default": false
},
"format": {
Expand Down
12 changes: 7 additions & 5 deletions src/PSRule/Pipeline/Output/CsvOutputWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal CsvOutputWriter(PipelineWriter inner, PSRuleOption option, ShouldProces
protected override string Serialize(object[] o)
{
WriteHeader();
if (Option.Output.As == ResultFormat.Detail)
if (Option.Output.As.GetValueOrDefault(OutputOption.Default.As.Value) == ResultFormat.Detail)
WriteDetail(o);
else
WriteSummary(o);
Expand Down Expand Up @@ -127,7 +127,11 @@ private void WriteColumn(string value)
return;

_Builder.Append(QUOTE);
_Builder.Append(value.Replace("\"", "\"\""));
_Builder.Append(value
.Replace("\"", "\"\"").Replace("\r\n", " ")
.Replace('\r', ' ').Replace('\n', ' ')
.Replace(" ", " ")
);
_Builder.Append(QUOTE);
}

Expand All @@ -136,8 +140,6 @@ private void WriteColumn(InfoString value)
if (!value.HasValue)
return;

_Builder.Append(QUOTE);
_Builder.Append(value.Text.Replace("\"", "\"\""));
_Builder.Append(QUOTE);
WriteColumn(value.Text);
}
}
41 changes: 36 additions & 5 deletions tests/PSRule.Tests/OutputWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ public void Yaml()
reason: []
info:
moduleName: TestModule
recommendation: Recommendation for rule 001
recommendation: >-
Recommendation for rule 001
over two lines.
level: Error
outcome: Pass
outcomeReason: Processed
Expand Down Expand Up @@ -216,7 +219,7 @@ public void Json()
""displayName"": ""Rule 001"",
""moduleName"": ""TestModule"",
""name"": ""rule-001"",
""recommendation"": ""Recommendation for rule 001"",
""recommendation"": ""Recommendation for rule 001\r\nover two lines."",
""synopsis"": ""This is rule 001.""
},
""level"": ""Error"",
Expand Down Expand Up @@ -340,6 +343,32 @@ public void NUnit3()
Assert.Equal("<test-suite type=\"TestFixture\" name=\"TestObject1\" executed=\"True\" result=\"Failure\" success=\"False\" time=\"3.5\" asserts=\"3\" description=\"\"><results><test-case description=\"This is rule 001.\" name=\"TestObject1 -- rule-001\" time=\"0.5\" asserts=\"0\" success=\"True\" result=\"Success\" executed=\"True\" /><test-case description=\"This is rule 002.\" name=\"TestObject1 -- rule-002\" time=\"1\" asserts=\"0\" success=\"False\" result=\"Failure\" executed=\"True\"><failure><message><![CDATA[Recommendation for rule 002\r\n]]></message><stack-trace><![CDATA[]]></stack-trace></failure></test-case><test-case description=\"This is rule 002.\" name=\"TestObject1 -- rule-002\" time=\"1\" asserts=\"0\" success=\"False\" result=\"Failure\" executed=\"True\"><failure><message><![CDATA[Recommendation for rule 002\r\n]]></message><stack-trace><![CDATA[]]></stack-trace></failure></test-case><test-case description=\"Synopsis &quot;with quotes&quot;.\" name=\"TestObject1 -- rule-002\" time=\"1\" asserts=\"0\" success=\"False\" result=\"Failure\" executed=\"True\"><failure><message><![CDATA[Recommendation for rule 002\r\n]]></message><stack-trace><![CDATA[]]></stack-trace></failure></test-case></results></test-suite>", xml);
}

[Fact]
public void Csv()
{
var option = GetOption();
option.Repository.Url = "https://github.com/microsoft/PSRule.UnitTest";
var output = new TestWriter(option);
var result = new InvokeResult();
result.Add(GetPass());
result.Add(GetFail());
result.Add(GetFail("rid-003", SeverityLevel.Warning));
result.Add(GetFail("rid-004", SeverityLevel.Information));
var writer = new CsvOutputWriter(output, option, null);
writer.Begin();
writer.WriteObject(result, false);
writer.End();

var actual = output.Output.OfType<string>().FirstOrDefault();

Assert.Equal(@"RuleName,TargetName,TargetType,Outcome,Synopsis,Recommendation
""rule-001"",""TestObject1"",""TestType"",""Pass"",""Processed"",""This is rule 001."",""Recommendation for rule 001 over two lines.""
""rule-002"",""TestObject1"",""TestType"",""Fail"",""Processed"",""This is rule 002."",""Recommendation for rule 002""
""rule-002"",""TestObject1"",""TestType"",""Fail"",""Processed"",""This is rule 002."",""Recommendation for rule 002""
""rule-002"",""TestObject1"",""TestType"",""Fail"",""Processed"",""This is rule 002."",""Recommendation for rule 002""
", actual);
}

[Fact]
public void JobSummary()
{
Expand Down Expand Up @@ -367,20 +396,22 @@ public void JobSummary()

private static RuleRecord GetPass()
{
return new RuleRecord(
return new RuleRecord
(
runId: "run-001",
ruleId: ResourceId.Parse("TestModule\\rule-001"),
@ref: null,
targetObject: new TargetObject(new PSObject()),
targetName: "TestObject1",
targetType: "TestType",
tag: new ResourceTags(),
info: new RuleHelpInfo(
info: new RuleHelpInfo
(
"rule-001",
"Rule 001",
"TestModule",
synopsis: new InfoString("This is rule 001."),
recommendation: new InfoString("Recommendation for rule 001")
recommendation: new InfoString("Recommendation for rule 001\r\nover two lines.")
),
field: new Hashtable(),
level: SeverityLevel.Error,
Expand Down
4 changes: 2 additions & 2 deletions tests/PSRule.Tests/PSRule.Common.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ Describe 'Invoke-PSRule' -Tag 'Invoke-PSRule','Common' {
$resultCsv[1].Synopsis | Should -Be 'Test rule 3';
$resultCsv[2].RuleName | Should -Be 'WithCsv';
$resultCsv[2].Synopsis | Should -Be 'This is "a" synopsis.';
($resultCsv[2].Recommendation -replace "`r`n", "`n") | Should -Be "This is an extended recommendation.`n`n- That includes line breaks`n- And lists";
($resultCsv[2].Recommendation -replace "`r`n", "`n") | Should -Be "This is an extended recommendation. - That includes line breaks - And lists";

# Summary
$result = $testObject | Invoke-PSRule @option -As Summary | Out-String;
Expand All @@ -659,7 +659,7 @@ Describe 'Invoke-PSRule' -Tag 'Invoke-PSRule','Common' {
$resultCsv[1].Fail | Should -Be '1';
$resultCsv[2].RuleName | Should -Be 'WithCsv';
$resultCsv[2].Synopsis | Should -Be 'This is "a" synopsis.';
($resultCsv[2].Recommendation -replace "`r`n", "`n") | Should -Be "This is an extended recommendation.`n`n- That includes line breaks`n- And lists";
($resultCsv[2].Recommendation -replace "`r`n", "`n") | Should -Be "This is an extended recommendation. - That includes line breaks - And lists";
}

It 'Sarif' {
Expand Down

0 comments on commit 229f4f6

Please sign in to comment.