1
0
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:
Sven Merk 2022-03-22 14:47:19 +01:00 committed by GitHub
parent 62a7f6336a
commit f06890a9b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 167 additions and 85 deletions

View File

@ -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,

View File

@ -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{}

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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{}

View File

@ -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)

View File

@ -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,