From cccd7d185f6f4cdf42ead76a7654154db50881af Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Fri, 5 Jan 2024 17:05:04 +0800 Subject: [PATCH 01/18] feat: support sarif --- .github/README.md | 1 + README.md | 1 + cmd/detail/detail.go | 14 +++++++ cmd/format/sarif.go | 94 ++++++++++++++++++++++++++++++++++++++++++++ cmd/format/save.go | 2 + main.go | 2 +- 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 cmd/format/sarif.go diff --git a/.github/README.md b/.github/README.md index 1c144ed5..62fe2d8c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -131,6 +131,7 @@ Files supported by the `out` parameter are listed below: | | `html` | `.html` | `v1.0.6` and above | | | `sqlite` | `.sqlite` | `v1.0.13` and above| | | `csv` | `.csv` | `v1.0.13` and above| +| | `sarif`| `.sarif` | | | SBOM | `spdx` | `.spdx` `.spdx.json` `.spdx.xml` | `v1.0.8` and above | | | `cdx` | `.cdx.json` `.cdx.xml` | `v1.0.11`and above | | | `swid` | `.swid.json` `.swid.xml` | `v1.0.11`and above | diff --git a/README.md b/README.md index 905b680a..8056760c 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ | | `html` | `.html` | | | `sqlite` | `.sqlite` | | | `csv` | `.csv` | +| | `sarif` | `.sarif` | | SBOM清单 | `spdx` | `.spdx` `.spdx.json` `.spdx.xml` | | | `cdx` | `.cdx.json` `.cdx.xml` | | | `swid` | `.swid.json` `.swid.xml` | diff --git a/cmd/detail/detail.go b/cmd/detail/detail.go index 91045cd9..1a8e80e9 100644 --- a/cmd/detail/detail.go +++ b/cmd/detail/detail.go @@ -156,6 +156,20 @@ type Vuln struct { ExploitLevelId int `json:"exploit_level_id" gorm:"column:exploit_level_id"` } +func (v *Vuln) SecurityLevel() string { + switch v.SecurityLevelId { + case 1: + return "C" + case 2: + return "H" + case 3: + return "M" + case 4: + return "L" + } + return "U" +} + func vulnLanguageKey(language model.Language) []string { switch language { case model.Lan_Java: diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go new file mode 100644 index 00000000..53442de9 --- /dev/null +++ b/cmd/format/sarif.go @@ -0,0 +1,94 @@ +package format + +import ( + "encoding/json" + "fmt" + "io" + "path/filepath" + "strings" + + "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" +) + +type sarifReport struct { + Version string `json:"version"` + Schema string `json:"$schema"` + Runs []sarifRun `json:"runs"` +} + +type sarifRun struct { + Tool struct { + Driver struct { + Name string `json:"name"` + Version string `json:"version"` + InformationUri string `json:"informationUri"` + } `json:"driver"` + } `json:"tool"` + Results []sarifResult `json:"results"` +} + +type sarifResult struct { + RuleId string `json:"ruleId"` + Level string `json:"level"` + Message struct { + Text string `json:"text"` + } `json:"message"` + Locations []sarifLocation `json:"locations"` +} + +type sarifLocation struct { + PhysicalLocation struct { + ArtifactLocation struct { + Uri string `json:"uri"` + Index int `json:"index,omitempty"` + } `json:"artifactLocation"` + // Region struct { + // StartLine int `json:"startLine"` + // StartColumn int `json:"startColumn"` + // } `json:"region"` + } `json:"physicalLocation"` +} + +func Sarif(report Report, out string) { + + s := sarifReport{ + Version: "2.1.0", + Schema: "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json", + } + + run := sarifRun{} + run.Tool.Driver.Name = "opensca-cli" + run.Tool.Driver.Version = strings.TrimLeft(report.TaskInfo.ToolVersion, "vV") + run.Tool.Driver.InformationUri = "https://opensca.xmirror.cn" + + report.ForEach(func(n *detail.DepDetailGraph) bool { + for _, vuln := range n.Vulnerabilities { + if vuln.Id == "" { + continue + } + result := sarifResult{ + RuleId: "XM1001", + Level: "warning", + } + result.Message.Text = fmt.Sprintf("id:%s cve:%s cwe:%s level:%s\ndesc:%s\nsuggestion:%s", vuln.Id, vuln.Cve, vuln.Cwe, vuln.SecurityLevel(), vuln.Description, vuln.Suggestion) + for i, path := range n.Paths { + location := sarifLocation{} + if truncIndex := strings.Index(path, "["); truncIndex > 0 { + path = path[:truncIndex] + path = strings.TrimPrefix(path, filepath.Base(report.TaskInfo.AppName)) + path = strings.Trim(path, `\/`) + } + location.PhysicalLocation.ArtifactLocation.Uri = path + location.PhysicalLocation.ArtifactLocation.Index = i + result.Locations = append(result.Locations, location) + } + run.Results = append(run.Results, result) + } + return true + }) + + s.Runs = []sarifRun{run} + outWrite(out, func(w io.Writer) { + json.NewEncoder(w).Encode(s) + }) +} diff --git a/cmd/format/save.go b/cmd/format/save.go index 91546f70..41e90a73 100644 --- a/cmd/format/save.go +++ b/cmd/format/save.go @@ -71,6 +71,8 @@ func Save(report Report, output string) { Csv(report, out) case ".sqlite", ".db": Sqlite(report, out) + case ".sarif": + Sarif(report, out) default: Json(report, out) } diff --git a/main.go b/main.go index 3d7c58ac..ed860873 100644 --- a/main.go +++ b/main.go @@ -104,7 +104,7 @@ func args() { flag.BoolVar(&login, "login", false, "login to cloud server. example: -login") flag.StringVar(&cfgf, "config", "", "config path. example: -config config.json") flag.StringVar(&cfg.Path, "path", cfg.Path, "project path. example: -path project_path") - flag.StringVar(&cfg.Output, "out", cfg.Output, "report path, support html/json/xml/csv/sqlite/cdx/spdx/swid/dsdx. example: -out out.json,out.html") + flag.StringVar(&cfg.Output, "out", cfg.Output, "report path, support html/json/xml/csv/sarif/sqlite/cdx/spdx/swid/dsdx. example: -out out.json,out.html") flag.StringVar(&cfg.LogFile, "log", cfg.LogFile, "-log ./my_opensca_log.txt") flag.StringVar(&cfg.Origin.Token, "token", "", "web token, example: -token xxxx") flag.StringVar(&proj, "proj", proj, "saas project id, example: -proj xxxx") From 15550c9113a17c2bcc675ebc0e6ce6dddcfc754f Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:17:25 +0800 Subject: [PATCH 02/18] update: vulnerability level mapping --- cmd/detail/detail.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/detail/detail.go b/cmd/detail/detail.go index 1a8e80e9..ef9a0485 100644 --- a/cmd/detail/detail.go +++ b/cmd/detail/detail.go @@ -159,15 +159,15 @@ type Vuln struct { func (v *Vuln) SecurityLevel() string { switch v.SecurityLevelId { case 1: - return "C" + return "Critical" case 2: - return "H" + return "High" case 3: - return "M" + return "Medium" case 4: - return "L" + return "Low" } - return "U" + return "Unknown" } func vulnLanguageKey(language model.Language) []string { From 67cdb33a78225602ed21d5afdd6b47b788967db0 Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:10:48 +0800 Subject: [PATCH 03/18] update: format sarif report add sarifRule struct and related functions --- cmd/format/sarif.go | 97 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 53442de9..3c834be0 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -3,6 +3,7 @@ package format import ( "encoding/json" "fmt" + "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" "io" "path/filepath" "strings" @@ -19,14 +20,39 @@ type sarifReport struct { type sarifRun struct { Tool struct { Driver struct { - Name string `json:"name"` - Version string `json:"version"` - InformationUri string `json:"informationUri"` + Name string `json:"name"` + Version string `json:"version"` + InformationUri string `json:"informationUri"` + Rules []sarifRule `json:"rules"` } `json:"driver"` } `json:"tool"` Results []sarifResult `json:"results"` } +type sarifRule struct { + Id string `json:"id"` + ShortDescription sarifRuleShortDescription `json:"shortDescription"` + Help sarifRuleHelp `json:"help"` + Properties sarifRuleProperties `json:"properties"` +} + +type sarifRuleShortDescription struct { + Text string `json:"text"` +} + +type sarifRuleFullDescription struct { + Text string `json:"text"` +} + +type sarifRuleHelp struct { + Text string `json:"text"` + Markdown string `json:"markdown"` +} + +type sarifRuleProperties struct { + Tags []string `json:"tags"` +} + type sarifResult struct { RuleId string `json:"ruleId"` Level string `json:"level"` @@ -63,6 +89,20 @@ func Sarif(report Report, out string) { report.ForEach(func(n *detail.DepDetailGraph) bool { for _, vuln := range n.Vulnerabilities { + run.Tool.Driver.Rules = append(run.Tool.Driver.Rules, sarifRule{ + Id: vuln.Id, + ShortDescription: sarifRuleShortDescription{ + Text: fmt.Sprintf("[%s] 组件 %s 中存在 %s", vuln.SecurityLevel(), n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name), + }, + Help: sarifRuleHelp{ + Markdown: formatDesc(vuln), + }, + Properties: sarifRuleProperties{ + Tags: formatTags(n), + }, + }, + ) + if vuln.Id == "" { continue } @@ -70,7 +110,7 @@ func Sarif(report Report, out string) { RuleId: "XM1001", Level: "warning", } - result.Message.Text = fmt.Sprintf("id:%s cve:%s cwe:%s level:%s\ndesc:%s\nsuggestion:%s", vuln.Id, vuln.Cve, vuln.Cwe, vuln.SecurityLevel(), vuln.Description, vuln.Suggestion) + result.Message.Text = fmt.Sprintf("引入的组件 %s 中存在 %s", n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name) for i, path := range n.Paths { location := sarifLocation{} if truncIndex := strings.Index(path, "["); truncIndex > 0 { @@ -89,6 +129,53 @@ func Sarif(report Report, out string) { s.Runs = []sarifRun{run} outWrite(out, func(w io.Writer) { - json.NewEncoder(w).Encode(s) + err := json.NewEncoder(w).Encode(s) + if err != nil { + logs.Error(err) + } }) } + +func formatDesc(v *detail.Vuln) string { + text := fmt.Sprintf("| id | %s |", v.Id) + text = fmt.Sprintf("%s\n| --- | --- |", text) + if v.Cve != "" { + text = fmt.Sprintf("%s\n| cve | %s |", text, v.Cve) + } + if v.Cnnvd != "" { + text = fmt.Sprintf("%s\n| cnnvd | %s |", text, v.Cnnvd) + } + if v.Cnvd != "" { + text = fmt.Sprintf("%s\n| cnvd | %s |", text, v.Cnvd) + } + if v.Cwe != "" { + text = fmt.Sprintf("%s\n| cwe | %s |", text, v.Cwe) + } + text = fmt.Sprintf("%s\n| level | %s |", text, v.SecurityLevel()) + text = fmt.Sprintf("%s\n| desc | %s |", text, v.Description) + text = fmt.Sprintf("%s\n| suggestion | %s |", text, v.Suggestion) + + return text +} + +func formatTags(dg *detail.DepDetailGraph) []string { + tags := []string{} + exists := make(map[string]bool) + tags = append(tags, "security") + tags = append(tags, "Use-Vulnerable-and-Outdated-Components") + if dg.Dep.Language != "" { + tags = append(tags, dg.Dep.Language) + exists[dg.Dep.Language] = true + } + for _, v := range dg.Vulnerabilities { + if v.Cve != "" && !exists[v.Cve] { + tags = append(tags, v.Cve) + exists[v.Cve] = true + } + if v.AttackType != "" && !exists[v.AttackType] { + tags = append(tags, v.AttackType) + exists[v.AttackType] = true + } + } + return tags +} From f18e34d69c1497d0dc286b6d1953e6b29e4cbb26 Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:42:06 +0800 Subject: [PATCH 04/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 3c834be0..f3ce37a0 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -32,6 +32,7 @@ type sarifRun struct { type sarifRule struct { Id string `json:"id"` ShortDescription sarifRuleShortDescription `json:"shortDescription"` + FullDescription sarifRuleFullDescription `json:"fullDescription"` Help sarifRuleHelp `json:"help"` Properties sarifRuleProperties `json:"properties"` } From 0425a8e910dbcf0cbf91d30b4893d5d0f6e4d7ee Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:43:00 +0800 Subject: [PATCH 05/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index f3ce37a0..b464347a 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -95,6 +95,9 @@ func Sarif(report Report, out string) { ShortDescription: sarifRuleShortDescription{ Text: fmt.Sprintf("[%s] 组件 %s 中存在 %s", vuln.SecurityLevel(), n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name), }, + FullDescription: sarifRuleFullDescription{ + Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), + }, Help: sarifRuleHelp{ Markdown: formatDesc(vuln), }, From 2a739fb86712df6f9aad7de061ae739a2af1d407 Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:45:55 +0800 Subject: [PATCH 06/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index b464347a..92f6d997 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -98,6 +98,9 @@ func Sarif(report Report, out string) { FullDescription: sarifRuleFullDescription{ Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), }, + FullDescription: sarifRuleFullDescription{ + Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), + }, Help: sarifRuleHelp{ Markdown: formatDesc(vuln), }, From 20ba486384374b041785e93ff6f354ab9b580606 Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:46:02 +0800 Subject: [PATCH 07/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 92f6d997..cf3f10e3 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -33,6 +33,7 @@ type sarifRule struct { Id string `json:"id"` ShortDescription sarifRuleShortDescription `json:"shortDescription"` FullDescription sarifRuleFullDescription `json:"fullDescription"` + FullDescription sarifRuleFullDescription `json:"fullDescription"` Help sarifRuleHelp `json:"help"` Properties sarifRuleProperties `json:"properties"` } From bb4d8dfcd6419cf40e188410a255ac942e930d0c Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:53:03 +0800 Subject: [PATCH 08/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index cf3f10e3..8c03ce2f 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -99,9 +99,6 @@ func Sarif(report Report, out string) { FullDescription: sarifRuleFullDescription{ Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), }, - FullDescription: sarifRuleFullDescription{ - Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), - }, Help: sarifRuleHelp{ Markdown: formatDesc(vuln), }, From 5ca90a79f7017115d4c0a6d6ae79f3a0504b687f Mon Sep 17 00:00:00 2001 From: CyberChen <53457978+SuperChen-CC@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:53:10 +0800 Subject: [PATCH 09/18] Update cmd/format/sarif.go --- cmd/format/sarif.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 8c03ce2f..b464347a 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -33,7 +33,6 @@ type sarifRule struct { Id string `json:"id"` ShortDescription sarifRuleShortDescription `json:"shortDescription"` FullDescription sarifRuleFullDescription `json:"fullDescription"` - FullDescription sarifRuleFullDescription `json:"fullDescription"` Help sarifRuleHelp `json:"help"` Properties sarifRuleProperties `json:"properties"` } From 2dc7c984440890995d57722c9fd7afbc1aea4669 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:00:09 +0800 Subject: [PATCH 10/18] feat: set vulnId to ruleId --- cmd/format/sarif.go | 73 +++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index b464347a..fc5341f7 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -3,11 +3,11 @@ package format import ( "encoding/json" "fmt" - "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" "io" - "path/filepath" "strings" + "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" + "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" ) @@ -31,6 +31,7 @@ type sarifRun struct { type sarifRule struct { Id string `json:"id"` + Name string `json:"name"` ShortDescription sarifRuleShortDescription `json:"shortDescription"` FullDescription sarifRuleFullDescription `json:"fullDescription"` Help sarifRuleHelp `json:"help"` @@ -88,49 +89,47 @@ func Sarif(report Report, out string) { run.Tool.Driver.Version = strings.TrimLeft(report.TaskInfo.ToolVersion, "vV") run.Tool.Driver.InformationUri = "https://opensca.xmirror.cn" + vulnInfos := map[string]*detail.VulnInfo{} + report.ForEach(func(n *detail.DepDetailGraph) bool { for _, vuln := range n.Vulnerabilities { - run.Tool.Driver.Rules = append(run.Tool.Driver.Rules, sarifRule{ - Id: vuln.Id, - ShortDescription: sarifRuleShortDescription{ - Text: fmt.Sprintf("[%s] 组件 %s 中存在 %s", vuln.SecurityLevel(), n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name), - }, - FullDescription: sarifRuleFullDescription{ - Text: fmt.Sprintf("%s - %s", vuln.Id, n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")]), - }, - Help: sarifRuleHelp{ - Markdown: formatDesc(vuln), - }, - Properties: sarifRuleProperties{ - Tags: formatTags(n), - }, - }, - ) if vuln.Id == "" { continue } + + vulnInfos[vuln.Id] = &detail.VulnInfo{Vuln: vuln, Language: n.Language} + result := sarifResult{ - RuleId: "XM1001", + RuleId: vuln.Id, Level: "warning", } result.Message.Text = fmt.Sprintf("引入的组件 %s 中存在 %s", n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name) for i, path := range n.Paths { location := sarifLocation{} - if truncIndex := strings.Index(path, "["); truncIndex > 0 { - path = path[:truncIndex] - path = strings.TrimPrefix(path, filepath.Base(report.TaskInfo.AppName)) - path = strings.Trim(path, `\/`) - } location.PhysicalLocation.ArtifactLocation.Uri = path location.PhysicalLocation.ArtifactLocation.Index = i + // location.PhysicalLocation.Region.StartColumn = 1 + // location.PhysicalLocation.Region.StartLine = 1 result.Locations = append(result.Locations, location) } + run.Results = append(run.Results, result) } return true }) + for _, vuln := range vulnInfos { + run.Tool.Driver.Rules = append(run.Tool.Driver.Rules, sarifRule{ + Id: vuln.Id, + Name: vuln.Name, + ShortDescription: sarifRuleShortDescription{Text: vuln.Name}, + FullDescription: sarifRuleFullDescription{Text: vuln.Description}, + Help: sarifRuleHelp{Markdown: formatDesc(vuln)}, + Properties: sarifRuleProperties{Tags: formatTags(vuln)}, + }) + } + s.Runs = []sarifRun{run} outWrite(out, func(w io.Writer) { err := json.NewEncoder(w).Encode(s) @@ -140,7 +139,7 @@ func Sarif(report Report, out string) { }) } -func formatDesc(v *detail.Vuln) string { +func formatDesc(v *detail.VulnInfo) string { text := fmt.Sprintf("| id | %s |", v.Id) text = fmt.Sprintf("%s\n| --- | --- |", text) if v.Cve != "" { @@ -162,23 +161,13 @@ func formatDesc(v *detail.Vuln) string { return text } -func formatTags(dg *detail.DepDetailGraph) []string { - tags := []string{} - exists := make(map[string]bool) - tags = append(tags, "security") - tags = append(tags, "Use-Vulnerable-and-Outdated-Components") - if dg.Dep.Language != "" { - tags = append(tags, dg.Dep.Language) - exists[dg.Dep.Language] = true - } - for _, v := range dg.Vulnerabilities { - if v.Cve != "" && !exists[v.Cve] { - tags = append(tags, v.Cve) - exists[v.Cve] = true - } - if v.AttackType != "" && !exists[v.AttackType] { - tags = append(tags, v.AttackType) - exists[v.AttackType] = true +func formatTags(v *detail.VulnInfo) []string { + tags := []string{"security", "Use-Vulnerable-and-Outdated-Components", v.Cve, v.Cwe, v.AttackType, v.Language} + for i := 0; i < len(tags); { + if tags[i] == "" { + tags = append(tags[:i], tags[i+1:]...) + } else { + i++ } } return tags From 7f89c20243f9c6eeeca56034b887de89a9321fa3 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:16:08 +0800 Subject: [PATCH 11/18] feat: markdown table format --- cmd/format/sarif.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index fc5341f7..005d9537 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -140,25 +140,28 @@ func Sarif(report Report, out string) { } func formatDesc(v *detail.VulnInfo) string { - text := fmt.Sprintf("| id | %s |", v.Id) - text = fmt.Sprintf("%s\n| --- | --- |", text) - if v.Cve != "" { - text = fmt.Sprintf("%s\n| cve | %s |", text, v.Cve) + table := []struct { + fmt string + val string + }{ + {"| id | %s |", v.Id}, + {"| --- | --- |", ""}, + {"| cve | %s |", v.Cve}, + {"| cnnvd | %s |", v.Cnnvd}, + {"| cnvd | %s |", v.Cnvd}, + {"| cwe | %s |", v.Cwe}, + {"| level | %s |", v.SecurityLevel()}, + {"| desc | %s |", v.Description}, + {"| suggestion | %s |", v.Suggestion}, } - if v.Cnnvd != "" { - text = fmt.Sprintf("%s\n| cnnvd | %s |", text, v.Cnnvd) - } - if v.Cnvd != "" { - text = fmt.Sprintf("%s\n| cnvd | %s |", text, v.Cnvd) - } - if v.Cwe != "" { - text = fmt.Sprintf("%s\n| cwe | %s |", text, v.Cwe) + var lines []string + for _, line := range table { + if strings.Contains(line.fmt, "%s") && line.val == "" { + continue + } + lines = append(lines, fmt.Sprintf(line.fmt, line.val)) } - text = fmt.Sprintf("%s\n| level | %s |", text, v.SecurityLevel()) - text = fmt.Sprintf("%s\n| desc | %s |", text, v.Description) - text = fmt.Sprintf("%s\n| suggestion | %s |", text, v.Suggestion) - - return text + return strings.Join(lines, "\n") } func formatTags(v *detail.VulnInfo) []string { From a2775c25dc08f506bbc5361faa962d7493db177c Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:48:44 +0800 Subject: [PATCH 12/18] feat: save write error --- cmd/format/csv.go | 5 +++-- cmd/format/cyclonedx.go | 8 ++++---- cmd/format/dsdx.go | 16 ++++++---------- cmd/format/html.go | 5 +++-- cmd/format/json.go | 4 ++-- cmd/format/sarif.go | 9 ++------- cmd/format/save.go | 6 ++++-- cmd/format/spdx.go | 16 ++++++---------- cmd/format/swid.go | 19 ++++++++++++------- cmd/format/xml.go | 4 ++-- 10 files changed, 44 insertions(+), 48 deletions(-) diff --git a/cmd/format/csv.go b/cmd/format/csv.go index 43e8f74e..840fb4d0 100644 --- a/cmd/format/csv.go +++ b/cmd/format/csv.go @@ -25,8 +25,9 @@ func Csv(report Report, out string) { return true }) - outWrite(out, func(w io.Writer) { - w.Write([]byte(table)) + outWrite(out, func(w io.Writer) error { + _, err := w.Write([]byte(table)) + return err }) } diff --git a/cmd/format/cyclonedx.go b/cmd/format/cyclonedx.go index b783ef98..6c5dc779 100644 --- a/cmd/format/cyclonedx.go +++ b/cmd/format/cyclonedx.go @@ -58,14 +58,14 @@ func cyclonedxbom(dep *detail.DepDetailGraph) *cyclonedx.BOM { func CycloneDXJson(report Report, out string) { bom := cyclonedxbom(report.DepDetailGraph) - outWrite(out, func(w io.Writer) { - cyclonedx.NewBOMEncoder(w, cyclonedx.BOMFileFormatJSON).SetPretty(true).Encode(bom) + outWrite(out, func(w io.Writer) error { + return cyclonedx.NewBOMEncoder(w, cyclonedx.BOMFileFormatJSON).SetPretty(true).Encode(bom) }) } func CycloneDXXml(report Report, out string) { bom := cyclonedxbom(report.DepDetailGraph) - outWrite(out, func(w io.Writer) { - cyclonedx.NewBOMEncoder(w, cyclonedx.BOMFileFormatXML).SetPretty(true).Encode(bom) + outWrite(out, func(w io.Writer) error { + return cyclonedx.NewBOMEncoder(w, cyclonedx.BOMFileFormatXML).SetPretty(true).Encode(bom) }) } diff --git a/cmd/format/dsdx.go b/cmd/format/dsdx.go index 700334bc..63bae61f 100644 --- a/cmd/format/dsdx.go +++ b/cmd/format/dsdx.go @@ -6,28 +6,24 @@ import ( "io" "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" - "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" "github.com/xmirrorsecurity/opensca-cli/v3/opensca/model" ) func Dsdx(report Report, out string) { - outWrite(out, func(w io.Writer) { - err := dsdxDoc(report).WriteDsdx(w) - if err != nil { - logs.Warn(err) - } + outWrite(out, func(w io.Writer) error { + return dsdxDoc(report).WriteDsdx(w) }) } func DsdxJson(report Report, out string) { - outWrite(out, func(w io.Writer) { - json.NewEncoder(w).Encode(dsdxDoc(report)) + outWrite(out, func(w io.Writer) error { + return json.NewEncoder(w).Encode(dsdxDoc(report)) }) } func DsdxXml(report Report, out string) { - outWrite(out, func(w io.Writer) { - xml.NewEncoder(w).Encode(dsdxDoc(report)) + outWrite(out, func(w io.Writer) error { + return xml.NewEncoder(w).Encode(dsdxDoc(report)) }) } diff --git a/cmd/format/html.go b/cmd/format/html.go index 99bbad36..18844d4b 100644 --- a/cmd/format/html.go +++ b/cmd/format/html.go @@ -86,8 +86,9 @@ func Html(report Report, out string) { }); err != nil { logs.Warn(err) } else { - outWrite(out, func(w io.Writer) { - w.Write(bytes.Replace(index, []byte(`"此处填充json数据"`), data, 1)) + outWrite(out, func(w io.Writer) error { + _, err := w.Write(bytes.Replace(index, []byte(`"此处填充json数据"`), data, 1)) + return err }) return } diff --git a/cmd/format/json.go b/cmd/format/json.go index 7144b34d..7965319b 100644 --- a/cmd/format/json.go +++ b/cmd/format/json.go @@ -6,9 +6,9 @@ import ( ) func Json(report Report, out string) { - outWrite(out, func(w io.Writer) { + outWrite(out, func(w io.Writer) error { encoder := json.NewEncoder(w) encoder.SetIndent("", " ") - encoder.Encode(report) + return encoder.Encode(report) }) } diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 005d9537..5253a089 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -6,8 +6,6 @@ import ( "io" "strings" - "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" - "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" ) @@ -131,11 +129,8 @@ func Sarif(report Report, out string) { } s.Runs = []sarifRun{run} - outWrite(out, func(w io.Writer) { - err := json.NewEncoder(w).Encode(s) - if err != nil { - logs.Error(err) - } + outWrite(out, func(w io.Writer) error { + return json.NewEncoder(w).Encode(s) }) } diff --git a/cmd/format/save.go b/cmd/format/save.go index 41e90a73..e9b8361f 100644 --- a/cmd/format/save.go +++ b/cmd/format/save.go @@ -79,7 +79,7 @@ func Save(report Report, output string) { } } -func outWrite(out string, do func(io.Writer)) { +func outWrite(out string, do func(io.Writer) error) { if out == "" { do(os.Stdout) @@ -97,7 +97,9 @@ func outWrite(out string, do func(io.Writer)) { logs.Warn(err) } else { defer w.Close() - do(w) + if err = do(w); err != nil { + logs.Warn(err) + } } } diff --git a/cmd/format/spdx.go b/cmd/format/spdx.go index cd8a457f..313983f7 100644 --- a/cmd/format/spdx.go +++ b/cmd/format/spdx.go @@ -6,28 +6,24 @@ import ( "io" "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" - "github.com/xmirrorsecurity/opensca-cli/v3/opensca/logs" "github.com/xmirrorsecurity/opensca-cli/v3/opensca/model" ) func Spdx(report Report, out string) { - outWrite(out, func(w io.Writer) { - err := spdxDoc(report).WriteSpdx(w) - if err != nil { - logs.Warn(err) - } + outWrite(out, func(w io.Writer) error { + return spdxDoc(report).WriteSpdx(w) }) } func SpdxJson(report Report, out string) { - outWrite(out, func(w io.Writer) { - json.NewEncoder(w).Encode(spdxDoc(report)) + outWrite(out, func(w io.Writer) error { + return json.NewEncoder(w).Encode(spdxDoc(report)) }) } func SpdxXml(report Report, out string) { - outWrite(out, func(w io.Writer) { - xml.NewEncoder(w).Encode(spdxDoc(report)) + outWrite(out, func(w io.Writer) error { + return xml.NewEncoder(w).Encode(spdxDoc(report)) }) } diff --git a/cmd/format/swid.go b/cmd/format/swid.go index 78c61df8..fb954931 100644 --- a/cmd/format/swid.go +++ b/cmd/format/swid.go @@ -4,6 +4,7 @@ import ( "archive/zip" "encoding/json" "encoding/xml" + "errors" "io" "path/filepath" "strings" @@ -14,12 +15,14 @@ import ( "github.com/veraison/swid" ) -func swidZip(out string, report Report, writeFunc func(tag *swid.SoftwareIdentity, w io.Writer)) { - outWrite(out+".zip", func(writer io.Writer) { +func swidZip(out string, report Report, writeFunc func(tag *swid.SoftwareIdentity, w io.Writer) error) { + outWrite(out+".zip", func(writer io.Writer) error { zf := zip.NewWriter(writer) defer zf.Close() + var werr error + report.DepDetailGraph.ForEach(func(n *detail.DepDetailGraph) bool { if n.Name == "" { @@ -63,21 +66,23 @@ func swidZip(out string, report Report, writeFunc func(tag *swid.SoftwareIdentit return true } - writeFunc(tag, w) + werr = errors.Join(werr, writeFunc(tag, w)) + return true }) + return werr }) } func SwidJson(report Report, out string) { - swidZip(out, report, func(tag *swid.SoftwareIdentity, w io.Writer) { - json.NewEncoder(w).Encode(tag) + swidZip(out, report, func(tag *swid.SoftwareIdentity, w io.Writer) error { + return json.NewEncoder(w).Encode(tag) }) } func SwidXml(report Report, out string) { - swidZip(out, report, func(tag *swid.SoftwareIdentity, w io.Writer) { - xml.NewEncoder(w).Encode(tag) + swidZip(out, report, func(tag *swid.SoftwareIdentity, w io.Writer) error { + return xml.NewEncoder(w).Encode(tag) }) } diff --git a/cmd/format/xml.go b/cmd/format/xml.go index c56473e5..5b7e4d00 100644 --- a/cmd/format/xml.go +++ b/cmd/format/xml.go @@ -6,7 +6,7 @@ import ( ) func Xml(report Report, out string) { - outWrite(out, func(w io.Writer) { - xml.NewEncoder(w).Encode(report) + outWrite(out, func(w io.Writer) error { + return xml.NewEncoder(w).Encode(report) }) } From f6519d692485c15ea701ef70f7db7e5de01fe1e8 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:51:38 +0800 Subject: [PATCH 13/18] feat: location.region --- cmd/format/sarif.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 5253a089..7a15d8f0 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -68,10 +68,12 @@ type sarifLocation struct { Uri string `json:"uri"` Index int `json:"index,omitempty"` } `json:"artifactLocation"` - // Region struct { - // StartLine int `json:"startLine"` - // StartColumn int `json:"startColumn"` - // } `json:"region"` + Region struct { + StartColumn int `json:"startColumn"` + EndColumn int `json:"endColumn"` + StartLine int `json:"startLine"` + EndLine int `json:"endLine"` + } `json:"region"` } `json:"physicalLocation"` } @@ -107,8 +109,10 @@ func Sarif(report Report, out string) { location := sarifLocation{} location.PhysicalLocation.ArtifactLocation.Uri = path location.PhysicalLocation.ArtifactLocation.Index = i - // location.PhysicalLocation.Region.StartColumn = 1 - // location.PhysicalLocation.Region.StartLine = 1 + location.PhysicalLocation.Region.StartColumn = 1 + location.PhysicalLocation.Region.EndColumn = 1 + location.PhysicalLocation.Region.StartLine = 1 + location.PhysicalLocation.Region.EndLine = 1 result.Locations = append(result.Locations, location) } From da97dbaaaac90a0d32bb21c6080135bdff361c04 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:53:19 +0800 Subject: [PATCH 14/18] feat: trune path gav --- cmd/format/sarif.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 7a15d8f0..8b350b24 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -106,6 +106,9 @@ func Sarif(report Report, out string) { } result.Message.Text = fmt.Sprintf("引入的组件 %s 中存在 %s", n.Dep.Key()[:strings.LastIndex(n.Dep.Key(), ":")], vuln.Name) for i, path := range n.Paths { + if truncIndex := strings.Index(path, "["); truncIndex > 0 { + path = strings.Trim(path[:truncIndex], `\/`) + } location := sarifLocation{} location.PhysicalLocation.ArtifactLocation.Uri = path location.PhysicalLocation.ArtifactLocation.Index = i From 93e02cd3959c94223964516f0ded6d594222dd58 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 15:57:57 +0800 Subject: [PATCH 15/18] feat: markdown escape html --- cmd/format/sarif.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 8b350b24..5f90c46b 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -3,6 +3,7 @@ package format import ( "encoding/json" "fmt" + "html" "io" "strings" @@ -163,7 +164,7 @@ func formatDesc(v *detail.VulnInfo) string { } lines = append(lines, fmt.Sprintf(line.fmt, line.val)) } - return strings.Join(lines, "\n") + return html.EscapeString(strings.Join(lines, "\n")) } func formatTags(v *detail.VulnInfo) []string { From 5dc00dd0354128972d158eb65f4587210b4bbd8c Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 16:00:03 +0800 Subject: [PATCH 16/18] feat: table format --- cmd/format/sarif.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 5f90c46b..88de5a36 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -162,7 +162,11 @@ func formatDesc(v *detail.VulnInfo) string { if strings.Contains(line.fmt, "%s") && line.val == "" { continue } - lines = append(lines, fmt.Sprintf(line.fmt, line.val)) + if line.val == "" { + lines = append(lines, line.fmt) + } else { + lines = append(lines, fmt.Sprintf(line.fmt, line.val)) + } } return html.EscapeString(strings.Join(lines, "\n")) } From ce52b082a42c5247181151dd6c835ea4b595f0b6 Mon Sep 17 00:00:00 2001 From: CyberChen Date: Tue, 9 Jan 2024 17:00:27 +0800 Subject: [PATCH 17/18] update: sanitize HTML tag and new line breaks in desc --- cmd/format/sarif.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index 88de5a36..faa0f117 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -5,6 +5,7 @@ import ( "fmt" "html" "io" + "regexp" "strings" "github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" @@ -154,8 +155,8 @@ func formatDesc(v *detail.VulnInfo) string { {"| cnvd | %s |", v.Cnvd}, {"| cwe | %s |", v.Cwe}, {"| level | %s |", v.SecurityLevel()}, - {"| desc | %s |", v.Description}, - {"| suggestion | %s |", v.Suggestion}, + {"| desc | %s |", sanitizeString(v.Description)}, + {"| suggestion | %s |", sanitizeString(v.Suggestion)}, } var lines []string for _, line := range table { @@ -168,9 +169,20 @@ func formatDesc(v *detail.VulnInfo) string { lines = append(lines, fmt.Sprintf(line.fmt, line.val)) } } + return html.EscapeString(strings.Join(lines, "\n")) } +func sanitizeString(s string) string { + re := regexp.MustCompile("<[^>]*>") + s = re.ReplaceAllString(s, "") + + s = strings.ReplaceAll(s, "\r", "") + s = strings.ReplaceAll(s, "\n", "") + + return s +} + func formatTags(v *detail.VulnInfo) []string { tags := []string{"security", "Use-Vulnerable-and-Outdated-Components", v.Cve, v.Cwe, v.AttackType, v.Language} for i := 0; i < len(tags); { From eeb2dabfa9975db821353ad937f79cb5ea33a8b9 Mon Sep 17 00:00:00 2001 From: luotianqi777 Date: Tue, 9 Jan 2024 17:36:28 +0800 Subject: [PATCH 18/18] feat: remove escape html --- cmd/format/sarif.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/format/sarif.go b/cmd/format/sarif.go index faa0f117..b0f6c31d 100644 --- a/cmd/format/sarif.go +++ b/cmd/format/sarif.go @@ -3,7 +3,6 @@ package format import ( "encoding/json" "fmt" - "html" "io" "regexp" "strings" @@ -170,7 +169,7 @@ func formatDesc(v *detail.VulnInfo) string { } } - return html.EscapeString(strings.Join(lines, "\n")) + return strings.Join(lines, "\n") } func sanitizeString(s string) string {