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 
			
		
		
		
	Simplify and gzip Fortify .sarif files (#4181)
* fix(Fortify):simplify plain text .sarif and gzip the complete result * fix(Fortify):no longer add snippet text to .sarif to reduce file size (still keep end/start lines) * fix: formatting
This commit is contained in:
		| @@ -306,14 +306,21 @@ func runFortifyScan(ctx context.Context, config fortifyExecuteScanOptions, sys f | ||||
| 	if config.ConvertToSarif { | ||||
| 		resultFilePath := fmt.Sprintf("%vtarget/result.fpr", config.ModulePath) | ||||
| 		log.Entry().Info("Calling conversion to SARIF function.") | ||||
| 		sarif, err := fortify.ConvertFprToSarif(sys, projectVersion, resultFilePath, filterSet) | ||||
| 		sarif, sarifSimplified, err := fortify.ConvertFprToSarif(sys, projectVersion, resultFilePath, filterSet) | ||||
| 		if err != nil { | ||||
| 			return reports, fmt.Errorf("failed to generate SARIF") | ||||
| 		} | ||||
| 		log.Entry().Debug("Writing sarif file to disk.") | ||||
| 		paths, err := fortify.WriteSarif(sarif) | ||||
| 		log.Entry().Debug("Writing simplified sarif file in plain text to disk.") | ||||
| 		paths, err := fortify.WriteSarif(sarifSimplified, "result.sarif") | ||||
| 		if err != nil { | ||||
| 			return reports, fmt.Errorf("failed to write sarif") | ||||
| 			return reports, fmt.Errorf("failed to write simplified sarif") | ||||
| 		} | ||||
| 		reports = append(reports, paths...) | ||||
|  | ||||
| 		log.Entry().Debug("Writing full sarif file to disk and gzip it.") | ||||
| 		paths, err = fortify.WriteGzipSarif(sarif, "result.sarif.gz") | ||||
| 		if err != nil { | ||||
| 			return reports, fmt.Errorf("failed to write gzip sarif") | ||||
| 		} | ||||
| 		reports = append(reports, paths...) | ||||
| 	} | ||||
|   | ||||
| @@ -498,30 +498,31 @@ type Attribute struct { | ||||
| } | ||||
|  | ||||
| // ConvertFprToSarif converts the FPR file contents into SARIF format | ||||
| func ConvertFprToSarif(sys System, projectVersion *models.ProjectVersion, resultFilePath string, filterSet *models.FilterSet) (format.SARIF, error) { | ||||
| func ConvertFprToSarif(sys System, projectVersion *models.ProjectVersion, resultFilePath string, filterSet *models.FilterSet) (format.SARIF, format.SARIF, error) { | ||||
| 	log.Entry().Debug("Extracting FPR.") | ||||
| 	var sarif format.SARIF | ||||
| 	var sarifSimplified format.SARIF | ||||
| 	tmpFolder, err := ioutil.TempDir(".", "temp-") | ||||
| 	defer os.RemoveAll(tmpFolder) | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).WithField("path", tmpFolder).Debug("Creating temp directory failed") | ||||
| 		return sarif, err | ||||
| 		return sarif, sarifSimplified, err | ||||
| 	} | ||||
|  | ||||
| 	_, err = piperutils.Unzip(resultFilePath, tmpFolder) | ||||
| 	if err != nil { | ||||
| 		return sarif, err | ||||
| 		return sarif, sarifSimplified, err | ||||
| 	} | ||||
|  | ||||
| 	log.Entry().Debug("Reading audit file.") | ||||
| 	data, err := ioutil.ReadFile(filepath.Join(tmpFolder, "audit.fvdl")) | ||||
| 	if err != nil { | ||||
| 		return sarif, err | ||||
| 		return sarif, sarifSimplified, err | ||||
| 	} | ||||
| 	if len(data) == 0 { | ||||
| 		log.Entry().Error("Error reading audit file at " + filepath.Join(tmpFolder, "audit.fvdl") + ". This might be that the file is missing, corrupted, or too large. Aborting procedure.") | ||||
| 		err := errors.New("cannot read audit file") | ||||
| 		return sarif, err | ||||
| 		return sarif, sarifSimplified, err | ||||
| 	} | ||||
|  | ||||
| 	log.Entry().Debug("Calling Parse.") | ||||
| @@ -529,7 +530,7 @@ func ConvertFprToSarif(sys System, projectVersion *models.ProjectVersion, result | ||||
| } | ||||
|  | ||||
| // Parse parses the FPR file | ||||
| func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filterSet *models.FilterSet) (format.SARIF, error) { | ||||
| func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filterSet *models.FilterSet) (format.SARIF, format.SARIF, error) { | ||||
| 	//To read XML data, Unmarshal or Decode can be used, here we use Decode to work on the stream | ||||
| 	reader := bytes.NewReader(data) | ||||
| 	decoder := xml.NewDecoder(reader) | ||||
| @@ -539,7 +540,7 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 	var fvdl FVDL | ||||
| 	err := decoder.Decode(&fvdl) | ||||
| 	if err != nil { | ||||
| 		return format.SARIF{}, err | ||||
| 		return format.SARIF{}, format.SARIF{}, err | ||||
| 	} | ||||
|  | ||||
| 	//Create an object containing all audit data | ||||
| @@ -572,6 +573,12 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 	cweIdsForTaxonomies := make(map[string]string) //Defining this here and filling it in the course of the program helps filling the Taxonomies object easily. Map because easy to check for keys | ||||
| 	sarif.Runs = append(sarif.Runs, fortifyRun) | ||||
|  | ||||
| 	// Initialize the simplified version | ||||
| 	var sarifSimplified format.SARIF | ||||
| 	sarifSimplified.Schema = sarif.Schema | ||||
| 	sarifSimplified.Version = sarif.Version | ||||
| 	sarifSimplified.Runs = append(sarifSimplified.Runs, fortifyRun) | ||||
|  | ||||
| 	// Handle results/vulnerabilities | ||||
| 	log.Entry().Debug("[SARIF] Now handling results.") | ||||
| 	for i := 0; i < len(fvdl.Vulnerabilities.Vulnerability); i++ { | ||||
| @@ -655,9 +662,6 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 							tfloc.PhysicalLocation.ContextRegion = new(format.ContextRegion) | ||||
| 							tfloc.PhysicalLocation.ContextRegion.StartLine = fvdl.Snippets[j].StartLine | ||||
| 							tfloc.PhysicalLocation.ContextRegion.EndLine = fvdl.Snippets[j].EndLine | ||||
| 							snippetSarif := new(format.SnippetSarif) | ||||
| 							snippetSarif.Text = fvdl.Snippets[j].Text | ||||
| 							tfloc.PhysicalLocation.ContextRegion.Snippet = snippetSarif | ||||
| 							break | ||||
| 						} | ||||
| 					} | ||||
| @@ -670,33 +674,6 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 						if !(fvdl.Vulnerabilities.Vulnerability[i].AnalysisInfo.Trace[k].Primary.Entry[l].Node.Action.ActionData == "") { | ||||
| 							tfloc.Message = new(format.Message) | ||||
| 							tfloc.Message.Text = fvdl.Vulnerabilities.Vulnerability[i].AnalysisInfo.Trace[k].Primary.Entry[l].Node.Action.ActionData | ||||
|  | ||||
| 							// Handle snippet | ||||
| 							snippetTarget := handleSnippet(fvdl.Vulnerabilities.Vulnerability[i].AnalysisInfo.Trace[k].Primary.Entry[l].Node.Action.Type, fvdl.Vulnerabilities.Vulnerability[i].AnalysisInfo.Trace[k].Primary.Entry[l].Node.Action.ActionData) | ||||
|  | ||||
| 							if tfloc.PhysicalLocation.ContextRegion != nil && tfloc.PhysicalLocation.ContextRegion.Snippet != nil { | ||||
| 								physLocationSnippetLines := strings.Split(tfloc.PhysicalLocation.ContextRegion.Snippet.Text, "\n") | ||||
| 								snippetText := "" | ||||
| 								for j := 0; j < len(physLocationSnippetLines); j++ { | ||||
| 									if strings.Contains(physLocationSnippetLines[j], snippetTarget) { | ||||
| 										snippetText = physLocationSnippetLines[j] | ||||
| 										break | ||||
| 									} | ||||
| 								} | ||||
| 								snippetSarif := new(format.SnippetSarif) | ||||
| 								if snippetText != "" { | ||||
| 									snippetSarif.Text = snippetText | ||||
| 								} else { | ||||
| 									snippetSarif.Text = tfloc.PhysicalLocation.ContextRegion.Snippet.Text | ||||
| 								} | ||||
| 								tfloc.PhysicalLocation.Region.Snippet = snippetSarif | ||||
| 							} | ||||
| 						} else { | ||||
| 							if tfloc.PhysicalLocation.ContextRegion != nil && tfloc.PhysicalLocation.ContextRegion.Snippet != nil { | ||||
| 								snippetSarif := new(format.SnippetSarif) | ||||
| 								snippetSarif.Text = tfloc.PhysicalLocation.ContextRegion.Snippet.Text | ||||
| 								tfloc.PhysicalLocation.Region.Snippet = snippetSarif | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					location = *tfloc | ||||
| @@ -739,9 +716,6 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 									nintfloc.PhysicalLocation.ContextRegion = new(format.ContextRegion) | ||||
| 									nintfloc.PhysicalLocation.ContextRegion.StartLine = fvdl.Snippets[j].StartLine | ||||
| 									nintfloc.PhysicalLocation.ContextRegion.EndLine = fvdl.Snippets[j].EndLine | ||||
| 									snippetSarif := new(format.SnippetSarif) | ||||
| 									snippetSarif.Text = fvdl.Snippets[j].Text | ||||
| 									nintfloc.PhysicalLocation.ContextRegion.Snippet = snippetSarif | ||||
| 									break | ||||
| 								} | ||||
| 							} | ||||
| @@ -806,6 +780,15 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 		result.Properties = prop | ||||
|  | ||||
| 		sarif.Runs[0].Results = append(sarif.Runs[0].Results, result) | ||||
|  | ||||
| 		// Handle simplified version of a result | ||||
| 		resultSimplified := *new(format.Results) | ||||
| 		resultSimplified.RuleID = result.RuleID | ||||
| 		resultSimplified.Kind = result.Kind | ||||
| 		resultSimplified.Level = result.Level | ||||
| 		resultSimplified.Message = result.Message | ||||
| 		resultSimplified.Properties = result.Properties | ||||
| 		sarifSimplified.Runs[0].Results = append(sarifSimplified.Runs[0].Results, resultSimplified) | ||||
| 	} | ||||
|  | ||||
| 	//handle the tool object | ||||
| @@ -816,6 +799,10 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 	tool.Driver.Version = fvdl.EngineData.EngineVersion | ||||
| 	tool.Driver.InformationUri = "https://www.microfocus.com/documentation/fortify-static-code-analyzer-and-tools/2020/SCA_Guide_20.2.0.pdf" | ||||
|  | ||||
| 	//handle the simplified tool object | ||||
| 	toolSimplified := *new(format.Tool) | ||||
| 	toolSimplified.Driver = tool.Driver | ||||
|  | ||||
| 	//handles rules | ||||
| 	for i := 0; i < len(fvdl.EngineData.RuleInfo); i++ { //i iterates on rules | ||||
| 		sarifRule := *new(format.SarifRule) | ||||
| @@ -971,6 +958,15 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 				//Finalize: append the rule | ||||
| 				tool.Driver.Rules = append(tool.Driver.Rules, sarifRule) | ||||
|  | ||||
| 				// Handle simplified version of tool | ||||
| 				sarifRuleSimplified := *new(format.SarifRule) | ||||
| 				sarifRuleSimplified.ID = sarifRule.ID | ||||
| 				sarifRuleSimplified.GUID = sarifRule.GUID | ||||
| 				sarifRuleSimplified.Name = sarifRule.Name | ||||
| 				sarifRuleSimplified.DefaultConfiguration = sarifRule.DefaultConfiguration | ||||
| 				sarifRuleSimplified.Properties = sarifRule.Properties | ||||
| 				toolSimplified.Driver.Rules = append(toolSimplified.Driver.Rules, sarifRuleSimplified) | ||||
|  | ||||
| 				// A rule vuln has been found for this rule, no need to keep iterating | ||||
| 				break | ||||
| 			} | ||||
| @@ -994,6 +990,7 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
|  | ||||
| 	//Finalize: tool | ||||
| 	sarif.Runs[0].Tool = tool | ||||
| 	sarifSimplified.Runs[0].Tool = toolSimplified | ||||
|  | ||||
| 	//handle invocations object | ||||
| 	log.Entry().Debug("[SARIF] Now handling invocation.") | ||||
| @@ -1090,35 +1087,12 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 				loc.PhysicalLocation.ContextRegion = new(format.ContextRegion) | ||||
| 				loc.PhysicalLocation.ContextRegion.StartLine = fvdl.Snippets[j].StartLine | ||||
| 				loc.PhysicalLocation.ContextRegion.EndLine = fvdl.Snippets[j].EndLine | ||||
| 				snippetSarif := new(format.SnippetSarif) | ||||
| 				snippetSarif.Text = fvdl.Snippets[j].Text | ||||
| 				loc.PhysicalLocation.ContextRegion.Snippet = snippetSarif | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		loc.Message = new(format.Message) | ||||
| 		loc.Message.Text = fvdl.UnifiedNodePool.Node[i].Action.ActionData | ||||
|  | ||||
| 		// Handle snippet | ||||
| 		snippetTarget := handleSnippet(fvdl.UnifiedNodePool.Node[i].Action.Type, fvdl.UnifiedNodePool.Node[i].Action.ActionData) | ||||
|  | ||||
| 		if loc.PhysicalLocation.ContextRegion != nil && 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 | ||||
| 				} | ||||
| 			} | ||||
| 			snippetSarif := new(format.SnippetSarif) | ||||
| 			if snippetText != "" { | ||||
| 				snippetSarif.Text = snippetText | ||||
| 			} else { | ||||
| 				snippetSarif.Text = loc.PhysicalLocation.ContextRegion.Snippet.Text | ||||
| 			} | ||||
| 			loc.PhysicalLocation.Region.Snippet = snippetSarif | ||||
| 		} | ||||
| 		log.Entry().Debug("Compute eventual sub-nodes") | ||||
| 		threadFlowIndexMap[i+1] = computeLocationPath(fvdl, i+1) // Recursively traverse array | ||||
| 		locs := format.Locations{Location: loc} | ||||
| @@ -1180,7 +1154,7 @@ func Parse(sys System, projectVersion *models.ProjectVersion, data []byte, filte | ||||
| 	} | ||||
| 	sarif.Runs[0].Taxonomies = append(sarif.Runs[0].Taxonomies, taxonomy) | ||||
|  | ||||
| 	return sarif, nil | ||||
| 	return sarif, sarifSimplified, nil | ||||
| } | ||||
|  | ||||
| func integrateAuditData(ruleProp *format.SarifProperties, issueInstanceID string, sys System, projectVersion *models.ProjectVersion, auditData []*models.ProjectVersionIssue, filterSet *models.FilterSet, oneRequestPerIssue bool, maxretries int) error { | ||||
| @@ -1301,6 +1275,8 @@ func integrateAuditData(ruleProp *format.SarifProperties, issueInstanceID string | ||||
| } | ||||
|  | ||||
| // Factorizes some code used to obtain the relevant value for a snippet based on the type given by Fortify | ||||
| // Note: snippet text is no longer part of .sarif due to size issue. | ||||
| // This function however is helpful to explain how to get snippet out of FPR | ||||
| func handleSnippet(snippetType string, snippet string) string { | ||||
| 	snippetTarget := "" | ||||
| 	switch snippetType { | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| package fortify | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/format" | ||||
| 	"github.com/piper-validation/fortify-client-go/models" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestParse(t *testing.T) { | ||||
| @@ -361,30 +360,56 @@ If you are concerned about leaking system data via NFC on an Android device, you | ||||
|  | ||||
| 	t.Run("Valid config", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		sarif, err := Parse(sys, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		sarif, sarifSimplified, err := Parse(sys, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		assert.NoError(t, err, "error") | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].Locations), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].CodeFlows), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].RelatedLocations), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolState, "Exploitable") | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolAuditMessage, "Dummy comment.") | ||||
| 		assert.Equal(t, sarif.Runs[0].OriginalUriBaseIds, &format.OriginalUriBaseIds{SrcRoot: format.SrcRoot{Uri: "file:///C:/fortify-reference-pipeline/"}}) | ||||
|  | ||||
| 		//test simplified structure | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results), 2)                     // same results | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Tool.Driver.Rules), 1)           // same rules | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].Locations), 0)        // without location | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].CodeFlows), 0)        // without code flows | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].RelatedLocations), 0) // without related location | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolState, "Exploitable") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolAuditMessage, "Dummy comment.") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) // without OriginalUriBaseIds | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Missing data", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		_, err := Parse(sys, &projectVersion, []byte{}, filterSet) | ||||
| 		_, _, err := Parse(sys, &projectVersion, []byte{}, filterSet) | ||||
| 		assert.Error(t, err, "EOF") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("No system instance", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		sarif, err := Parse(nil, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		sarif, sarifSimplified, err := Parse(nil, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		assert.NoError(t, err, "error") | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].Locations), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].CodeFlows), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].RelatedLocations), 1) | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolState, "Unknown") | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolAuditMessage, "Cannot fetch audit state: no sys instance") | ||||
| 		assert.Equal(t, sarif.Runs[0].OriginalUriBaseIds, &format.OriginalUriBaseIds{SrcRoot: format.SrcRoot{Uri: "file:///C:/fortify-reference-pipeline/"}}) | ||||
|  | ||||
| 		//test simplified structure | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results), 2)                     // same results | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Tool.Driver.Rules), 1)           // same rules | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].Locations), 0)        // without location | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].CodeFlows), 0)        // without code flows | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].RelatedLocations), 0) // without related location | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolState, "Unknown") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolAuditMessage, "Cannot fetch audit state: no sys instance") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) // without OriginalUriBaseIds | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @@ -739,30 +764,55 @@ If you are concerned about leaking system data via NFC on an Android device, you | ||||
|  | ||||
| 	t.Run("Valid config", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		sarif, err := Parse(sys, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		sarif, sarifSimplified, err := Parse(sys, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		assert.NoError(t, err, "error") | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].Locations), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].CodeFlows), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].RelatedLocations), 1) | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolState, "Exploitable") | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolAuditMessage, "Dummy comment.") | ||||
| 		assert.Equal(t, sarif.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) | ||||
|  | ||||
| 		// test simplified sarif structure | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].Locations), 0) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].CodeFlows), 0) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].RelatedLocations), 0) | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolState, "Exploitable") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolAuditMessage, "Dummy comment.") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Missing data", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		_, err := Parse(sys, &projectVersion, []byte{}, filterSet) | ||||
| 		_, _, err := Parse(sys, &projectVersion, []byte{}, filterSet) | ||||
| 		assert.Error(t, err, "EOF") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("No system instance", func(t *testing.T) { | ||||
| 		projectVersion := models.ProjectVersion{ID: 11037} | ||||
| 		sarif, err := Parse(nil, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		sarif, sarifSimplified, err := Parse(nil, &projectVersion, []byte(testFvdl), filterSet) | ||||
| 		assert.NoError(t, err, "error") | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].Locations), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].CodeFlows), 1) | ||||
| 		assert.Equal(t, len(sarif.Runs[0].Results[0].RelatedLocations), 1) | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolState, "Unknown") | ||||
| 		assert.Equal(t, sarif.Runs[0].Results[0].Properties.ToolAuditMessage, "Cannot fetch audit state: no sys instance") | ||||
| 		assert.Equal(t, sarif.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) | ||||
|  | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results), 2) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Tool.Driver.Rules), 1) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].Locations), 0) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].CodeFlows), 0) | ||||
| 		assert.Equal(t, len(sarifSimplified.Runs[0].Results[0].RelatedLocations), 0) | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolState, "Unknown") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].Results[0].Properties.ToolAuditMessage, "Cannot fetch audit state: no sys instance") | ||||
| 		assert.Equal(t, sarifSimplified.Runs[0].OriginalUriBaseIds, (*format.OriginalUriBaseIds)(nil)) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package fortify | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"crypto/sha1" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| @@ -144,11 +145,11 @@ func WriteJSONReport(jsonReport FortifyReportData) ([]piperutils.Path, error) { | ||||
| 	return reportPaths, nil | ||||
| } | ||||
|  | ||||
| func WriteSarif(sarif format.SARIF) ([]piperutils.Path, error) { | ||||
| func WriteSarif(sarif format.SARIF, fileName string) ([]piperutils.Path, error) { | ||||
| 	utils := piperutils.Files{} | ||||
| 	reportPaths := []piperutils.Path{} | ||||
|  | ||||
| 	sarifReportPath := filepath.Join(ReportsDirectory, "result.sarif") | ||||
| 	sarifReportPath := filepath.Join(ReportsDirectory, fileName) | ||||
| 	// Ensure reporting directory exists | ||||
| 	if err := utils.MkdirAll(ReportsDirectory, 0777); err != nil { | ||||
| 		return reportPaths, errors.Wrapf(err, "failed to create report directory") | ||||
| @@ -177,6 +178,43 @@ func WriteSarif(sarif format.SARIF) ([]piperutils.Path, error) { | ||||
| 	return reportPaths, nil | ||||
| } | ||||
|  | ||||
| func WriteGzipSarif(sarif format.SARIF, fileName string) ([]piperutils.Path, error) { | ||||
| 	utils := piperutils.Files{} | ||||
| 	reportPaths := []piperutils.Path{} | ||||
|  | ||||
| 	sarifReportPath := filepath.Join(ReportsDirectory, fileName) | ||||
| 	// Ensure reporting directory exists | ||||
| 	if err := utils.MkdirAll(ReportsDirectory, 0777); err != nil { | ||||
| 		return reportPaths, errors.Wrapf(err, "failed to create report directory") | ||||
| 	} | ||||
|  | ||||
| 	// HTML characters will most likely be present: we need to use encode: create a buffer to hold JSON data | ||||
| 	// https://stackoverflow.com/questions/28595664/how-to-stop-json-marshal-from-escaping-and | ||||
| 	buffer := new(bytes.Buffer) | ||||
| 	// create JSON encoder for buffer | ||||
| 	bufEncoder := json.NewEncoder(buffer) | ||||
| 	// set options | ||||
| 	bufEncoder.SetEscapeHTML(false) | ||||
| 	bufEncoder.SetIndent("", "  ") | ||||
| 	//encode to buffer | ||||
| 	bufEncoder.Encode(sarif) | ||||
|  | ||||
| 	// Initialize gzip | ||||
| 	gzBuffer := &bytes.Buffer{} | ||||
| 	gzWriter := gzip.NewWriter(gzBuffer) | ||||
| 	gzWriter.Write([]byte(buffer.Bytes())) | ||||
| 	gzWriter.Close() | ||||
|  | ||||
| 	log.Entry().Info("Writing file to disk: ", sarifReportPath) | ||||
| 	if err := utils.FileWrite(sarifReportPath, gzBuffer.Bytes(), 0666); err != nil { | ||||
| 		log.SetErrorCategory(log.ErrorConfiguration) | ||||
| 		return reportPaths, errors.Wrapf(err, "failed to write Fortify SARIF gzip report") | ||||
| 	} | ||||
| 	reportPaths = append(reportPaths, piperutils.Path{Name: "Fortify SARIF gzip Report", Target: sarifReportPath}) | ||||
|  | ||||
| 	return reportPaths, nil | ||||
| } | ||||
|  | ||||
| func WriteCustomReports(scanReport reporting.ScanReport) ([]piperutils.Path, error) { | ||||
| 	utils := piperutils.Files{} | ||||
| 	reportPaths := []piperutils.Path{} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user