Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix XML Reporters not Escaping Characters #969

Closed
wants to merge 12 commits into from
Closed
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@
[Marcelo Fabri](https://github.com/marcelofabri)
[#940](https://github.com/realm/SwiftLint/issues/940)

* Fix XML reporters not escaping characters.
[Fabian Ehrentraud](https://github.com/fabb)
[#968](https://github.com/realm/SwiftLint/issues/968)

* Fix specifying multiple rule identifiers in comment commands.
[JP Simard](https://github.com/jpsim)
[#976](https://github.com/realm/SwiftLint/issues/976)
Expand Down
25 changes: 25 additions & 0 deletions Source/SwiftLintFramework/Extensions/String+XML.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// String+XML.swift
// SwiftLint
//
// Created by Fabian Ehrentraud on 12/12/16.
// Copyright © 2016 Realm. All rights reserved.
//

extension String {
func escapedForXML() -> String {
// & needs to go first, otherwise other replacements will be replaced again
let htmlEscapes = [
("&", "&"),
("\"", """),
("'", "'"),
(">", ">"),
("<", "&lt;")
]
var newString = self
for (key, value) in htmlEscapes {
newString = newString.replacingOccurrences(of: key, with: value)
}
return newString
}
}
4 changes: 2 additions & 2 deletions Source/SwiftLintFramework/Reporters/CheckstyleReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ public struct CheckstyleReporter: Reporter {
}

private static func generateForSingleViolation(_ violation: StyleViolation) -> String {
let file: String = violation.location.file ?? "<nopath>"
let file: String = (violation.location.file ?? "<nopath>").escapedForXML()
let line: Int = violation.location.line ?? 0
let col: Int = violation.location.character ?? 0
let severity: String = violation.severity.rawValue
let reason: String = violation.reason
let reason: String = violation.reason.escapedForXML()
return [
"\n\t<file name=\"", file, "\">\n",
"\t\t<error line=\"\(line)\" ",
Expand Down
17 changes: 13 additions & 4 deletions Source/SwiftLintFramework/Reporters/HTMLReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,25 @@ public struct HTMLReporter: Reporter {
return "Reports violations as HTML"
}

// swiftlint:disable:next function_body_length
public static func generateReport(_ violations: [StyleViolation]) -> String {
let dateString = formatter.string(from: Date())
return generateReport(
violations,
swiftlintVersion: swiftlintVersion,
dateString: dateString
)
}

// swiftlint:disable function_body_length
// swiftlint:disable line_length
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i tried these 2 variants, but it didn't work:

    // swiftlint:disable function_body_length line_length
    // swiftlint:disable:next function_body_length
    // swiftlint:disable:next line_length

bug?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, could you please file a new ticket for this? I don't think my quick implementation from last week is 100% correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, done: #976

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #978, thanks!

internal static func generateReport(_ violations: [StyleViolation], swiftlintVersion: String, dateString: String) -> String {
let rows = violations.enumerated().reduce("") { rows, indexAndViolation in
return rows + generateSingleRow(for: indexAndViolation.1, at: indexAndViolation.0 + 1)
}

let fileCount = Set(violations.flatMap({ $0.location.file })).count
let warningCount = violations.filter({ $0.severity == .warning }).count
let errorCount = violations.filter({ $0.severity == .error }).count
let dateString = formatter.string(from: Date())

return [
"<!doctype html>\n",
Expand Down Expand Up @@ -120,7 +129,7 @@ public struct HTMLReporter: Reporter {
private static func generateSingleRow(for violation: StyleViolation, at index: Int) -> String {
let severity: String = violation.severity.rawValue.capitalized
let location = violation.location
let file: String = location.file ?? ""
let file: String = (violation.location.file ?? "<nopath>").escapedForXML()
let line: Int = location.line ?? 0
let character: Int = location.character ?? 0
return [
Expand All @@ -129,7 +138,7 @@ public struct HTMLReporter: Reporter {
"\t\t\t\t\t<td>", file, "</td>\n",
"\t\t\t\t\t<td align=\"center\">\(line):\(character)</td>\n",
"\t\t\t\t\t<td class=\'", severity.lowercased(), "\'>", severity, "</td>\n",
"\t\t\t\t\t<td>\(violation.reason)</td>\n",
"\t\t\t\t\t<td>\(violation.reason.escapedForXML())</td>\n",
"\t\t\t\t</tr>\n"
].joined()
}
Expand Down
5 changes: 3 additions & 2 deletions Source/SwiftLintFramework/Reporters/JUnitReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ public struct JUnitReporter: Reporter {
public static func generateReport(_ violations: [StyleViolation]) -> String {
return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<testsuites><testsuite>" +
violations.map({ violation in
let fileName = violation.location.file ?? "<nopath>"
let fileName = (violation.location.file ?? "<nopath>").escapedForXML()
let severity = violation.severity.rawValue + ":\n"
let message = severity + "Line:" + String(violation.location.line ?? 0) + " "
let reason = violation.reason.escapedForXML()
return ["\n\t<testcase classname='Formatting Test' name='\(fileName)\'>\n",
"<failure message='\(violation.reason)\'>" + message + "</failure>",
"<failure message='\(reason)\'>" + message + "</failure>",
"\t</testcase>"].joined(separator: "")
}).joined(separator: "") + "\n</testsuite></testsuites>"
}
Expand Down
32 changes: 32 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@
93E0C3CE1D67BD7F007FA25D /* ConditionalReturnsOnNewline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E0C3CD1D67BD7F007FA25D /* ConditionalReturnsOnNewline.swift */; };
B2902A0C1D66815600BFCCF7 /* PrivateUnitTestRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2902A0B1D66815600BFCCF7 /* PrivateUnitTestRule.swift */; };
B2902A0E1D6681F700BFCCF7 /* PrivateUnitTestConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2902A0D1D6681F700BFCCF7 /* PrivateUnitTestConfiguration.swift */; };
B3935371E92E0CF3F7668303 /* CannedJunitReporterOutput.xml in Resources */ = {isa = PBXBuildFile; fileRef = B39359A325FE84B7EDD1C455 /* CannedJunitReporterOutput.xml */; };
B3935522DC192D38D4852FA3 /* CannedXcodeReporterOutput.txt in Resources */ = {isa = PBXBuildFile; fileRef = B39352E4EA2A06BE66BD661A /* CannedXcodeReporterOutput.txt */; };
B39357173B43C9B5E351C360 /* CannedCheckstyleReporterOutput.xml in Resources */ = {isa = PBXBuildFile; fileRef = B39356DE1F73BDA1CA21C504 /* CannedCheckstyleReporterOutput.xml */; };
B3935797FF80C7F97953D375 /* CannedHTMLReporterOutput.html in Resources */ = {isa = PBXBuildFile; fileRef = B3935250C8E0DBACFB27E021 /* CannedHTMLReporterOutput.html */; };
B39358AA2D2AF5219D3FD7C0 /* CannedEmojiReporterOutput.txt in Resources */ = {isa = PBXBuildFile; fileRef = B3935001033261E5A70CE101 /* CannedEmojiReporterOutput.txt */; };
B3935A1C3BCA03A6B902E7AF /* CannedJSONReporterOutput.json in Resources */ = {isa = PBXBuildFile; fileRef = B39350463894A3FC1338E0AF /* CannedJSONReporterOutput.json */; };
B3935A32BE03C4D11B4364D6 /* CannedCSVReporterOutput.csv in Resources */ = {isa = PBXBuildFile; fileRef = B3935939C8366514D2694722 /* CannedCSVReporterOutput.csv */; };
B3935EE74B1E8E14FBD65E7F /* String+XML.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39353F28BCCA39247B316BD /* String+XML.swift */; };
B58AEED61C492C7B00E901FD /* ForceUnwrappingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */; };
BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */; };
D0AAAB5019FB0960007B24B3 /* SwiftLintFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D1216D19E87B05005E4BAA /* SwiftLintFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -268,6 +276,14 @@
93E0C3CD1D67BD7F007FA25D /* ConditionalReturnsOnNewline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConditionalReturnsOnNewline.swift; sourceTree = "<group>"; };
B2902A0B1D66815600BFCCF7 /* PrivateUnitTestRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateUnitTestRule.swift; sourceTree = "<group>"; };
B2902A0D1D6681F700BFCCF7 /* PrivateUnitTestConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateUnitTestConfiguration.swift; sourceTree = "<group>"; };
B3935001033261E5A70CE101 /* CannedEmojiReporterOutput.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CannedEmojiReporterOutput.txt; sourceTree = "<group>"; };
B39350463894A3FC1338E0AF /* CannedJSONReporterOutput.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = CannedJSONReporterOutput.json; sourceTree = "<group>"; };
B3935250C8E0DBACFB27E021 /* CannedHTMLReporterOutput.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = CannedHTMLReporterOutput.html; sourceTree = "<group>"; };
B39352E4EA2A06BE66BD661A /* CannedXcodeReporterOutput.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CannedXcodeReporterOutput.txt; sourceTree = "<group>"; };
B39353F28BCCA39247B316BD /* String+XML.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+XML.swift"; sourceTree = "<group>"; };
B39356DE1F73BDA1CA21C504 /* CannedCheckstyleReporterOutput.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CannedCheckstyleReporterOutput.xml; sourceTree = "<group>"; };
B3935939C8366514D2694722 /* CannedCSVReporterOutput.csv */ = {isa = PBXFileReference; lastKnownFileType = file.csv; path = CannedCSVReporterOutput.csv; sourceTree = "<group>"; };
B39359A325FE84B7EDD1C455 /* CannedJunitReporterOutput.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CannedJunitReporterOutput.xml; sourceTree = "<group>"; };
B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceUnwrappingRule.swift; sourceTree = "<group>"; };
BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingWhitespaceConfiguration.swift; sourceTree = "<group>"; };
D0D1211B19E87861005E4BAA /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; usesTabs = 0; };
Expand Down Expand Up @@ -420,6 +436,13 @@
3B12C9BF1C3209AC000B423F /* test.yml */,
3BDB224A1C345B4900473680 /* ProjectMock */,
F9D73F021D0CF15E00222FC4 /* test.txt */,
B3935250C8E0DBACFB27E021 /* CannedHTMLReporterOutput.html */,
B39359A325FE84B7EDD1C455 /* CannedJunitReporterOutput.xml */,
B39356DE1F73BDA1CA21C504 /* CannedCheckstyleReporterOutput.xml */,
B3935939C8366514D2694722 /* CannedCSVReporterOutput.csv */,
B39352E4EA2A06BE66BD661A /* CannedXcodeReporterOutput.txt */,
B3935001033261E5A70CE101 /* CannedEmojiReporterOutput.txt */,
B39350463894A3FC1338E0AF /* CannedJSONReporterOutput.json */,
);
path = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -777,6 +800,7 @@
3B5B9FE01C444DA20009AD27 /* Array+SwiftLint.swift */,
3BB47D841C51D80000AE6A10 /* NSRegularExpression+SwiftLint.swift */,
3BA79C9A1C4767910057E705 /* NSRange+SwiftLint.swift */,
B39353F28BCCA39247B316BD /* String+XML.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -912,6 +936,13 @@
3B12C9C11C3209CB000B423F /* test.yml in Resources */,
F9D73F031D0CF15E00222FC4 /* test.txt in Resources */,
3BDB224B1C345B4900473680 /* ProjectMock in Resources */,
B3935797FF80C7F97953D375 /* CannedHTMLReporterOutput.html in Resources */,
B3935371E92E0CF3F7668303 /* CannedJunitReporterOutput.xml in Resources */,
B39357173B43C9B5E351C360 /* CannedCheckstyleReporterOutput.xml in Resources */,
B3935A32BE03C4D11B4364D6 /* CannedCSVReporterOutput.csv in Resources */,
B3935522DC192D38D4852FA3 /* CannedXcodeReporterOutput.txt in Resources */,
B39358AA2D2AF5219D3FD7C0 /* CannedEmojiReporterOutput.txt in Resources */,
B3935A1C3BCA03A6B902E7AF /* CannedJSONReporterOutput.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1095,6 +1126,7 @@
D46E041D1DE3712C00728374 /* TrailingCommaRule.swift in Sources */,
E88198521BEA941300333A11 /* TodoRule.swift in Sources */,
3B828E531C546468000D180E /* RuleConfiguration.swift in Sources */,
B3935EE74B1E8E14FBD65E7F /* String+XML.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Loading