You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +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:
		| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user