Skip to content

Commit

Permalink
Add SARIF reporter (#5516)
Browse files Browse the repository at this point in the history
  • Loading branch information
waitbutY committed Apr 13, 2024
1 parent 2d4f0bc commit 5315c3d
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

#### Enhancements

* Add a reporter that outputs violations in the Static
Analysis Results Interchange Format (SARIF).
[waitButY](https://github.com/waitbutY)

* Ignore absence of a non-initial local config instead of
falling back to default.
[kohtenko](https://github.com/kohtenko)
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintCore/Models/ReportersList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public let reportersList: [any Reporter.Type] = [
JUnitReporter.self,
MarkdownReporter.self,
RelativePathReporter.self,
SARIFReporter.self,
SonarQubeReporter.self,
SummaryReporter.self,
XcodeReporter.self
Expand Down
75 changes: 75 additions & 0 deletions Source/SwiftLintCore/Reporters/SARIFReporter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Foundation
import SourceKittenFramework

/// To some tools (i.e. Datadog), code quality findings are reported in the SARIF format:
/// - Full Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html
/// - Samples: https://github.com/microsoft/sarif-tutorials/blob/main/samples/
/// - JSON Validator: https://sarifweb.azurewebsites.net/Validation
struct SARIFReporter: Reporter {
// MARK: - Reporter Conformance
static let identifier = "sarif"
static let isRealtime = false
static let description = "Reports violations in the Static Analysis Results Interchange Format (SARIF)"
static let swiftlintVersion = "https://github.com/realm/SwiftLint/blob/\(Version.current.value)/README.md"

static func generateReport(_ violations: [StyleViolation]) -> String {
let SARIFJson = [
"version": "2.1.0",
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
"runs": [
[
"tool": [
"driver": [
"name": "SwiftLint",
"semanticVersion": Version.current.value,
"informationUri": swiftlintVersion
]
],
"results": violations.map(dictionary(for:))
]
]
] as [String: Any]

return toJSON(SARIFJson)
}

// MARK: - Private

private static func dictionary(for violation: StyleViolation) -> [String: Any] {
return [
"level": violation.severity.rawValue,
"ruleId": violation.ruleIdentifier,
"message": [
"text": violation.reason
],
"locations": [
dictionary(for: violation.location)
]
]
}

private static func dictionary(for location: Location) -> [String: Any] {
// According to SARIF specification JSON1008, minimum value for line is 1
if let line = location.line, line > 0 {
return [
"physicalLocation": [
"artifactLocation": [
"uri": location.file ?? ""
],
"region": [
"startLine": line,
"startColumn": location.character ?? "1"
]
]
]
}

return [
"physicalLocation": [
"artifactLocation": [
"uri": location.file ?? ""
]
]
]
}
}
6 changes: 6 additions & 0 deletions Tests/SwiftLintFrameworkTests/ReporterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ class ReporterTests: SwiftLintTestCase {
XCTAssertEqual(result, expectedOutput)
}

func testSARIFReporter() {
let expectedOutput = stringFromFile("CannedSARIFReporterOutput.json")
let result = SARIFReporter.generateReport(generateViolations())
XCTAssertEqual(expectedOutput, result)
}

func testJunitReporter() {
let expectedOutput = stringFromFile("CannedJunitReporterOutput.xml")
let result = JUnitReporter.generateReport(generateViolations())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"$schema" : "https:\/\/docs.oasis-open.org\/sarif\/sarif\/v2.1.0\/cos02\/schemas\/sarif-schema-2.1.0.json",
"runs" : [
{
"results" : [
{
"level" : "warning",
"locations" : [
{
"physicalLocation" : {
"artifactLocation" : {
"uri" : "filename"
},
"region" : {
"startColumn" : 2,
"startLine" : 1
}
}
}
],
"message" : {
"text" : "Violation Reason"
},
"ruleId" : "line_length"
},
{
"level" : "error",
"locations" : [
{
"physicalLocation" : {
"artifactLocation" : {
"uri" : "filename"
},
"region" : {
"startColumn" : 2,
"startLine" : 1
}
}
}
],
"message" : {
"text" : "Violation Reason"
},
"ruleId" : "line_length"
},
{
"level" : "error",
"locations" : [
{
"physicalLocation" : {
"artifactLocation" : {
"uri" : "filename"
},
"region" : {
"startColumn" : 2,
"startLine" : 1
}
}
}
],
"message" : {
"text" : "Shorthand syntactic sugar should be used, i.e. [Int] instead of Array<Int>"
},
"ruleId" : "syntactic_sugar"
},
{
"level" : "error",
"locations" : [
{
"physicalLocation" : {
"artifactLocation" : {
"uri" : ""
}
}
}
],
"message" : {
"text" : "Colons should be next to the identifier when specifying a type and next to the key in dictionary literals"
},
"ruleId" : "colon"
}
],
"tool" : {
"driver" : {
"informationUri" : "https:\/\/github.com\/realm\/SwiftLint\/blob\/0.54.0\/README.md",
"name" : "SwiftLint",
"semanticVersion" : "0.54.0"
}
}
}
],
"version" : "2.1.0"
}

0 comments on commit 5315c3d

Please sign in to comment.