package reporting import ( "bytes" "fmt" "text/template" "time" "github.com/SAP/jenkins-library/pkg/orchestrator" "golang.org/x/text/cases" "golang.org/x/text/language" ) // VulnerabilityReport represents metadata for a report on a vulnerability type VulnerabilityReport struct { ProjectName string ProjectVersion string BlackDuckProjectLink string ArtifactID string Branch string CommitID string Description string DependencyType string Footer string Group string PackageURL string PipelineName string PipelineLink string PublishDate string Resolution string Score float64 Severity string Version string VulnerabilityLink string VulnerabilityName string Origin string } const vulnerabilityMdTemplate string = `# {{title .Severity }} ({{ .Score }}) Vulnerability {{ .VulnerabilityName }} - {{ .ArtifactID }} **Vulnerability link:** [{{ .VulnerabilityLink }}]({{ .VulnerabilityLink }}) {{if .Resolution -}} ## Fix **{{ .Resolution }}** {{- end}} ## Context {{if .PipelineLink -}} ### Pipeline Pipeline run: [{{ .PipelineName }}]({{ .PipelineLink }}) {{- end}} ### Detected in **Project Version:** [{{ .ProjectName }} {{ .ProjectVersion }}]({{ .BlackDuckProjectLink }}) {{if .Branch}}**Branch:** {{ .Branch }}{{- end}} {{if .CommitID}}**CommitId:** {{ .CommitID }}{{- end}} {{if .Group}}**Group:** {{ .Group }}{{- end}} {{if .PublishDate}}**Publishing date:** {{.PublishDate }}{{- end}} {{if .ArtifactID}}**ArtifactId:** {{ .ArtifactID }}{{- end}} {{if .Version}}**Version:** {{ .Version }}{{- end}} {{if .Origin}}**Origin:** {{ .Origin }}{{- end}} {{if .DependencyType}}**Dependency:** {{ .DependencyType }}{{- end}} {{if .PackageURL}}**Package URL:** {{ .PackageURL }}{{- end}} ## Description {{ .Description }} --- {{.Footer}} ` // ToMarkdown creates a vulnerability in markdown format which can be used in GitHub issues func (v *VulnerabilityReport) ToMarkdown() ([]byte, error) { funcMap := template.FuncMap{ "date": func(t time.Time) string { return t.Format("2006-01-02") }, "title": func(s string) string { caser := cases.Title(language.AmericanEnglish) return caser.String(s) }, } // only fill with orchestrator information if orchestrator can be identified properly if provider, err := orchestrator.NewOrchestratorSpecificConfigProvider(); err == nil { // only add information if not yet provided if len(v.CommitID) == 0 { v.CommitID = provider.GetCommit() } if len(v.PipelineLink) == 0 { v.PipelineLink = provider.GetJobURL() v.PipelineName = provider.GetJobName() } if len(v.Branch) == 0 { v.Branch = provider.GetBranch() } } md := []byte{} tmpl, err := template.New("report").Funcs(funcMap).Parse(vulnerabilityMdTemplate) if err != nil { return md, fmt.Errorf("failed to create markdown issue template: %w", err) } buf := new(bytes.Buffer) err = tmpl.Execute(buf, v) if err != nil { return md, fmt.Errorf("failed to execute markdown issue template: %w", err) } md = buf.Bytes() return md, nil }