Skip to content

Commit 92bd914

Browse files
authored
Try to sanitize invalid YAML containing unescaped double quotes (#970)
* Add test case for Stylelint message containing double quote * Add YAML sanitation to parser
1 parent d17a93d commit 92bd914

File tree

3 files changed

+80
-8
lines changed

3 files changed

+80
-8
lines changed

src/Cake.Issues.Tap.Tests/LogFileFormat/StylelintLogFileFormatTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,30 @@ public void Should_Read_Issue_Correct_When_Parse_Error()
129129
.OfRule("unknown")
130130
.Create());
131131
}
132+
133+
[Fact]
134+
public void Should_Read_Issue_Correct_When_Message_Contains_Double_Quotes()
135+
{
136+
// Given
137+
var fixture = new TapIssuesProviderFixture<StylelintLogFileFormat>("double-quotes-in-message.tap");
138+
139+
// When
140+
var issues = fixture.ReadIssues().ToList();
141+
142+
// Then
143+
issues.Count.ShouldBe(1);
144+
145+
var issue = issues[0];
146+
IssueChecker.Check(
147+
issue,
148+
IssueBuilder.NewIssue(
149+
"Unexpected \"width\" property. Use \"inline-size\". (csstools/use-logical)",
150+
"Cake.Issues.Tap.TapIssuesProvider",
151+
"TAP")
152+
.InFile("path/to/file.css", 13, 13, 3, 15)
153+
.WithPriority(IssuePriority.Error)
154+
.OfRule("csstools/use-logical")
155+
.Create());
156+
}
132157
}
133158
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
TAP version 14
2+
1..1
3+
not ok 1 - path/to/file.css
4+
---
5+
csstools/use-logical:
6+
- message: "Unexpected "width" property. Use "inline-size". (csstools/use-logical)"
7+
severity: error
8+
line: 13
9+
column: 3
10+
endLine: 13
11+
endColumn: 15
12+
...

src/Cake.Issues.Tap/Parser/TapParser.cs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.Collections.Generic;
5+
using System.Linq;
56
using System.Text.RegularExpressions;
67
using YamlDotNet.Serialization;
78
using YamlDotNet.Serialization.NamingConventions;
@@ -76,6 +77,9 @@ public void Parse(string tapContent)
7677
[GeneratedRegex(@"^#\s*(.*)$")]
7778
private static partial Regex CommentRegEx14();
7879

80+
[GeneratedRegex(": \"(.*)\"$")]
81+
private static partial Regex YamlSanitizeUnescapedDoubeQuotesRegEx();
82+
7983
/// <summary>
8084
/// Parses the content of a TAP file.
8185
/// </summary>
@@ -157,6 +161,38 @@ private void ParseVersion14(string[] lines)
157161

158162
private void ParseYamlVersion14(List<string> yamlLines)
159163
{
164+
// Deserializes YAML content into diagnostics of a TapTestPoint.
165+
static void DeserializeYaml(TapTestPoint testPoint, string yamlContent)
166+
{
167+
var deserializer = new DeserializerBuilder()
168+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
169+
.Build();
170+
var yamlData = deserializer.Deserialize<Dictionary<string, object>>(yamlContent);
171+
172+
foreach (var entry in yamlData)
173+
{
174+
testPoint.Diagnostics[entry.Key] = entry.Value;
175+
}
176+
}
177+
178+
// Tries to sanitizes invalid YAML content reported by linters.
179+
static string SanitizeYaml(string yamlContent)
180+
{
181+
var lines = yamlContent.Split('\n');
182+
return string.Join("\n", lines.Select(line =>
183+
{
184+
// Replace unescaped double quotes with escaped double quotes.
185+
var match = YamlSanitizeUnescapedDoubeQuotesRegEx().Match(line);
186+
if (match.Success)
187+
{
188+
var value = match.Groups[1].Value.Replace("\"", "\\\"");
189+
return line.Replace(match.Groups[1].Value, value);
190+
}
191+
192+
return line;
193+
}));
194+
}
195+
160196
if (this.Results.Count == 0)
161197
{
162198
return;
@@ -167,14 +203,13 @@ private void ParseYamlVersion14(List<string> yamlLines)
167203

168204
try
169205
{
170-
var deserializer = new DeserializerBuilder()
171-
.WithNamingConvention(CamelCaseNamingConvention.Instance)
172-
.Build();
173-
var yamlData = deserializer.Deserialize<Dictionary<string, object>>(yamlContent);
174-
foreach (var entry in yamlData)
175-
{
176-
lastResult.Diagnostics[entry.Key] = entry.Value;
177-
}
206+
DeserializeYaml(lastResult, yamlContent);
207+
}
208+
catch (YamlDotNet.Core.SemanticErrorException)
209+
{
210+
// Attempt to sanitize and retry parsing
211+
yamlContent = SanitizeYaml(yamlContent);
212+
DeserializeYaml(lastResult, yamlContent);
178213
}
179214
catch (Exception ex)
180215
{

0 commit comments

Comments
 (0)