mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
SARIF format and GHIssue format improvements (#3646)
* Improve reporting * Fix location * Align casing * Fix severity mapping * Fix format * Improve title * Title format * Fix severity * Align title * Fix schema reference * Fix schema reference * Fix fmt * Fix fmt2 * Fix tests * fix(sarif): proper handling of omitempty in SnippetSarif * fix(fortifyExecuteScan): sarif format version * Addressing comments * Fix SARIF * fix(sarif): omitempty handling * fix(fortifyExecuteScan): pointer indirection * Added TODOs for audit data Co-authored-by: Xavier Goffin <x.goffin@sap.com> Co-authored-by: xgoffin <86716549+xgoffin@users.noreply.github.com> Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
parent
62a7f6336a
commit
f06890a9b2
@ -93,19 +93,21 @@ type VulnerabilityWithRemediation struct {
|
||||
|
||||
// Title returns the issue title representation of the contents
|
||||
func (v Vulnerability) Title() string {
|
||||
return fmt.Sprintf("%v/%v/%v-%v", "SECURITY_VULNERABILITY", v.VulnerabilityName, v.Name, v.Version)
|
||||
return fmt.Sprintf("%v/%v/%v/%v-%v", "SECURITY_VULNERABILITY", v.VulnerabilityWithRemediation.Severity, v.VulnerabilityName, v.Name, v.Version)
|
||||
}
|
||||
|
||||
// ToMarkdown returns the markdown representation of the contents
|
||||
func (v Vulnerability) ToMarkdown() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(
|
||||
`**Vulnerability %v**
|
||||
| Severity | Package | Installed Version | Description | Fix Resolution | Link |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
|%v|%v|%v|%v|%v|[%v](%v)|
|
||||
| Severity | Base (NVD) Score | Temporal Score | Package | Installed Version | Description | Fix Resolution | Link |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
|%v|%v|%v|%v|%v|%v|%v|[%v](%v)|
|
||||
`,
|
||||
v.VulnerabilityWithRemediation.VulnerabilityName,
|
||||
v.VulnerabilityWithRemediation.Severity,
|
||||
v.VulnerabilityWithRemediation.BaseScore,
|
||||
v.VulnerabilityWithRemediation.OverallScore,
|
||||
v.Name,
|
||||
v.Version,
|
||||
v.Description,
|
||||
@ -119,6 +121,8 @@ func (v Vulnerability) ToMarkdown() ([]byte, error) {
|
||||
func (v Vulnerability) ToTxt() string {
|
||||
return fmt.Sprintf(`Vulnerability %v
|
||||
Severity: %v
|
||||
Base (NVD) Score: %v
|
||||
Temporal Score: %v
|
||||
Package: %v
|
||||
Installed Version: %v
|
||||
Description: %v
|
||||
@ -126,6 +130,8 @@ Fix Resolution: %v
|
||||
Link: [%v](%v)`,
|
||||
v.VulnerabilityName,
|
||||
v.Severity,
|
||||
v.VulnerabilityWithRemediation.BaseScore,
|
||||
v.VulnerabilityWithRemediation.OverallScore,
|
||||
v.Name,
|
||||
v.Version,
|
||||
v.Description,
|
||||
|
@ -17,7 +17,7 @@ func CreateSarifResultFile(vulns *Vulnerabilities) *format.SARIF {
|
||||
//Now, we handle the sarif
|
||||
log.Entry().Debug("Creating SARIF file for data transfer")
|
||||
var sarif format.SARIF
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Version = "2.1.0"
|
||||
var wsRun format.Runs
|
||||
sarif.Runs = append(sarif.Runs, wsRun)
|
||||
@ -37,15 +37,19 @@ func CreateSarifResultFile(vulns *Vulnerabilities) *format.SARIF {
|
||||
id := v.Title()
|
||||
log.Entry().Debugf("Transforming alert %v into SARIF format", id)
|
||||
result.RuleID = id
|
||||
result.Level = v.VulnerabilityWithRemediation.Severity
|
||||
result.Level = transformToLevel(v.VulnerabilityWithRemediation.Severity)
|
||||
result.RuleIndex = i //Seems very abstract
|
||||
result.Message = new(format.Message)
|
||||
result.Message.Text = v.VulnerabilityWithRemediation.Description
|
||||
result.AnalysisTarget = new(format.ArtifactLocation)
|
||||
result.AnalysisTarget.URI = v.Name
|
||||
result.AnalysisTarget.Index = 0
|
||||
location := format.Location{PhysicalLocation: format.PhysicalLocation{ArtifactLocation: format.ArtifactLocation{URI: v.Name}, Region: format.Region{}, LogicalLocations: []format.LogicalLocation{{FullyQualifiedName: ""}}}}
|
||||
location := format.Location{PhysicalLocation: format.PhysicalLocation{ArtifactLocation: format.ArtifactLocation{URI: v.Name}}}
|
||||
result.Locations = append(result.Locations, location)
|
||||
//TODO add audit and tool related information, maybe fortifyCategory needs to become more general
|
||||
//result.Properties = new(format.SarifProperties)
|
||||
//result.Properties.ToolSeverity
|
||||
//result.Properties.ToolAuditMessage
|
||||
|
||||
sarifRule := *new(format.SarifRule)
|
||||
sarifRule.ID = id
|
||||
@ -54,21 +58,13 @@ func CreateSarifResultFile(vulns *Vulnerabilities) *format.SARIF {
|
||||
sarifRule.FullDescription = new(format.Message)
|
||||
sarifRule.FullDescription.Text = v.VulnerabilityWithRemediation.Description
|
||||
sarifRule.DefaultConfiguration = new(format.DefaultConfiguration)
|
||||
sarifRule.DefaultConfiguration.Level = v.Severity
|
||||
sarifRule.DefaultConfiguration.Level = transformToLevel(v.VulnerabilityWithRemediation.Severity)
|
||||
sarifRule.HelpURI = ""
|
||||
markdown, _ := v.ToMarkdown()
|
||||
sarifRule.Help = new(format.Help)
|
||||
sarifRule.Help.Text = v.ToTxt()
|
||||
sarifRule.Help.Markdown = string(markdown)
|
||||
|
||||
// Avoid empty descriptions to respect standard
|
||||
if sarifRule.ShortDescription.Text == "" {
|
||||
sarifRule.ShortDescription.Text = "None."
|
||||
}
|
||||
if sarifRule.FullDescription.Text == "" { // OR USE OMITEMPTY
|
||||
sarifRule.FullDescription.Text = "None."
|
||||
}
|
||||
|
||||
ruleProp := *new(format.SarifRuleProperties)
|
||||
ruleProp.Tags = append(ruleProp.Tags, "SECURITY_VULNERABILITY")
|
||||
ruleProp.Tags = append(ruleProp.Tags, v.VulnerabilityWithRemediation.Description)
|
||||
@ -87,6 +83,20 @@ func CreateSarifResultFile(vulns *Vulnerabilities) *format.SARIF {
|
||||
return &sarif
|
||||
}
|
||||
|
||||
func transformToLevel(severity string) string {
|
||||
switch severity {
|
||||
case "LOW":
|
||||
return "warning"
|
||||
case "MEDIUM":
|
||||
return "warning"
|
||||
case "HIGH":
|
||||
return "error"
|
||||
case "CRITICAL":
|
||||
return "error"
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// WriteVulnerabilityReports writes vulnerability information from ScanReport into dedicated outputs e.g. HTML
|
||||
func WriteVulnerabilityReports(scanReport reporting.ScanReport, utils piperutils.FileUtils) ([]piperutils.Path, error) {
|
||||
reportPaths := []piperutils.Path{}
|
||||
|
@ -24,7 +24,7 @@ func TestCreateSarifResultFile(t *testing.T) {
|
||||
|
||||
sarif := CreateSarifResultFile(&vulns)
|
||||
|
||||
assert.Equal(t, "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json", sarif.Schema)
|
||||
assert.Equal(t, "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", sarif.Schema)
|
||||
assert.Equal(t, "2.1.0", sarif.Version)
|
||||
assert.Equal(t, 1, len(sarif.Runs))
|
||||
assert.Equal(t, "Blackduck Hub Detect", sarif.Runs[0].Tool.Driver.Name)
|
||||
|
@ -2,7 +2,7 @@ package format
|
||||
|
||||
// SARIF format related JSON structs
|
||||
type SARIF struct {
|
||||
Schema string `json:"$schema" default:"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json"`
|
||||
Schema string `json:"$schema" default:"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"`
|
||||
Version string `json:"version" default:"2.1.0"`
|
||||
Runs []Runs `json:"runs"`
|
||||
}
|
||||
@ -30,7 +30,7 @@ type Results struct {
|
||||
Locations []Location `json:"locations,omitempty"`
|
||||
CodeFlows []CodeFlow `json:"codeFlows,omitempty"`
|
||||
RelatedLocations []RelatedLocation `json:"relatedLocations,omitempty"`
|
||||
Properties SarifProperties `json:"properties"`
|
||||
Properties *SarifProperties `json:"properties"`
|
||||
}
|
||||
|
||||
// Message to detail the finding
|
||||
@ -60,13 +60,13 @@ type ArtifactLocation struct {
|
||||
|
||||
// Region where the finding was detected
|
||||
type Region struct {
|
||||
StartLine int `json:"startLine,omitempty"`
|
||||
StartColumn int `json:"startColumn,omitempty"`
|
||||
EndLine int `json:"EndLine,omitempty"`
|
||||
EndColumn int `json:"EndColumn,omitempty"`
|
||||
ByteOffset int `json:"ByteOffset,omitempty"`
|
||||
ByteLength int `json:"ByteLength,omitempty"`
|
||||
Snippet SnippetSarif `json:"snippet"`
|
||||
StartLine int `json:"startLine,omitempty"`
|
||||
StartColumn int `json:"startColumn,omitempty"`
|
||||
EndLine int `json:"endLine,omitempty"`
|
||||
EndColumn int `json:"endColumn,omitempty"`
|
||||
ByteOffset int `json:"byteOffset,omitempty"`
|
||||
ByteLength int `json:"byteLength,omitempty"`
|
||||
Snippet *SnippetSarif `json:"snippet,omitempty"`
|
||||
}
|
||||
|
||||
// LogicalLocation of the finding
|
||||
@ -76,17 +76,17 @@ type LogicalLocation struct {
|
||||
|
||||
// SarifProperties adding additional information/context to the finding
|
||||
type SarifProperties struct {
|
||||
InstanceID string `json:"InstanceID,omitempty"`
|
||||
InstanceSeverity string `json:"InstanceSeverity,omitempty"`
|
||||
Confidence string `json:"Confidence,omitempty"`
|
||||
FortifyCategory string `json:"FortifyCategory,omitempty"`
|
||||
Audited bool `json:"Audited"`
|
||||
ToolSeverity string `json:"ToolSeverity"`
|
||||
ToolSeverityIndex int `json:"ToolSeverityIndex"`
|
||||
ToolState string `json:"ToolState"`
|
||||
ToolStateIndex int `json:"ToolStateIndex"`
|
||||
ToolAuditMessage string `json:"ToolAuditMessage"`
|
||||
UnifiedAuditState string `json:"UnifiedAuditState"`
|
||||
InstanceID string `json:"instanceID,omitempty"`
|
||||
InstanceSeverity string `json:"instanceSeverity,omitempty"`
|
||||
Confidence string `json:"confidence,omitempty"`
|
||||
FortifyCategory string `json:"fortifyCategory,omitempty"`
|
||||
Audited bool `json:"audited"`
|
||||
ToolSeverity string `json:"toolSeverity"`
|
||||
ToolSeverityIndex int `json:"toolSeverityIndex"`
|
||||
ToolState string `json:"toolState"`
|
||||
ToolStateIndex int `json:"toolStateIndex"`
|
||||
ToolAuditMessage string `json:"toolAuditMessage"`
|
||||
UnifiedAuditState string `json:"unifiedAuditState"`
|
||||
}
|
||||
|
||||
// Tool these structs are relevant to the Tool object
|
||||
@ -130,9 +130,9 @@ type SnippetSarif struct {
|
||||
|
||||
// ContextRegion provides the context for the finding
|
||||
type ContextRegion struct {
|
||||
StartLine int `json:"startLine"`
|
||||
EndLine int `json:"endLine"`
|
||||
Snippet SnippetSarif `json:"snippet"`
|
||||
StartLine int `json:"startLine,omitempty"`
|
||||
EndLine int `json:"endLine,omitempty"`
|
||||
Snippet *SnippetSarif `json:"snippet,omitempty"`
|
||||
}
|
||||
|
||||
// CodeFlow
|
||||
@ -185,7 +185,7 @@ type DefaultConfiguration struct {
|
||||
|
||||
// DefaultProperties
|
||||
type DefaultProperties struct {
|
||||
DefaultSeverity string `json:"DefaultSeverity,omitempty"`
|
||||
DefaultSeverity string `json:"defaultSeverity,omitempty"`
|
||||
}
|
||||
|
||||
// Relationships
|
||||
@ -208,9 +208,9 @@ type ToolComponent struct {
|
||||
|
||||
//SarifRuleProperties
|
||||
type SarifRuleProperties struct {
|
||||
Accuracy string `json:"Accuracy,omitempty"`
|
||||
Impact string `json:"Impact,omitempty"`
|
||||
Probability string `json:"Probability,omitempty"`
|
||||
Accuracy string `json:"accuracy,omitempty"`
|
||||
Impact string `json:"impact,omitempty"`
|
||||
Probability string `json:"probability,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Precision string `json:"precision,omitempty"`
|
||||
}
|
||||
@ -239,7 +239,7 @@ type Descriptor struct {
|
||||
|
||||
// InvocationProperties
|
||||
type InvocationProperties struct {
|
||||
Platform string `json:"Platform"`
|
||||
Platform string `json:"platform"`
|
||||
}
|
||||
|
||||
// OriginalUriBaseIds These structs are relevant to the originalUriBaseIds object
|
||||
|
@ -547,7 +547,7 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
|
||||
//Now, we handle the sarif
|
||||
var sarif format.SARIF
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Version = "2.1.0"
|
||||
var fortifyRun format.Runs
|
||||
fortifyRun.ColumnKind = "utf16CodeUnits"
|
||||
@ -608,7 +608,9 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
if fvdl.Snippets[j].SnippetId == targetSnippetId {
|
||||
threadFlowLocation.Location.PhysicalLocation.ContextRegion.StartLine = fvdl.Snippets[j].StartLine
|
||||
threadFlowLocation.Location.PhysicalLocation.ContextRegion.EndLine = fvdl.Snippets[j].EndLine
|
||||
threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet.Text = fvdl.Snippets[j].Text
|
||||
snippetSarif := new(format.SnippetSarif)
|
||||
snippetSarif.Text = fvdl.Snippets[j].Text
|
||||
threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet = snippetSarif
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -654,13 +656,17 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
break
|
||||
}
|
||||
}
|
||||
snippetSarif := new(format.SnippetSarif)
|
||||
if snippetText != "" {
|
||||
threadFlowLocation.Location.PhysicalLocation.Region.Snippet.Text = snippetText
|
||||
snippetSarif.Text = snippetText
|
||||
} else {
|
||||
threadFlowLocation.Location.PhysicalLocation.Region.Snippet.Text = threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
snippetSarif.Text = threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
}
|
||||
threadFlowLocation.Location.PhysicalLocation.Region.Snippet = snippetSarif
|
||||
} else {
|
||||
threadFlowLocation.Location.PhysicalLocation.Region.Snippet.Text = threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
snippetSarif := new(format.SnippetSarif)
|
||||
snippetSarif.Text = threadFlowLocation.Location.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
threadFlowLocation.Location.PhysicalLocation.Region.Snippet = snippetSarif
|
||||
}
|
||||
location = *threadFlowLocation.Location
|
||||
//set Kinds
|
||||
@ -691,7 +697,7 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
result.RelatedLocations = append(result.RelatedLocations, relatedLocation)
|
||||
|
||||
//handle properties
|
||||
prop := *new(format.SarifProperties)
|
||||
prop := new(format.SarifProperties)
|
||||
prop.InstanceSeverity = fvdl.Vulnerabilities.Vulnerability[i].InstanceInfo.InstanceSeverity
|
||||
prop.Confidence = fvdl.Vulnerabilities.Vulnerability[i].InstanceInfo.Confidence
|
||||
prop.InstanceID = fvdl.Vulnerabilities.Vulnerability[i].InstanceInfo.InstanceID
|
||||
@ -704,7 +710,7 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
prop.ToolState = "Not an Issue"
|
||||
prop.ToolStateIndex = 1
|
||||
} else if sys != nil {
|
||||
if err := integrateAuditData(&prop, fvdl.Vulnerabilities.Vulnerability[i].InstanceInfo.InstanceID, sys, project, projectVersion, filterSet); err != nil {
|
||||
if err := integrateAuditData(prop, fvdl.Vulnerabilities.Vulnerability[i].InstanceInfo.InstanceID, sys, project, projectVersion, filterSet); err != nil {
|
||||
log.Entry().Debug(err)
|
||||
prop.Audited = false
|
||||
prop.ToolState = "Unknown"
|
||||
@ -942,7 +948,9 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
if fvdl.Snippets[j].SnippetId == targetSnippetId {
|
||||
loc.PhysicalLocation.ContextRegion.StartLine = fvdl.Snippets[j].StartLine
|
||||
loc.PhysicalLocation.ContextRegion.EndLine = fvdl.Snippets[j].EndLine
|
||||
loc.PhysicalLocation.ContextRegion.Snippet.Text = fvdl.Snippets[j].Text
|
||||
snippetSarif := new(format.SnippetSarif)
|
||||
snippetSarif.Text = fvdl.Snippets[j].Text
|
||||
loc.PhysicalLocation.ContextRegion.Snippet = snippetSarif
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -976,18 +984,22 @@ func Parse(sys System, project *models.Project, projectVersion *models.ProjectVe
|
||||
default:
|
||||
snippetTarget = fvdl.UnifiedNodePool.Node[i].Action.ActionData
|
||||
}
|
||||
physLocationSnippetLines := strings.Split(loc.PhysicalLocation.ContextRegion.Snippet.Text, "\n")
|
||||
snippetText := ""
|
||||
for j := 0; j < len(physLocationSnippetLines); j++ {
|
||||
if strings.Contains(physLocationSnippetLines[j], snippetTarget) {
|
||||
snippetText = physLocationSnippetLines[j]
|
||||
break
|
||||
if loc.PhysicalLocation.ContextRegion.Snippet != nil {
|
||||
physLocationSnippetLines := strings.Split(loc.PhysicalLocation.ContextRegion.Snippet.Text, "\n")
|
||||
snippetText := ""
|
||||
for j := 0; j < len(physLocationSnippetLines); j++ {
|
||||
if strings.Contains(physLocationSnippetLines[j], snippetTarget) {
|
||||
snippetText = physLocationSnippetLines[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if snippetText != "" {
|
||||
loc.PhysicalLocation.Region.Snippet.Text = snippetText
|
||||
} else {
|
||||
loc.PhysicalLocation.Region.Snippet.Text = loc.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
snippetSarif := new(format.SnippetSarif)
|
||||
if snippetText != "" {
|
||||
snippetSarif.Text = snippetText
|
||||
} else {
|
||||
snippetSarif.Text = loc.PhysicalLocation.ContextRegion.Snippet.Text
|
||||
}
|
||||
loc.PhysicalLocation.Region.Snippet = snippetSarif
|
||||
}
|
||||
locations.Location = loc
|
||||
locations.Kinds = append(locations.Kinds, "unknown")
|
||||
|
@ -175,7 +175,7 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
|
||||
//Now, we handle the sarif
|
||||
log.Entry().Debug("Creating SARIF file for data transfer")
|
||||
var sarif format.SARIF
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Schema = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"
|
||||
sarif.Version = "2.1.0"
|
||||
var wsRun format.Runs
|
||||
sarif.Runs = append(sarif.Runs, wsRun)
|
||||
@ -194,18 +194,20 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
|
||||
id := fmt.Sprintf("%v/%v/%v", alert.Type, alert.Vulnerability.Name, alert.Library.ArtifactID)
|
||||
log.Entry().Debugf("Transforming alert %v into SARIF format", id)
|
||||
result.RuleID = id
|
||||
result.Level = alert.Level
|
||||
result.Level = transformToLevel(alert.Vulnerability.Severity, alert.Vulnerability.CVSS3Severity)
|
||||
result.RuleIndex = i //Seems very abstract
|
||||
msg := new(format.Message)
|
||||
msg.Text = alert.Vulnerability.Description
|
||||
result.Message = msg
|
||||
result.Level = alert.Level
|
||||
result.Message = new(format.Message)
|
||||
result.Message.Text = alert.Vulnerability.Description
|
||||
artLoc := new(format.ArtifactLocation)
|
||||
artLoc.Index = 0
|
||||
artLoc.URI = alert.Library.Filename
|
||||
result.AnalysisTarget = artLoc
|
||||
location := format.Location{PhysicalLocation: format.PhysicalLocation{ArtifactLocation: format.ArtifactLocation{URI: alert.Library.Filename}, Region: format.Region{}, LogicalLocations: []format.LogicalLocation{{FullyQualifiedName: ""}}}, Message: nil}
|
||||
location := format.Location{PhysicalLocation: format.PhysicalLocation{ArtifactLocation: format.ArtifactLocation{URI: alert.Library.Filename}}}
|
||||
result.Locations = append(result.Locations, location)
|
||||
//TODO add audit and tool related information, maybe fortifyCategory needs to become more general
|
||||
//result.Properties = new(format.SarifProperties)
|
||||
//result.Properties.ToolSeverity
|
||||
//result.Properties.ToolAuditMessage
|
||||
|
||||
sarifRule := *new(format.SarifRule)
|
||||
sarifRule.ID = id
|
||||
@ -216,7 +218,7 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
|
||||
fd.Text = alert.Vulnerability.Description
|
||||
sarifRule.FullDescription = fd
|
||||
defaultConfig := new(format.DefaultConfiguration)
|
||||
defaultConfig.Level = alert.Level
|
||||
defaultConfig.Level = transformToLevel(alert.Vulnerability.Severity, alert.Vulnerability.CVSS3Severity)
|
||||
sarifRule.DefaultConfiguration = defaultConfig
|
||||
sarifRule.HelpURI = alert.Vulnerability.URL
|
||||
markdown, _ := alert.ToMarkdown()
|
||||
@ -224,14 +226,6 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
|
||||
sarifRule.Help.Text = alert.ToTxt()
|
||||
sarifRule.Help.Markdown = string(markdown)
|
||||
|
||||
// Avoid empty descriptions to respect standard
|
||||
if sarifRule.ShortDescription.Text == "" {
|
||||
sarifRule.ShortDescription.Text = "None."
|
||||
}
|
||||
if sarifRule.FullDescription.Text == "" { // OR USE OMITEMPTY
|
||||
sarifRule.FullDescription.Text = "None."
|
||||
}
|
||||
|
||||
ruleProp := *new(format.SarifRuleProperties)
|
||||
ruleProp.Tags = append(ruleProp.Tags, alert.Type)
|
||||
ruleProp.Tags = append(ruleProp.Tags, alert.Description)
|
||||
@ -249,6 +243,26 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
|
||||
return &sarif
|
||||
}
|
||||
|
||||
func transformToLevel(cvss2severity, cvss3severity string) string {
|
||||
switch cvss3severity {
|
||||
case "low":
|
||||
return "warning"
|
||||
case "medium":
|
||||
return "warning"
|
||||
case "high":
|
||||
return "error"
|
||||
}
|
||||
switch cvss2severity {
|
||||
case "low":
|
||||
return "warning"
|
||||
case "medium":
|
||||
return "warning"
|
||||
case "high":
|
||||
return "error"
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// WriteSarifFile write a JSON sarif format file for upload into e.g. GCP
|
||||
func WriteSarifFile(sarif *format.SARIF, utils piperutils.FileUtils) ([]piperutils.Path, error) {
|
||||
reportPaths := []piperutils.Path{}
|
||||
|
@ -69,7 +69,7 @@ func TestCreateSarifResultFile(t *testing.T) {
|
||||
|
||||
sarif := CreateSarifResultFile(scan, &alerts)
|
||||
|
||||
assert.Equal(t, "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos01/schemas/sarif-schema-2.1.0.json", sarif.Schema)
|
||||
assert.Equal(t, "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", sarif.Schema)
|
||||
assert.Equal(t, "2.1.0", sarif.Version)
|
||||
assert.Equal(t, 1, len(sarif.Runs))
|
||||
assert.Equal(t, "Some test agent", sarif.Runs[0].Tool.Driver.Name)
|
||||
|
@ -57,19 +57,51 @@ type Alert struct {
|
||||
|
||||
// Title returns the issue title representation of the contents
|
||||
func (a Alert) Title() string {
|
||||
return fmt.Sprintf("%v/%v/%v", a.Type, a.Vulnerability.Name, a.Library.ArtifactID)
|
||||
return fmt.Sprintf("%v/%v/%v/%v", a.Type, consolidate(a.Vulnerability.Severity, a.Vulnerability.CVSS3Severity, a.Vulnerability.Score, a.Vulnerability.CVSS3Score), a.Vulnerability.Name, a.Library.ArtifactID)
|
||||
}
|
||||
|
||||
func consolidate(cvss2severity, cvss3severity string, cvss2score, cvss3score float64) string {
|
||||
switch cvss3severity {
|
||||
case "low":
|
||||
return "LOW"
|
||||
case "medium":
|
||||
return "MEDIUM"
|
||||
case "high":
|
||||
if cvss3score >= 9 {
|
||||
return "CRITICAL"
|
||||
}
|
||||
return "HIGH"
|
||||
}
|
||||
switch cvss2severity {
|
||||
case "low":
|
||||
return "LOW"
|
||||
case "medium":
|
||||
return "MEDIUM"
|
||||
case "high":
|
||||
if cvss2score >= 9 {
|
||||
return "CRITICAL"
|
||||
}
|
||||
return "HIGH"
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// ToMarkdown returns the markdown representation of the contents
|
||||
func (a Alert) ToMarkdown() ([]byte, error) {
|
||||
score := a.Vulnerability.CVSS3Score
|
||||
if score == 0 {
|
||||
score = a.Vulnerability.Score
|
||||
}
|
||||
return []byte(fmt.Sprintf(
|
||||
`**Vulnerability %v**
|
||||
| Severity | Package | Installed Version | Description | Fix Resolution | Link |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
|%v|%v|%v|%v|%v|[%v](%v)|
|
||||
| Severity | Base (NVD) Score | Temporal Score | Package | Installed Version | Description | Fix Resolution | Link |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
|%v|%v|%v|%v|%v|%v|%v|[%v](%v)|
|
||||
`,
|
||||
a.Vulnerability.Name,
|
||||
a.Vulnerability.Severity,
|
||||
score,
|
||||
score,
|
||||
a.Library.ArtifactID,
|
||||
a.Library.Version,
|
||||
a.Vulnerability.Description,
|
||||
@ -81,8 +113,14 @@ func (a Alert) ToMarkdown() ([]byte, error) {
|
||||
|
||||
// ToTxt returns the textual representation of the contents
|
||||
func (a Alert) ToTxt() string {
|
||||
score := a.Vulnerability.CVSS3Score
|
||||
if score == 0 {
|
||||
score = a.Vulnerability.Score
|
||||
}
|
||||
return fmt.Sprintf(`Vulnerability %v
|
||||
Severity: %v
|
||||
Base (NVD) Score: %v
|
||||
Temporal Score: %v
|
||||
Package: %v
|
||||
Installed Version: %v
|
||||
Description: %v
|
||||
@ -90,6 +128,8 @@ Fix Resolution: %v
|
||||
Link: [%v](%v)`,
|
||||
a.Vulnerability.Name,
|
||||
a.Vulnerability.Severity,
|
||||
score,
|
||||
score,
|
||||
a.Library.ArtifactID,
|
||||
a.Library.Version,
|
||||
a.Vulnerability.Description,
|
||||
|
Loading…
Reference in New Issue
Block a user