///|
fn sarif_level(severity : Severity) -> String {
match severity {
Error => "error"
Warning => "warning"
Info => "note"
}
}
///|
fn sarif_rule_name(code : StringView) -> String {
match code {
"MS001" => "module-name-required"
"MS002" => "module-version-required"
"MS003" => "license-declaration-required"
"MS004" => "valid-spdx-expression"
"MS005" => "repository-url-required"
"MS006" => "license-file-required"
"MS007" => "readme-required"
"MS008" => "continuous-integration-recommended"
"MS009" => "changelog-recommended"
"MS010" => "gitignore-recommended"
"MS011" => "license-declaration-matches-text"
"MS012" => "license-text-recognized"
"MS013" => "moonbit-source-required"
"MS014" => "repository-url-valid"
_ => "moonseal-finding"
}
}
///|
fn sarif_rule_description(code : StringView) -> String {
match code {
"MS001" => "MoonBit modules must declare a package name."
"MS002" => "Publishable MoonBit modules must declare a version."
"MS003" => "Open-source modules must declare an SPDX license."
"MS004" => "License declarations must use a supported SPDX expression."
"MS005" => "Published modules should link to their source repository."
"MS006" => "Open-source repositories must include a license file."
"MS007" => "Repositories must include usage documentation."
"MS008" => "Repositories should continuously build and test changes."
"MS009" => "Published tools should document user-visible changes."
"MS010" => "Repositories should exclude generated artifacts."
"MS011" => "The manifest declaration must agree with the license text."
"MS012" => "MoonSeal should be able to identify the license text."
"MS013" => "A MoonBit project must contain MoonBit source code."
"MS014" => "Repository metadata should use a valid Git transport URL."
_ => "MoonSeal release-readiness finding."
}
}
///|
fn sarif_help_uri(code : StringView) -> String {
"https://github.com/liyun6666/moonseal#\{sarif_rule_name(code)}"
}
///|
fn sarif_unique_codes(findings : Array[Finding]) -> Array[String] {
let codes = []
for finding in findings {
if !codes.any(fn(code) { code == finding.code }) {
codes.push(finding.code)
}
}
codes
}
///|
fn sarif_rule(code : String) -> Json {
{
"id": code,
"name": sarif_rule_name(code),
"shortDescription": { "text": sarif_rule_description(code) },
"fullDescription": { "text": sarif_rule_description(code) },
"helpUri": sarif_help_uri(code),
"defaultConfiguration": {
"level": if code == "MS010" {
"note"
} else {
"warning"
},
},
"properties": {
"precision": "very-high",
"tags": ["MoonBit", "supply-chain", "release-readiness"],
},
}
}
///|
fn sarif_location(path : String) -> Json {
{
"physicalLocation": {
"artifactLocation": { "uri": path.replace_all(old="\\", new="/") },
"region": { "startLine": 1, "startColumn": 1 },
},
}
}
///|
fn sarif_result(finding : Finding) -> Json {
let locations : Array[Json] = if finding.path.is_empty() {
[]
} else {
[sarif_location(finding.path)]
}
{
"ruleId": finding.code,
"level": sarif_level(finding.severity),
"message": { "text": finding.message },
"locations": locations,
"properties": {
"moonsealSeverity": finding.severity.label(),
"category": "release-readiness",
},
}
}
///|
/// Convert an audit report to SARIF 2.1.0 for GitHub Code Scanning and other
/// interoperable static-analysis consumers.
pub fn sarif(report : AuditReport) -> Json {
let rules : Array[Json] = sarif_unique_codes(report.findings).map(sarif_rule)
let results : Array[Json] = report.findings.map(sarif_result)
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "MoonSeal",
"informationUri": "https://github.com/liyun6666/moonseal",
"semanticVersion": "0.2.1",
"rules": rules,
},
},
"automationDetails": {
"id": "moonseal/\{report.project.name}",
"description": { "text": "MoonSeal release-readiness audit" },
},
"invocations": [
{
"executionSuccessful": true,
"properties": {
"ready": report.is_ready(),
"score": report.score,
"moonbitSourceLines": report.moonbit_source_lines,
},
},
],
"results": results,
"properties": {
"projectName": report.project.name,
"projectVersion": report.project.version,
"errorCount": report.error_count(),
"warningCount": report.warning_count(),
},
},
],
}
}
///|
pub fn render_sarif(report : AuditReport, indent? : Int = 2) -> String {
sarif(report).stringify(indent~)
}