Cybersecurity-Projects/PROJECTS/intermediate/secrets-scanner/internal/reporter/sarif.go

169 lines
3.5 KiB
Go

// ©AngelaMos | 2026
// sarif.go
package reporter
import (
"encoding/json"
"io"
"github.com/CarterPerez-dev/portia/pkg/types"
)
type SARIF struct{}
type sarifLog struct {
Version string `json:"version"`
Schema string `json:"$schema"`
Runs []sarifRun `json:"runs"`
}
type sarifRun struct {
Tool sarifTool `json:"tool"`
Results []sarifResult `json:"results"`
}
type sarifTool struct {
Driver sarifDriver `json:"driver"`
}
type sarifDriver struct {
Name string `json:"name"`
Version string `json:"version"`
Rules []sarifRule `json:"rules,omitempty"`
}
type sarifRule struct {
ID string `json:"id"`
ShortDescription sarifMessage `json:"shortDescription"`
DefaultConfig sarifRuleConfig `json:"defaultConfiguration"`
}
type sarifRuleConfig struct {
Level string `json:"level"`
}
type sarifMessage struct {
Text string `json:"text"`
}
type sarifResult struct {
RuleID string `json:"ruleId"`
Level string `json:"level"`
Message sarifMessage `json:"message"`
Locations []sarifLocation `json:"locations"`
Properties map[string]any `json:"properties,omitempty"`
}
type sarifLocation struct {
PhysicalLocation sarifPhysicalLocation `json:"physicalLocation"`
}
type sarifPhysicalLocation struct {
ArtifactLocation sarifArtifactLocation `json:"artifactLocation"`
Region sarifRegion `json:"region"`
}
type sarifArtifactLocation struct {
URI string `json:"uri"`
}
type sarifRegion struct {
StartLine int `json:"startLine"`
}
func (s *SARIF) Report(
w io.Writer, result *types.ScanResult,
) error {
ruleMap := make(map[string]bool)
var sarifRules []sarifRule
for _, f := range result.Findings {
if ruleMap[f.RuleID] {
continue
}
ruleMap[f.RuleID] = true
sarifRules = append(sarifRules, sarifRule{
ID: f.RuleID,
ShortDescription: sarifMessage{Text: f.Description},
DefaultConfig: sarifRuleConfig{
Level: severityToSARIF(f.Severity),
},
})
}
var results []sarifResult
for _, f := range result.Findings {
r := sarifResult{
RuleID: f.RuleID,
Level: severityToSARIF(f.Severity),
Message: sarifMessage{Text: f.Description},
Locations: []sarifLocation{
{
PhysicalLocation: sarifPhysicalLocation{
ArtifactLocation: sarifArtifactLocation{
URI: f.FilePath,
},
Region: sarifRegion{
StartLine: f.LineNumber,
},
},
},
},
}
props := make(map[string]any)
if f.CommitSHA != "" {
props["commit"] = f.CommitSHA
}
if f.Entropy > 0 {
props["entropy"] = f.Entropy
}
if f.HIBPStatus == types.HIBPBreached {
props["hibp_breached"] = true
props["breach_count"] = f.BreachCount
}
if len(props) > 0 {
r.Properties = props
}
results = append(results, r)
}
log := sarifLog{
Version: "2.1.0",
Schema: "https://raw.githubusercontent.com/" +
"oasis-tcs/sarif-spec/main/sarif-2.1/" +
"schema/sarif-schema-2.1.0.json",
Runs: []sarifRun{
{
Tool: sarifTool{
Driver: sarifDriver{
Name: "portia",
Version: "1.0.0",
Rules: sarifRules,
},
},
Results: results,
},
},
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(log)
}
func severityToSARIF(sev types.Severity) string {
switch sev {
case types.SeverityCritical, types.SeverityHigh:
return "error"
case types.SeverityMedium:
return "warning"
case types.SeverityLow:
return "note"
default:
return "note"
}
}