diff --git a/cve-vulner-manager/common/analysis.go b/cve-vulner-manager/common/analysis.go new file mode 100644 index 0000000000000000000000000000000000000000..5d360952f8eaeeaf8a3edeeb8cb35fd864b32729 --- /dev/null +++ b/cve-vulner-manager/common/analysis.go @@ -0,0 +1,58 @@ +package common + +import "k8s.io/apimachinery/pkg/util/sets" + +const ( + AnalysisWillFix = "正常修复" + AnalysisStill = "暂不修复-漏洞仍在分析中" + AnalysisNoPatch = "暂不修复-暂无解决方案或补丁" + AnalysisUpgrade = "暂不修复-待升级版本修复" + AnalysisOutScope = "不修复-超出修复范围" + AnalysisNotFix = "不修复-特殊原因导致不再修复" + AnalysisComponentNotPresent = "不受影响-组件不存在" + AnalysisMitigationsExist = "不受影响-已有内置的内联控制或缓解措施" + AnalysisCannotControlled = "不受影响-漏洞代码不能被攻击者触发" + AnalysisNotExecute = "不受影响-漏洞代码不在执行路径" + AnalysisCodeNotPresent = "不受影响-漏洞代码不存在" + + TypeAffected = "Affected" + TypeUnaffected = "Unaffected" +) + +var AnalysisUnaffected = map[string]struct{}{ + AnalysisComponentNotPresent: {}, + AnalysisMitigationsExist: {}, + AnalysisCannotControlled: {}, + AnalysisNotExecute: {}, + AnalysisCodeNotPresent: {}, +} + +var AnalysisEnglishMap = map[string]string{ + AnalysisWillFix: "will fix", + AnalysisStill: "none_available-Vulnerabilities are still analyzing", + AnalysisNoPatch: "none_available-No solution or patch", + AnalysisUpgrade: "none_available-To be fixed through an upgraded version", + AnalysisOutScope: "no_fix_planned-Out of support scope", + AnalysisNotFix: "no_fix_planned-Will not fix", + AnalysisComponentNotPresent: "component_not_present", + AnalysisMitigationsExist: "inline_mitigations_already_exist", + AnalysisCannotControlled: "vulnerable_code_cannot_be_controlled_by_adversary", + AnalysisNotExecute: "vulnerable_code_not_in_execute_path", + AnalysisCodeNotPresent: "vulnerable_code_not_present", +} + +var ( + AnalysisSets = sets.NewString( + AnalysisWillFix, + AnalysisStill, + AnalysisNoPatch, + AnalysisUpgrade, + AnalysisOutScope, + AnalysisNotFix, + AnalysisComponentNotPresent, + AnalysisMitigationsExist, + AnalysisCannotControlled, + AnalysisNotExecute, + AnalysisCodeNotPresent, + ) +) diff --git a/cve-vulner-manager/controllers/hook.go b/cve-vulner-manager/controllers/hook.go index 218ab5bd915142b26bd6c12774cf3224f0ec1e24..8ed47936e5bafed9d4376ec4b5e2f9e3fe79f586 100644 --- a/cve-vulner-manager/controllers/hook.go +++ b/cve-vulner-manager/controllers/hook.go @@ -445,9 +445,7 @@ func closeIssueProc(issueHook *models.IssuePayload, issueTmp *models.IssueTempla sigReviewSend(issueHook, issueTmp, token, owner, fixed, unFix, assignee, cveCenter) } if openScoreFlag { - unFixList := taskhandler.CheckAffectVerComplete(issueTmp.AffectedVersion, issueTmp.Repo, - issueTmp.OwnedVersion, cveCenter.OrganizationID) - if len(unFixList) > 0 { + if msg, tb, ok := taskhandler.CheckIssueAnalysisComplete(issueTmp, cveCenter.OrganizationID); !ok { //send comment to issue issueTmp.IssueStatus = 1 issueTmp.IssueLabel = unFix @@ -455,68 +453,53 @@ func closeIssueProc(issueHook *models.IssuePayload, issueTmp *models.IssueTempla _, issueErr := taskhandler.UpdateIssueToGit(token, owner, issueTmp.Repo, *cveCenter, *issueTmp) if issueErr == nil { - na := "\n**请确认分支信息是否填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(CommentCheckVersion, issueHook.Sender.UserName, strings.Join(unFixList, ",")) + na + na := "\n**请确认分析内容的准确性,待分析内容请填写完整,否则将无法关闭当前issue.**" + //cc := fmt.Sprintf(ContentReview, "@"+issueHook.Sender.UserName) + tb + na + cc := fmt.Sprintf(CommentAnalysisCplTpl, issueHook.Sender.UserName, msg) + na taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) content := fmt.Sprintf("%v 仓库的CVE和安全问题的ISSUE,CVE编号: %v,", issueTmp.Repo, issueTmp.CveNum) - taskhandler.SendPrivateLetters(token, content, issueHook.Sender.UserName) + taskhandler.SendPrivateLetters(token, content+msg, issueHook.Sender.UserName) } } else { - if msg, tb, ok := taskhandler.CheckIssueClosedAnalysisComplete(issueTmp); !ok { - //send comment to issue + //1. change issue status + issueTmp.IssueStatus = 2 + //issueTmp.Status = 3 + cveCenter.IsExport = 3 + if issueTmp.MtAuditFlag == 0 { issueTmp.IssueStatus = 1 + issueTmp.Status = 1 + cveCenter.IsExport = 0 issueTmp.IssueLabel = unFix issueTmp.StatusName = "open" _, issueErr := taskhandler.UpdateIssueToGit(token, owner, issueTmp.Repo, *cveCenter, *issueTmp) if issueErr == nil { - na := "\n**请确认分析内容的准确性,待分析内容请填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(ContentReview, "@"+issueHook.Sender.UserName) + tb + na + na := "\n**issue关闭前,请确认模板分析内容的准确性与完整性,确认无误后,请在评论区输入: /approve, 否则无法关闭当前issue.**" + cc := fmt.Sprintf(ContentReview, assignee) + tb + na taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) - content := fmt.Sprintf("%v 仓库的CVE和安全问题的ISSUE,CVE编号: %v,", issueTmp.Repo, issueTmp.CveNum) - taskhandler.SendPrivateLetters(token, content+msg, issueHook.Sender.UserName) - } - } else { - //1. change issue status - issueTmp.IssueStatus = 2 - //issueTmp.Status = 3 - cveCenter.IsExport = 3 - if issueTmp.MtAuditFlag == 0 { - issueTmp.IssueStatus = 1 - issueTmp.Status = 1 - cveCenter.IsExport = 0 - issueTmp.IssueLabel = unFix - issueTmp.StatusName = "open" - _, issueErr := taskhandler.UpdateIssueToGit(token, owner, issueTmp.Repo, - *cveCenter, *issueTmp) - if issueErr == nil { - na := "\n**issue关闭前,请确认模板分析内容的准确性与完整性,确认无误后,请在评论区输入: /approve, 否则无法关闭当前issue.**" - cc := fmt.Sprintf(ContentReview, assignee) + tb + na - taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) - } - return } - issueTmp.IssueLabel = unFix - issueTmp.StatusName = "open" - issueTmp.Status = 1 - issuePrFlag := VerifyIssueAsPr(issueTmp, *cveCenter, false, - assignee, issueHook.Sender.UserName) - if issuePrFlag { - issueTmp.StatusName = issueHook.Issue.StateName - issueTmp.Status = 3 - if isNormalCloseIssue(issueTmp.CveId, issueTmp.IssueStatus) { - issueTmp.IssueStatus = 2 - cveCenter.IsExport = 3 - issueTmp.IssueLabel = fixed - } else { - issueTmp.IssueStatus = 6 - cveCenter.IsExport = 2 - issueTmp.IssueLabel = unFix - } + return + } + issueTmp.IssueLabel = unFix + issueTmp.StatusName = "open" + issueTmp.Status = 1 + issuePrFlag := VerifyIssueAsPr(issueTmp, *cveCenter, false, + assignee, issueHook.Sender.UserName) + if issuePrFlag { + issueTmp.StatusName = issueHook.Issue.StateName + issueTmp.Status = 3 + if isNormalCloseIssue(issueTmp.CveId, issueTmp.IssueStatus) { + issueTmp.IssueStatus = 2 + cveCenter.IsExport = 3 + issueTmp.IssueLabel = fixed } else { - issueTmp.IssueStatus = 1 - cveCenter.IsExport = 0 + issueTmp.IssueStatus = 6 + cveCenter.IsExport = 2 + issueTmp.IssueLabel = unFix } + } else { + issueTmp.IssueStatus = 1 + cveCenter.IsExport = 0 } } } @@ -579,7 +562,7 @@ func handleIssueStateChange(issueHook *models.IssuePayload) error { } } - var checkFunc func(template *models.IssueTemplate, v *models.VulnCenter) (string, string, bool) + var checkFunc func(template *models.IssueTemplate, organizationID int8) (string, string, bool) switch cveCenter.OrganizationID { case util.Openeuler: checkFunc = taskhandler.CheckIssueAnalysisComplete @@ -590,7 +573,7 @@ func handleIssueStateChange(issueHook *models.IssuePayload) error { case IssueOpenState: issueTmp.Status = 1 cveCenter.IsExport = 0 - _, _, ok := checkFunc(&issueTmp, &cveCenter) + _, _, ok := checkFunc(&issueTmp, cveCenter.OrganizationID) if ok { issueTmp.IssueStatus = 3 } else { @@ -601,7 +584,7 @@ func handleIssueStateChange(issueHook *models.IssuePayload) error { case IssueProgressState: issueTmp.Status = 2 cveCenter.IsExport = 0 - _, _, ok := checkFunc(&issueTmp, &cveCenter) + _, _, ok := checkFunc(&issueTmp, cveCenter.OrganizationID) if ok { issueTmp.IssueStatus = 3 } else { @@ -672,8 +655,8 @@ func VerifyIssueAsPr(issueTmp *models.IssueTemplate, cveCenter models.VulnCenter } } - if sn.AffectProduct != "" && len(sn.AffectProduct) > 1 { - tmpAffectBranchsxList = strings.Split(sn.AffectProduct, "/") + if sn.WillFixProduct != "" && len(sn.WillFixProduct) > 1 { + tmpAffectBranchsxList = strings.Split(sn.WillFixProduct, "/") } affectProductList = common.RemoveDupString(tmpAffectBranchsxList) if len(affectProductList) > 0 { @@ -721,7 +704,7 @@ func VerifyIssueAsPr(issueTmp *models.IssueTemplate, cveCenter models.VulnCenter } } if len(branchMaps) == 0 { - logs.Info("sn.AffectProduct: ", sn.AffectProduct, ",There is no branch to follow to associate with pr") + logs.Info("sn.WillFixProduct: ", sn.WillFixProduct, ",There is no branch to follow to associate with pr") return true } brandStr := "" @@ -871,6 +854,29 @@ func paraAffectBrandBool(affectedVersion string) bool { return false } +func paraAnalysisBrandBool(analysisVersion string) bool { + unaffectedBranchList := make([]string, 0) + analysisVersion = strings.ReplaceAll(analysisVersion, ":", ":") + brandsGroup := strings.Split(analysisVersion, ",") + if len(brandsGroup) > 0 { + for _, brand := range brandsGroup { + if brand == "" || len(brand) < 2 { + continue + } + brand = common.BranchVersionRep(brand) + brandList := strings.Split(brand, ":") + if len(brandList) > 1 { + prams := strings.Replace(brandList[1], " ", "", -1) + if common.AnalysisSets.Has(prams) { + unaffectedBranchList = append(unaffectedBranchList, brandList[0]) + } + } + } + } + + return len(unaffectedBranchList) > 0 +} + func getPRRelatedBrandsAllIssue(token, owner, repo string, num int, issueNum string) bool { issueFlag := false url := fmt.Sprintf(`https://gitee.com/api/v5/repos/%s/%s/pulls/%v/issues`, owner, repo, num) @@ -1324,18 +1330,10 @@ func otherMaintainerApprove( func maintainerApprove(issueTmp *models.IssueTemplate, cuAccount, owner, token, fixed, unfixed string, organizationID int8) { - unFixList := taskhandler.CheckAffectVerComplete(issueTmp.AffectedVersion, - issueTmp.Repo, issueTmp.OwnedVersion, organizationID) - if len(unFixList) > 0 { - na := "\n**请确认分支信息是否填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(CommentCheckVersion, cuAccount, strings.Join(unFixList, ",")) + na - taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) - return - } - if _, tb, ok := taskhandler.CheckIssueClosedAnalysisComplete(issueTmp); !ok { + if msg, _, ok := taskhandler.CheckIssueAnalysisComplete(issueTmp, organizationID); !ok { //send comment to issue na := "\n**请确认分析内容的准确性,待分析内容请填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(AnalysisComplete, cuAccount) + tb + na + cc := fmt.Sprintf(CommentAnalysisCplTpl, cuAccount, msg) + na taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) return } else { @@ -1387,18 +1385,10 @@ func maintainerApprove(issueTmp *models.IssueTemplate, cuAccount, owner, token, func securityApprove(issueTmp *models.IssueTemplate, cuAccount, owner, token, fixed, unfixed string, organizationID int8) { - unFixList := taskhandler.CheckAffectVerComplete(issueTmp.AffectedVersion, - issueTmp.Repo, issueTmp.OwnedVersion, organizationID) - if len(unFixList) > 0 { - na := "\n**请确认分支信息是否填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(CommentCheckVersion, cuAccount, strings.Join(unFixList, ",")) + na - taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) - return - } - if _, tb, ok := taskhandler.CheckIssueClosedAnalysisComplete(issueTmp); !ok { + if msg, _, ok := taskhandler.CheckIssueAnalysisComplete(issueTmp, organizationID); !ok { //send comment to issue na := "\n**请确认分析内容的准确性,待分析内容请填写完整,否则将无法关闭当前issue.**" - cc := fmt.Sprintf(AnalysisComplete, cuAccount) + tb + na + cc := fmt.Sprintf(CommentAnalysisCplTpl, cuAccount, msg) + na taskhandler.AddCommentToIssue(cc, issueTmp.IssueNum, owner, issueTmp.Repo, token) return } else { @@ -1788,6 +1778,13 @@ func analysisComment(owner, accessToken, path string, cuAccount string, cBody st cols = append(cols, k) } } + case "analysis_version": + if v != "" && len(v) > 1 { + if paraAnalysisBrandBool(v) { + issueTmp.AnalysisVersion = v + cols = append(cols, k) + } + } case "solution": issueTmp.Solution = v cols = append(cols, k) @@ -1836,14 +1833,14 @@ func analysisComment(owner, accessToken, path string, cuAccount string, cBody st logs.Error(webhookCommentLogTag, "canVerfy of ", issueTmp.IssueNum, canVerfy) if canVerfy { //Check whether the data is legal - var checkFunc func(template *models.IssueTemplate, v *models.VulnCenter) (string, string, bool) + var checkFunc func(template *models.IssueTemplate, organizationID int8) (string, string, bool) switch v.OrganizationID { case util.Openeuler: checkFunc = taskhandler.CheckIssueAnalysisComplete default: checkFunc = taskhandler.CheckOtherIssueAnalysisComplete } - if msg, tb, ok := checkFunc(&issueTmp, &v); !ok { + if msg, tb, ok := checkFunc(&issueTmp, v.OrganizationID); !ok { //send comment to issue issueTmp.IssueStatus = 1 err := models.UpdateIssueTemplate(&issueTmp, "issue_status") diff --git a/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/dto.go b/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/dto.go index 200e707a53da96de7e3c35a29a818719cdb2ff24..a79d50606f6dd04682a007976dbc4b6946e13326 100644 --- a/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/dto.go +++ b/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/dto.go @@ -12,6 +12,7 @@ type CveInfo struct { Summary string `json:"summary"` Description string `json:"description"` AffectProduct string `json:"affect_product"` + WillFixProduct string `json:"will_fix_product"` ReferenceLink string `json:"reference_link"` OpeneulerScore float64 `json:"openeuler_score"` Theme string `json:"theme"` @@ -19,3 +20,16 @@ type CveInfo struct { CveVersion string `json:"cve_version"` AbiVersion string `json:"abi_version"` } + +// 是否是新增了分析说明模块的数据 +func (c CveInfo) IsIssueWithAnalysis() bool { + return c.WillFixProduct != "" +} + +func (c CveInfo) GetAffectProduct() string { + if c.IsIssueWithAnalysis() { + return c.WillFixProduct + } else { + return c.AffectProduct + } +} diff --git a/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/impl.go b/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/impl.go index c5ac2d5c7875e387379f57cf5ca005467b13b851..995717b352b46ce056dd7b925f92ebc0b40eeea8 100644 --- a/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/impl.go +++ b/cve-vulner-manager/cve-ddd/infrastructure/repositoryimpl/impl.go @@ -25,7 +25,7 @@ func (impl repositoryImpl) FindCves(opt repository.Option) (cves domain.Cves, er sql := `select a.cve_num,a.cve_version,b.openeuler_score,b.openeuler_vector,b.issue_num, b.repo,b.abi_version,b.owned_component, b.cve_brief, b.cve_level, b.affected_version, - c.introduction, c.summary, c.description, c.affect_product, c.reference_link,c.theme + c.introduction, c.summary, c.description, c.affect_product, c.will_fix_product, c.reference_link,c.theme from cve_vuln_center a join cve_issue_template b on a.cve_id=b.cve_id join cve_security_notice c on a.cve_id=c.cve_id @@ -46,14 +46,14 @@ and b.status < 4 } for _, v := range data { - affectVersion := strings.Split(v.AffectProduct, "/") + affect := v.GetAffectProduct() cve := domain.Cve{ Component: v.OwnedComponent, Description: v.Description, SeverityLevel: v.CveLevel, - AffectedVersion: affectVersion, - AffectedProduct: v.AffectProduct, + AffectedVersion: strings.Split(affect, "/"), + AffectedProduct: affect, OpeneulerScore: v.OpeneulerScore, Theme: v.Theme, CveNum: v.CveNum, @@ -122,7 +122,21 @@ func (impl repositoryImpl) SaveIssueNum(num string) error { type list struct { models.IssueTemplate - AffectProduct string `orm:"column(affect_product)"` + AffectProduct string `orm:"column(affect_product)"` + WillFixProduct string `orm:"column(will_fix_product)"` +} + +// 是否是新增了分析说明模块的数据 +func (l list) IsIssueWithAnalysis() bool { + return l.WillFixProduct != "" +} + +func (l list) GetAffectProduct() []string { + if l.IsIssueWithAnalysis() { + return strings.Split(l.WillFixProduct, "/") + } else { + return strings.Split(l.AffectProduct, "/") + } } var statusMap = map[int8]string{ @@ -132,7 +146,7 @@ var statusMap = map[int8]string{ } func (impl repositoryImpl) GetAllIssue() (data domain.CollectedDataSlice, err error) { - sql := `select a.*, b.affect_product from cve_issue_template a + sql := `select a.*, b.affect_product, b.will_fix_product from cve_issue_template a join cve_security_notice b on a.cve_id=b.cve_id where a.cve_id in (select cve_id from cve_vuln_center where cve_status = 2 and is_export in (0,3) and organizate_id = 1) and a.status < 4 @@ -155,7 +169,7 @@ func (impl repositoryImpl) GetAllIssue() (data domain.CollectedDataSlice, err er CveNum: v.CveNum, Score: v.OpenEulerScore, Version: v.OwnedVersion, - AffectedProduct: strings.Split(v.AffectProduct, "/"), + AffectedProduct: v.GetAffectProduct(), CreateTime: v.CreateTime, } diff --git a/cve-vulner-manager/doc/md/defect-manager-manual.md b/cve-vulner-manager/doc/md/defect-manager-manual.md new file mode 100644 index 0000000000000000000000000000000000000000..5a6b1eec38f94a97af1b2f21c70053718a91ec8a --- /dev/null +++ b/cve-vulner-manager/doc/md/defect-manager-manual.md @@ -0,0 +1,182 @@ + + + + +### 一、缺陷流程 + +![输入图片说明](pictures/%E7%BC%BA%E9%99%B7%E5%88%9B%E5%BB%BA%E9%97%AD%E7%8E%AF%E6%B5%81%E7%A8%8B-final.png) + +### 二、缺陷如何创建: + +#### 2.1 缺陷模板: + +``` +**【缺陷描述】:请补充详细的缺陷问题现象描述** + +**一、缺陷信息** + +**【缺陷所属的os版本】(如openEuler-22.03-LTS,参考命令"cat /etc/os-release"结果)** + +**【内核版本】(如kernel-5.10.0-60.138.0.165,参考命令"uname -r"结果)** + +**【缺陷所属软件及版本号】(如kernel-5.10.0-60.138.0.165,参考命令"rpm -q 包名"结果)** + +**【环境信息】** +硬件信息 + +- 提供跟硬件相关的信息,如架构、cpu和内存规格等 +- 虚拟机场景,额外补充宿主机os版本类型 + +软件信息 + +- 跟缺陷所属软件相关的其它软件版本信息(如软件包构建失败由gcc引起,请填写gcc的版本号) + +网络信息 + +- 如果有特殊组网,请提供网络拓扑信息以及网络数据走向 + +**【问题复现步骤】:请描述具体的操作步骤** + +**【实际结果】**,请描述出问题的结果和影响 +**【期望结果】**,请描述出期望的结果和影响 +**【其他相关附件信息】** +比如系统message日志/组件日志、dump信息、图片等 + +**【缺陷详情及分析指导参考链接】** +``` + +#### 2.2 缺陷模板创建示例: + + +![输入图片说明](pictures/%E7%BC%BA%E9%99%B7%E5%88%9B%E5%BB%BA%E4%B8%BE%E4%BE%8B.png) + +### 三、缺陷如何评论: + +#### 3.1 评论模板: + +``` +影响性分析说明: + +缺陷严重等级:(Critical/High/Moderate/Low) + +缺陷根因说明: + +受影响版本排查(受影响/不受影响): + +openEuler-20.03-LTS-SP4: +openEuler-22.03-LTS-SP1: +openEuler-22.03-LTS-SP3: +openEuler-22.03-LTS-SP4: +openEuler-24.03-LTS: +abi变化(是/否): + +openEuler-20.03-LTS-SP4: +openEuler-22.03-LTS-SP1: +openEuler-22.03-LTS-SP3: +openEuler-22.03-LTS-SP4: +openEuler-24.03-LTS: +``` + +#### 3.2 评论注意事项: + +- 影响性分析说明:必填 +- 缺陷严重等级:必填,Critical/High/Moderate/Low四选一填写 +- 缺陷根因说明: + - [ ] 各分支都不受影响:无需填写 + - [ ] 存在一个分支受影响:需填写 +- 受影响版本排查:必填,受影响/不受影响 二选一填写 +- abi变化:必填,是/否 二选一填写 + +#### 3.3 评论后回显: + +1. openeuler-ci-bot将用户评论回显到issue描述中的"二、缺陷分析结构反馈"部分 + +2. openeuler-ci-bot将用户评论回显为表格: + + | 状态 | 需分析 | 内容 | + | ------ | ---------------- | -------------------------- | + | 已分析 | 1.影响性分析说明 | 您分析的内容 | + | 已分析 | 2.缺陷严重等级 | Low | + | 已分析 | 3.缺陷根因说明 | 根因内容 | + | 已分析 | 4.受影响版本排查 | openEuler-20.03-LTS-SP4:受影响 | + | 已分析 | 5.abi变化 | openEuler-20.03-LTS-SP4:是 | + +#### 3.4 评论例子: +![输入图片说明](pictures/%E8%AF%84%E8%AE%BA%E4%BE%8B%E5%AD%90.png) + + +### 四、缺陷如何闭环: + +#### 4.1 其中有一分支受影响: + +- 所有受影响分支pr均需合入,否则issue会被openeuler-ci-bot重新打开 + +![输入图片说明](pictures/%E5%8F%97%E5%BD%B1%E5%93%8D%E5%88%86%E6%94%AFpr%E5%90%88%E5%85%A5.png) + +- 所有受影响分支pr合入后,issue状态切换已完成,issue会被打上FIXED标签 + +![输入图片说明](pictures/issue%E6%89%93fixed%E6%A0%87%E7%AD%BE.png) + +#### 4.2 各分支都不受影响: + +- issue状态切换已完成,issue会被打上UNAFFECTED标签 + +![输入图片说明](pictures/%E4%B8%8D%E5%8F%97%E5%BD%B1%E5%93%8D1.png) + +![输入图片说明](pictures/%E4%B8%8D%E5%8F%97%E5%BD%B1%E5%93%8D2.png) + +#### 4.3 非问题取消: + +- 无需评论影响性分析说明 + +- 评论/reason 非问题原因,issue状态切换为已取消 + +![输入图片说明](pictures/%E5%B7%B2%E5%8F%96%E6%B6%88.png) + +- 若后续需要评论修复,需要先将issue修改为待办的 + +#### 4.4 无解决方案挂起: + +- 若issue暂无解决方案,需挂起,评论 /reason 挂起原因,issue状态切换我已挂起 + +![输入图片说明](pictures/%E5%B7%B2%E6%8C%82%E8%B5%B7.png) + +#### 4.5 其余issue状态切换说明: + +- issue状态切换为修复中、已确认,工具不做检测 + +### 五、在研版本缺陷创建: + +#### 5.1 在研版本不使用缺陷模板原因: + +- 实际场景问题:在研版本实时跟进每轮issue闭环情况,若和维护版本共用一个issue,会影响在研版本跟进问题进度及效率 + +#### 5.2 在研版本如何创建缺陷issue: + +- 缺陷模板中:”缺陷所属OS版本”为在研版本,操作流程和旧的缺陷流程保持不变,开发无需评论各分支是否受影响 + +- 如以下图片中24.09为在研版本,openeuler-ci-bot判断24.09为白名单版本,不再做任何模板创建及评论的校验 + +![输入图片说明](pictures/%E5%9C%A8%E7%A0%94%E7%89%88%E6%9C%ACissue%E5%88%9B%E5%BB%BA.png) + +### 六、FAQ: + +#### 6.1 在创建完在维版本相关issue后,issue未出现评论模板回显,即工具defect-manager没有对应的动作响应,是为什么,怎么解决: + +- 这种情况出现的原因可能是gitee的webhook消息丢失,未到达工具defect-manager,可以在issue下方评论/check-issue重新触发工具响应。 + +#### 6.2 目前哪些版本是在维版本,哪些版本是在研版本: +``` +在维版本: +openEuler-20.03-LTS-SP4: +openEuler-22.03-LTS-SP1: +openEuler-22.03-LTS-SP3: +openEuler-22.03-LTS-SP4: +openEuler-24.03-LTS: + +在研版本: +master +openEuler-24.09 +openEuler-24.03-LTS-Next +``` + diff --git "a/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\345\267\245\345\205\267\346\217\220\347\244\272.png" "b/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\345\267\245\345\205\267\346\217\220\347\244\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..f2a3646fd198084a4b950de30a626e91d4b8590e Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\345\267\245\345\205\267\346\217\220\347\244\272.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\346\217\220\347\244\272.png" "b/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\346\217\220\347\244\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..83cf5494aee1560a83ac575eeef08ed4a4b01fbb Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/issue\345\210\233\345\273\272\351\224\231\350\257\257\346\217\220\347\244\272.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/issue\346\211\223fixed\346\240\207\347\255\276.png" "b/cve-vulner-manager/doc/md/pictures/issue\346\211\223fixed\346\240\207\347\255\276.png" new file mode 100644 index 0000000000000000000000000000000000000000..05c2ae9220ef82760ef44c54dffb534b21cc6a2a Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/issue\346\211\223fixed\346\240\207\347\255\276.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2151.png" "b/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2151.png" new file mode 100644 index 0000000000000000000000000000000000000000..bb7677316f9980e0aa5e7aa3b116e6dc99d7dd7e Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2151.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2152.png" "b/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2152.png" new file mode 100644 index 0000000000000000000000000000000000000000..58846693b1464abed0469175a5c17a081f839ced Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\344\270\215\345\217\227\345\275\261\345\223\2152.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\345\217\227\345\275\261\345\223\215\345\210\206\346\224\257pr\345\220\210\345\205\245.png" "b/cve-vulner-manager/doc/md/pictures/\345\217\227\345\275\261\345\223\215\345\210\206\346\224\257pr\345\220\210\345\205\245.png" new file mode 100644 index 0000000000000000000000000000000000000000..d464a1423b886911d15862cc20b8deb7db70a9d9 Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\345\217\227\345\275\261\345\223\215\345\210\206\346\224\257pr\345\220\210\345\205\245.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\345\234\250\347\240\224\347\211\210\346\234\254issue\345\210\233\345\273\272.png" "b/cve-vulner-manager/doc/md/pictures/\345\234\250\347\240\224\347\211\210\346\234\254issue\345\210\233\345\273\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..49859c82313be917fbe8f7319dea297ab24ababd Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\345\234\250\347\240\224\347\211\210\346\234\254issue\345\210\233\345\273\272.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\345\267\262\345\217\226\346\266\210.png" "b/cve-vulner-manager/doc/md/pictures/\345\267\262\345\217\226\346\266\210.png" new file mode 100644 index 0000000000000000000000000000000000000000..84fdaecfda808b8b09192fe36444cce59844b7ce Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\345\267\262\345\217\226\346\266\210.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\345\267\262\346\214\202\350\265\267.png" "b/cve-vulner-manager/doc/md/pictures/\345\267\262\346\214\202\350\265\267.png" new file mode 100644 index 0000000000000000000000000000000000000000..fe465338597e3d786528f2d194781fdaa537cf7f Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\345\267\262\346\214\202\350\265\267.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\344\270\276\344\276\213.png" "b/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\344\270\276\344\276\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..2820c729ac6fcfd58866ae1dcdafd79d6856fdcb Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\344\270\276\344\276\213.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\351\227\255\347\216\257\346\265\201\347\250\213-final.png" "b/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\351\227\255\347\216\257\346\265\201\347\250\213-final.png" new file mode 100644 index 0000000000000000000000000000000000000000..6ea43a047a7f77e8fab0f7f8096a9fe1bf48a838 Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\347\274\272\351\231\267\345\210\233\345\273\272\351\227\255\347\216\257\346\265\201\347\250\213-final.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\350\257\204\350\256\272\344\276\213\345\255\220.png" "b/cve-vulner-manager/doc/md/pictures/\350\257\204\350\256\272\344\276\213\345\255\220.png" new file mode 100644 index 0000000000000000000000000000000000000000..e65ad1a563a5ac1574d12ba20b6f4dfdd2bf057c Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\350\257\204\350\256\272\344\276\213\345\255\220.png" differ diff --git "a/cve-vulner-manager/doc/md/pictures/\350\257\246\346\203\205.png" "b/cve-vulner-manager/doc/md/pictures/\350\257\246\346\203\205.png" new file mode 100644 index 0000000000000000000000000000000000000000..3bacb753c0da577ba4052bf705d325bc70618575 Binary files /dev/null and "b/cve-vulner-manager/doc/md/pictures/\350\257\246\346\203\205.png" differ diff --git a/cve-vulner-manager/models/cve.go b/cve-vulner-manager/models/cve.go index e49d2c364699c318c2f259953196ee4bd312cb6e..53ba282cb64a9c3b04b2db101fe12fb8d6dd7440 100644 --- a/cve-vulner-manager/models/cve.go +++ b/cve-vulner-manager/models/cve.go @@ -1044,7 +1044,7 @@ func GetCanExportExcelData(cveNum, issueNum, repo string, issueId int64) (list [ sql := `SELECT b.num,c.*,a.issue_num,a.owned_component,a.cve_brief, d.sec_id,d.introduction,d.summary,d.theme,d.description,d.influence_component, d.affect_product,d.reference_link,d.affect_status, -e.public_date,e.openeuler_sa_num,a.cve_level,b.organizate_id,a.affected_version,a.issue_label +e.public_date,e.openeuler_sa_num,a.cve_level,b.organizate_id,a.affected_version,a.analysis_version,a.issue_label FROM cve_issue_template a RIGHT JOIN (SELECT (SELECT COUNT(*) FROM cve_vuln_center WHERE cve_num = ? AND is_export in (0,3) AND pack_name = ? AND organizate_id = 1) num, diff --git a/cve-vulner-manager/models/excel.go b/cve-vulner-manager/models/excel.go index 43d76ec669fb3c66288088c1c28f413e6679eb4b..ec4acb5f2945b51e1790f2aabb3d2d0b2f1ac48f 100644 --- a/cve-vulner-manager/models/excel.go +++ b/cve-vulner-manager/models/excel.go @@ -2,12 +2,15 @@ package models import ( "fmt" + "strings" "github.com/astaxie/beego/logs" "github.com/astaxie/beego/orm" + + "cvevulner/common" ) -//ExcelExport the export excel row content model +// ExcelExport the export excel row content model type ExcelExport struct { Num int64 Score @@ -27,39 +30,120 @@ type ExcelExport struct { CveLevel string `json:"cve_level" orm:"size(32);column(cve_level)"` OrganizateId int8 `json:"organizate_id" orm:"column(organizate_id)"` AffectedVersion string `json:"affected_version" orm:"column(affected_version)"` + AnalysisVersion string `json:"analysis_version" orm:"column(analysis_version)"` IssueLabel string `json:"issue_label" orm:"column(issue_label)"` Repo string `json:"repo" orm:"column(repo)"` } -//ExcelPackage Released packages +func (e ExcelExport) IsIssueWithAnalysisVersion() bool { + return e.AnalysisVersion != "" +} + +func (e ExcelExport) ParseAnalysisVersion() map[string]string { + result := make(map[string]string) + split := strings.Split(e.AnalysisVersion, ",") + for _, v := range split { + item := strings.Split(strings.ReplaceAll(v, ":", ":"), ":") + if len(item) != 2 || item[1] == "" { + return nil + } + + result[item[0]] = item[1] + } + + return result +} + +func (e ExcelExport) GetReasonByVersion(v string) string { + version := e.ParseAnalysisVersion() + reason, ok := version[v] + if ok { + english, ok2 := common.AnalysisEnglishMap[reason] + if ok2 { + return english + } + } + + return "" +} + +func (e ExcelExport) WillFixVersion() []string { + var version []string + + for v, reason := range e.ParseAnalysisVersion() { + if reason == common.AnalysisWillFix { + version = append(version, v) + } + } + + return version +} + +func (e ExcelExport) IsWillFixVersion(v string) bool { + for _, version := range e.WillFixVersion() { + if version == v { + return true + } + } + + return false +} + +func (e ExcelExport) IsNotWillFixVersion(v string) bool { + for version, reason := range e.ParseAnalysisVersion() { + if v == version && reason != common.AnalysisWillFix { + return true + } + } + + return false +} + +func (e ExcelExport) AffectType(v string) string { + if !e.IsIssueWithAnalysisVersion() { + return common.TypeUnaffected + } + + for version, reason := range e.ParseAnalysisVersion() { + if v == version { + if _, ok := common.AnalysisUnaffected[reason]; ok { + return common.TypeUnaffected + } + } + } + + return common.TypeAffected +} + +// ExcelPackage Released packages type ExcelPackage struct { PubTime string Repo string Packages string } -//Insert Insert a generated excel file record +// Insert Insert a generated excel file record func (er ExportRecord) Insert() error { o := orm.NewOrm() _, err := o.Insert(&er) return err } -//QueryLast query the last excel record +// QueryLast query the last excel record func (er *ExportRecord) QueryLast() error { o := orm.NewOrm() err := o.QueryTable(er).Filter("state", 0).OrderBy("-create_time").One(er) return err } -//Update update by column name +// Update update by column name func (er *ExportRecord) Update(field ...string) error { o := orm.NewOrm() _, err := o.Update(er, field...) return err } -//Read read by column name +// Read read by column name func (er *ExportRecord) Read(field ...string) error { o := orm.NewOrm() return o.Read(er, field...) diff --git a/cve-vulner-manager/models/modeldb.go b/cve-vulner-manager/models/modeldb.go index 47d3196d15f940c08871bcb12af64aff10c2f545..d9be60f6a556e686de7c3d3d72748854975f560a 100644 --- a/cve-vulner-manager/models/modeldb.go +++ b/cve-vulner-manager/models/modeldb.go @@ -146,6 +146,7 @@ type SecurityNotice struct { Description string `orm:"type(text);column(description)" description:"安全公告描述"` InfluenceComponent string `orm:"size(256);null;column(influence_component)" description:"影响组件"` AffectProduct string `orm:"size(256);null;column(affect_product)" description:"影响产品"` + WillFixProduct string `orm:"size(256);null;column(will_fix_product)" description:"正常修复产品"` ReferenceLink string `orm:"type(text);null;column(reference_link)" description:"参考链接"` Status int8 `orm:"default(0);column(sec_status)" description:"0:未发布;1:已发布"` AffectStatus string `orm:"size(16);null;column(affect_status)" description:"Fixed:已解决;UnFixed:未解决;UnAffected:不影响"` @@ -177,6 +178,7 @@ type IssueTemplate struct { CveAnalysis string `orm:"type(text);column(cve_analysis)" description:"影响性分析说明"` PrincipleAnalysis string `orm:"type(text);column(principle_analysis)" description:"原理分析"` AffectedVersion string `orm:"size(256);column(affected_version)" description:"受影响的版本"` + AnalysisVersion string `orm:"type(text);column(analysis_version)" description:"分析说明"` Solution string `orm:"type(text);column(solution)" description:"规避方案或消减措施"` IssueId int64 `orm:"column(issue_id)" description:"issue的id"` IssueNum string `orm:"size(64);column(issue_num)" description:"issue编号"` diff --git a/cve-vulner-manager/task/cve.go b/cve-vulner-manager/task/cve.go index 354500cf639c5f4841ce592e454740968952cd4e..6cc82376ca76ef57778a510226000a7aa3227069 100644 --- a/cve-vulner-manager/task/cve.go +++ b/cve-vulner-manager/task/cve.go @@ -15,12 +15,17 @@ import ( "github.com/opensourceways/server-common-lib/utils" "cvevulner/common" + "cvevulner/cve-ddd/infrastructure/obsimpl" "cvevulner/taskhandler" "github.com/astaxie/beego/config" "github.com/astaxie/beego/logs" ) +const ( + indexUnaffectTxt = "index_unaffect.txt" +) + // ProcCveOriginData Process raw data obtained by api func ProcCveOriginData(prcNum, days, credibilityLevel, openeulerNum int, cveRef, owner string) (bool, error) { // Process raw data obtained by api @@ -127,6 +132,8 @@ func ReleaseUnaffectedCve() error { return obsErr } + updateIndexAffectTxt(localFileName) + syncUnCVEUrl := beego.AppConfig.String("reflink::openeuler_api") + "/cve-security-notice-server/syncUnCVE" client := utils.NewHttpClient(3) @@ -157,3 +164,18 @@ func ReleaseUnaffectedCve() error { return nil } + +func updateIndexAffectTxt(fileName string) { + path := beego.AppConfig.String("obs::download_cvrf_dir") + indexUnaffectTxt + content, err := obsimpl.Instance().Download(path) + if err != nil { + logs.Error("download %s failed: %s ", indexUnaffectTxt, err.Error()) + return + } + + newContent := string(content) + "\n" + fileName + newContent = strings.TrimSpace(newContent) + if err = obsimpl.Instance().Upload(path, []byte(newContent)); err != nil { + logs.Error("upload %s failed: %s ", indexUnaffectTxt, err.Error()) + } +} diff --git a/cve-vulner-manager/taskhandler/check.go b/cve-vulner-manager/taskhandler/check.go index 2d869fc178b53fd67912e925eb112b3b4fee6265..6f2e64645632336516a508a76b2f02f6516ac935 100644 --- a/cve-vulner-manager/taskhandler/check.go +++ b/cve-vulner-manager/taskhandler/check.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "k8s.io/apimachinery/pkg/util/sets" + "cvevulner/common" "cvevulner/models" "cvevulner/util" @@ -11,146 +13,128 @@ import ( "github.com/astaxie/beego/logs" ) -func CheckIssueAnalysisComplete(i *models.IssueTemplate, v *models.VulnCenter) (msg, tbStr string, ok bool) { - tb := - `| 状态 | 需分析 | 内容 | +var tb = `| 状态 | 分析项目 | 内容 | |:--:|:--:|---------| |%v|%v|%v| |%v|%v|%v| |%v|%v|%v| |%v|%v|%v| |%v|%v|%v| +|%v|%v|%v| ` +func CheckIssueAnalysisComplete(i *models.IssueTemplate, organizationID int8) (msg, tbStr string, ok bool) { if i == nil { logs.Error("issue template is nil") return msg, "", false } - ok = true - tbContent := make([]interface{}, 15) - if util.TrimString(i.CveAnalysis) == "" || len(util.TrimString(i.CveAnalysis)) < 1 { - msg = fmt.Sprintf("1.影响性分析说明=> 没有填写或按正确格式填写") - ok = false + + if util.TrimString(i.CveAnalysis) == "" { + msg = "1.影响性分析说明=> 没有填写或按正确格式填写" return } - tbContent[0] = "已分析" - tbContent[1] = "1.影响性分析说明" - tbContent[2] = util.TrimStringNR(i.CveAnalysis) - affectedVersionFlag := 1 + + affectBranchsxList := make([]string, 0) + owner, accessToken := common.GetOwnerAndToken("", organizationID) + if organizationID == 1 || organizationID == 2 { + affectBranchsxList, _ = GetBranchesInfo(accessToken, owner, i.Repo, organizationID) + } else if organizationID == 3 || organizationID == 4 { + affectBranchsxList = CreateBrandAndTags(accessToken, owner, i.Repo, organizationID) + } + + if len(affectBranchsxList) == 0 { + msg = "获取仓库分支信息失败" + return + } + affectBranchSets := sets.New(affectBranchsxList...) + + var affectedResult string if i.AffectedVersion != "" { - versionfFlag := true - affectedVersionArry := strings.Split(i.AffectedVersion, ",") - if len(affectedVersionArry) > 0 { - for _, affectx := range affectedVersionArry { - affect := common.BranchVersionRep(affectx) - versionArry := strings.Split(affect, ":") - if len(versionArry) > 1 { - if versionArry[1] == "受影响" || versionArry[1] == "不受影响" { - if versionArry[1] == "受影响" { - affectedVersionFlag = 2 - } - continue - } else { - affectedVersionFlag = 3 - versionfFlag = false - break - } - } else { - affectedVersionFlag = 3 - versionfFlag = false - break - } - } - } - if !versionfFlag { - msg = fmt.Sprintf("4.受影响版本排查(受影响/不受影响)=> 没有分析或未按正确格式填写:%v", i.AffectedVersion) - ok = false + checkResult := itemCheck(i.AffectedVersion, affectBranchSets, func(result string) bool { + return result == "受影响" || result == "不受影响" + }) + if checkResult != "" { + msg = fmt.Sprintf("4.受影响版本排查(受影响/不受影响)=> %s", checkResult) return } - if versionfFlag { - tbContent[9] = "已分析" - tbContent[10] = "4.受影响版本排查" - tbContent[11] = util.TrimStringNR(i.AffectedVersion) - } else { - tbContent[9] = "待分析" - tbContent[10] = "4.受影响版本排查" - tbContent[11] = util.TrimStringNR(i.AffectedVersion) - } - } else { - tbContent[9] = "已分析" - tbContent[10] = "4.受影响版本排查" - tbContent[11] = "" - } - if affectedVersionFlag == 1 { - tbContent[3] = "已分析" - tbContent[4] = "2.openEulerScore" - tbContent[5] = i.OpenEulerScore - tbContent[6] = "已分析" - tbContent[7] = "3.openEulerVector" - tbContent[8] = util.TrimStringNR(i.OpenEulerVector) - } else { - if i.OpenEulerScore == 0.0 && i.OpenEulerVector != util.VectorNone { - msg = fmt.Sprintf("2.openEulerScore=> 没有填写或正确填写(0-10)") - ok = false + + affectedResult = util.TrimStringNR(i.AffectedVersion) + + if i.OpenEulerVector == "" { + msg = "2.openEulerVector=> 没有正确填写" return } - tbContent[3] = "已分析" - tbContent[4] = "2.openEulerScore" - tbContent[5] = i.OpenEulerScore - if i.OpenEulerVector == "" || len(i.OpenEulerVector) < 1 { - msg = fmt.Sprintf("2.openEulerVector=> 没有正确填写") - ok = false + if i.OpenEulerScore == 0.0 && i.OpenEulerVector != util.VectorNone { + msg = "2.openEulerScore=> 没有填写或正确填写(0-10)" return } - tbContent[6] = "已分析" - tbContent[7] = "3.openEulerVector" - tbContent[8] = util.TrimStringNR(i.OpenEulerVector) } + + var abiResult string if i.AbiVersion != "" { - versionAbiFlag := true - abiVersionArry := strings.Split(i.AbiVersion, ",") - if len(abiVersionArry) > 0 { - for _, affectx := range abiVersionArry { - affect := common.BranchVersionRep(affectx) - versionArry := strings.Split(affect, ":") - if len(versionArry) > 1 { - if versionArry[1] == "是" || versionArry[1] == "否" { - continue - } else { - versionAbiFlag = false - break - } - } else { - versionAbiFlag = false - break - } - } - } - if !versionAbiFlag { - msg = fmt.Sprintf("5.修复是否涉及abi变化(是/否)=> 没有分析或未按正确格式填写:%v", i.AbiVersion) - ok = false + checkResult := itemCheck(i.AbiVersion, affectBranchSets, func(result string) bool { + return result == "是" || result == "否" + }) + if checkResult != "" { + msg = fmt.Sprintf("5.修复是否涉及abi变化(是/否)=> %s", checkResult) return } - if versionAbiFlag { - tbContent[12] = "已分析" - tbContent[13] = "5.修复是否涉及abi变化" - tbContent[14] = util.TrimStringNR(i.AbiVersion) - } else { - tbContent[12] = "待分析" - tbContent[13] = "5.修复是否涉及abi变化" - tbContent[14] = util.TrimStringNR(i.AbiVersion) + + abiResult = util.TrimStringNR(i.AbiVersion) + } + + var analysisReasonResult string + if i.AnalysisVersion != "" { + checkResult := itemCheck(i.AnalysisVersion, affectBranchSets, func(result string) bool { + return common.AnalysisSets.Has(result) + }) + if checkResult != "" { + msg = fmt.Sprintf("6.原因说明=> %v", checkResult) + return } - } else { - tbContent[12] = "已分析" - tbContent[13] = "5.修复是否涉及abi变化" - tbContent[14] = "" + + analysisReasonResult = util.TrimStringNR(i.AnalysisVersion) } - tbStr = fmt.Sprintf(tb, tbContent...) + + tbStr = fmt.Sprintf(tb, + "已分析", "1.影响性分析说明", util.TrimStringNR(i.CveAnalysis), + "已分析", "2.openEulerScore", i.OpenEulerScore, + "已分析", "3.openEulerVector", util.TrimStringNR(i.OpenEulerVector), + "已分析", "4.受影响版本排查", affectedResult, + "已分析", "5.是否涉及abi变化", abiResult, + "已分析", "6.原因说明", analysisReasonResult, + ) + + ok = true + return } -func CheckOtherIssueAnalysisComplete(i *models.IssueTemplate, v *models.VulnCenter) (msg, tbStr string, ok bool) { +func itemCheck(item string, branchSets sets.Set[string], resultCheck func(result string) bool) string { + itemBranchSets := make(sets.Set[string]) + for _, v := range strings.Split(item, ",") { + row := strings.Split(v, ":") + if len(row) != 2 { + return fmt.Sprintf("没有分析或未按正确格式填写: %s", v) + } + + if !resultCheck(row[1]) { + return fmt.Sprintf("分支 %s 结果填写错误", row[0]) + } + + itemBranchSets.Insert(row[0]) + } + + diff := branchSets.Difference(itemBranchSets) + if diff.Len() != 0 { + return fmt.Sprintf("没有分析或未按正确格式填写,涉及分支: %v", diff.UnsortedList()) + } + + return "" +} + +func CheckOtherIssueAnalysisComplete(i *models.IssueTemplate, organizationID int8) (msg, tbStr string, ok bool) { tb := `| 状态 | 需分析 | 内容 | |:--:|:--:|---------| @@ -164,7 +148,7 @@ func CheckOtherIssueAnalysisComplete(i *models.IssueTemplate, v *models.VulnCent return msg, "", false } var score, vector string - switch v.OrganizationID { + switch organizationID { case util.OpenLookeng: score = "openLooKengScore" vector = "openLooKengVector" @@ -237,7 +221,7 @@ func CheckOtherIssueAnalysisComplete(i *models.IssueTemplate, v *models.VulnCent tbContent[7] = "3." + vector tbContent[8] = util.TrimStringNR(i.OpenEulerVector) } else { - if v.OrganizationID != util.OpenGauss && i.OpenEulerScore == 0.0 { + if organizationID != util.OpenGauss && i.OpenEulerScore == 0.0 { msg = fmt.Sprintf("2.%s=> 没有填写或正确填写(0-10)", score) ok = false return diff --git a/cve-vulner-manager/taskhandler/common.go b/cve-vulner-manager/taskhandler/common.go index db2bc95f8a064f7e3ed6a41f14583a1d7fc0df9c..cd6bb4a33b46c792fc9da292023470c19a054574 100644 --- a/cve-vulner-manager/taskhandler/common.go +++ b/cve-vulner-manager/taskhandler/common.go @@ -90,6 +90,8 @@ const bodyTpl = `一、漏洞信息 %v 修复是否涉及abi变化(是/否): %v + 原因说明: + %v ` const bodyUpTpl = `一、漏洞信息 @@ -122,6 +124,8 @@ const bodyUpTpl = `一、漏洞信息 %v 修复是否涉及abi变化(是/否): %v + 原因说明: + %v ` const bodySecLinkTpl = `一、漏洞信息 漏洞编号:%v @@ -153,6 +157,8 @@ const bodySecLinkTpl = `一、漏洞信息 %v 修复是否涉及abi变化(是/否): %v + 原因说明: + %v 三、漏洞修复 安全公告链接:%v ` @@ -162,7 +168,7 @@ const commentCopyValue = ` **issue处理注意事项:** **1. 当前issue受影响的分支提交pr时, 须在pr描述中填写当前issue编号进行关联, 否则无法关闭当前issue;** **2. 模板内容需要填写完整, 无论是受影响或者不受影响都需要填写完整内容,未引入的分支不需要填写, 否则无法关闭当前issue;** -**3. 以下为模板中需要填写完整的内容, 请复制到评论区回复, 注: 内容的标题名称(影响性分析说明, openEuler评分, 受影响版本排查(受影响/不受影响), 修复是否涉及abi变化(是/否))不能省略,省略后cve-manager将无法正常解析填写内容.** +**3. 以下为模板中需要填写完整的内容, 请复制到评论区回复, 注: 内容的标题名称(影响性分析说明, openEuler评分, 受影响版本排查(受影响/不受影响), 修复是否涉及abi变化(是/否), 原因说明)不能省略,省略后cve-manager将无法正常解析填写内容.** ************************************************************************ 影响性分析说明: @@ -174,7 +180,24 @@ openEuler评分: (评分和向量) %v 修复是否涉及abi变化(是/否): %v +原因说明: +%v ----------------------------------------------------------------------- +原因说明填写请参考下方表格(注意:版本是否受影响和版本的原因说明必须对应,例如master版本分支受影响,那原因说明只能是受影响对应的原因之一!): +| 分支状态 | 原因说明 | +|:--:|:--:| +|受影响|正常修复| +|受影响|暂不修复-漏洞仍在分析中| +|受影响|暂不修复-暂无解决方案或补丁| +|受影响|暂不修复-待升级版本修复| +|受影响|不修复-超出修复范围| +|受影响|不修复-特殊原因导致不再修复| +|不受影响|不受影响-组件不存在| +|不受影响|不受影响-已有内置的内联控制或缓解措施| +|不受影响|不受影响-漏洞代码不能被攻击者触发| +|不受影响|不受影响-漏洞代码不在执行路径| +|不受影响|不受影响-漏洞代码不存在| + issue处理具体操作请参考: %v pr关联issue具体操作请参考: @@ -444,7 +467,7 @@ func CommentTemplate(assignee, commentCmd, affectedVersion, path string, assignL } else { assigneeStr = "@" + assignee } - commentTemplate := fmt.Sprintf(commentCopyValue, assigneeStr, affectedVersion, affectedVersion, commentCmd, PrIssueLink) + commentTemplate := fmt.Sprintf(commentCopyValue, assigneeStr, affectedVersion, affectedVersion, affectedVersion, commentCmd, PrIssueLink) return commentTemplate } @@ -918,6 +941,7 @@ func CreateIssueBody(accessToken, owner, path, assignee string, } affectedVersion := AffectVersionExtract(brandArray, its.AffectedVersion, cve.PackName, cve.OrganizationID) abiVersion := AffectVersionExtract(brandArray, its.AbiVersion, cve.PackName, cve.OrganizationID) + analysisVersion := AffectVersionExtract(brandArray, its.AnalysisVersion, cve.PackName, cve.OrganizationID) updateTemplateAbi(brandArray, its) createdTime := time.Now() @@ -1010,12 +1034,12 @@ func CreateIssueBody(accessToken, owner, path, assignee string, if its.Status == 3 && len(its.SecLink) > 3 && cve.OrganizationID == 1 { body = fmt.Sprintf(bodySecLinkTpl, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, its.SecLink) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, analysisVersion, its.SecLink) } else { if cve.OrganizationID == 1 { body = fmt.Sprintf(bodyUpTplx, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, analysisVersion) } else { body = fmt.Sprintf(bodyUpTplx, cveNumber, cveRepo, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), @@ -1031,7 +1055,7 @@ func CreateIssueBody(accessToken, owner, path, assignee string, if cve.OrganizationID == 1 { body = fmt.Sprintf(bodyTplx, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion, analysisVersion) } else { body = fmt.Sprintf(bodyTplx, cveNumber, cveRepo, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), @@ -1047,7 +1071,7 @@ func CreateIssueBody(accessToken, owner, path, assignee string, if cve.OrganizationID == 1 { body = fmt.Sprintf(bodyTplx, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion, analysisVersion) } else { body = fmt.Sprintf(bodyTplx, cveNumber, cveRepo, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), @@ -1079,12 +1103,12 @@ func CreateIssueBody(accessToken, owner, path, assignee string, if its.Status == 3 && len(its.SecLink) > 3 && cve.OrganizationID == 1 { body = fmt.Sprintf(bodySecLinkTpl, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, its.SecLink) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, analysisVersion, its.SecLink) } else { if cve.OrganizationID == 1 { body = fmt.Sprintf(bodyUpTplx, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, oVector, affectedVersion, abiVersion, analysisVersion) } else { body = fmt.Sprintf(bodyUpTplx, cveNumber, cveRepo, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), @@ -1100,7 +1124,7 @@ func CreateIssueBody(accessToken, owner, path, assignee string, if cve.OrganizationID == 1 { body = fmt.Sprintf(bodyTplx, cveNumber, cvePkg, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), - genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion) + genPatchInfo(cve.CveNum), cveAnalysis, openEulerScore, affectedVersion, abiVersion, analysisVersion) } else { body = fmt.Sprintf(bodyTplx, cveNumber, cveRepo, cve.CveVersion, nvdType, nveScore, nveVector, cve.Description, cve.RepairTime, updateTime, cve.CveDetailUrl+"\n"+getCveDetail(cve.CveNum)+"\n", commentCmd, holeSource(cve.DataSource), diff --git a/cve-vulner-manager/taskhandler/createissue.go b/cve-vulner-manager/taskhandler/createissue.go index c6b89f4d07c421f1335c5e4e76960a1b8bd04177..53de98ab0265173a364454dbcc59b7f12ffb100f 100644 --- a/cve-vulner-manager/taskhandler/createissue.go +++ b/cve-vulner-manager/taskhandler/createissue.go @@ -374,6 +374,7 @@ func CreateIssueToGit(accessToken, owner, path, assignee string, brandStr := strings.Join(brandArrayTmp, ",") issueTemp.AffectedVersion = brandStr issueTemp.AbiVersion = brandStr + issueTemp.AnalysisVersion = brandStr issueTemp.SaAuditFlag = 0 } else { issueTemp.SaAuditFlag = 1 @@ -465,7 +466,7 @@ func CreateIssueToGit(accessToken, owner, path, assignee string, } // Store security bulletin related information var sec models.SecurityNotice - CreateSecNoticeData(&sec, cve, path, branchs, sc.NVDScore) + CreateSecNoticeData(&sec, cve, path, branchs, it.AnalysisVersion, sc.NVDScore) secID, noticeErr := models.UpdateSecNotice(&sec) if noticeErr != nil { logs.Error("CreateIssueToGit, Failed to update security information,"+ @@ -571,7 +572,7 @@ func UpdateIssueToGit(accessToken, owner, path string, } // Store security bulletin related information var sec models.SecurityNotice - CreateSecNoticeData(&sec, cve, path, its.AffectedVersion, its.OpenEulerScore) + CreateSecNoticeData(&sec, cve, path, its.AffectedVersion, its.AnalysisVersion, its.OpenEulerScore) secId, err := models.UpdateSecNotice(&sec) if err != nil { logs.Error("UpdateIssueToGit, Failed to update security information, "+ @@ -863,9 +864,44 @@ func AddAffectBrands(branchVersion string) string { return branchs } +func AddWillFixBrands(analysisVersion string) string { + branchs := "" + if analysisVersion != "" && len(analysisVersion) > 1 { + brandsGroup := strings.Split(analysisVersion, ",") + if len(brandsGroup) > 0 { + for _, brand := range brandsGroup { + if brand == "" || len(brand) < 2 { + continue + } + brand = common.BranchVersionRep(brand) + brandList := strings.Split(brand, ":") + if len(brandList) > 1 { + prams := strings.Replace(brandList[1], " ", "", -1) + if prams == "正常修复" { + branchs += brandList[0] + "/" + } + } else { + brandList := strings.Split(brand, ":") + if len(brandList) > 1 { + prams := strings.Replace(brandList[1], " ", "", -1) + if prams == "正常修复" { + branchs += brandList[0] + "/" + } + } + } + } + } + } + if branchs != "" && len(branchs) > 1 { + branchs = branchs[:len(branchs)-1] + } + return branchs +} + func CreateSecNoticeData(sec *models.SecurityNotice, iss models.VulnCenter, - path, branchVersion string, opScore float64) { + path, branchVersion, analysisVersion string, opScore float64) { branchs := AddAffectBrands(branchVersion) + willFixBrands := AddWillFixBrands(analysisVersion) sec.CveId = iss.CveId sec.CveNum = iss.CveNum opScoreLeve := models.OpenEulerScoreProc(opScore) @@ -895,6 +931,7 @@ func CreateSecNoticeData(sec *models.SecurityNotice, iss models.VulnCenter, " is available for each vulnerability from the CVElink(s) in the References section." } sec.AffectProduct = branchs + sec.WillFixProduct = willFixBrands } func CreateBrandAndTags(accessToken, owner, path string, organizationID int8) []string { diff --git a/cve-vulner-manager/taskhandler/cvrf.go b/cve-vulner-manager/taskhandler/cvrf.go index d15d5cbe9ad0ee9d4ddc215124cbb74cf58f36ec..862002450921274de3930293c9f2ecf2e17c2e5d 100644 --- a/cve-vulner-manager/taskhandler/cvrf.go +++ b/cve-vulner-manager/taskhandler/cvrf.go @@ -161,8 +161,8 @@ type CveNote struct { } type ProductStatuses struct { - XMLName xml.Name `xml:"ProductStatuses,omitempty"` - Status *Status `xml:"Status,omitempty"` + XMLName xml.Name `xml:"ProductStatuses,omitempty"` + Status []*Status `xml:"Status,omitempty"` } type Status struct { @@ -222,6 +222,7 @@ type UnRemediation struct { Description string `xml:"Description"` Date string `xml:"DATE"` ProductId string `xml:"ProductID"` + Reason string `xml:"Reason,omitempty"` } type CveInfo struct { @@ -292,6 +293,7 @@ type UnaffectVulnerability struct { Xmlns string `xml:"xmlns,attr"` CveNotes *CveNotes `xml:"Notes,omitempty"` Cve string `xml:"CVE"` + Threats *Threats `xml:"Threats"` ProductStatuses *ProductStatuses `xml:"ProductStatuses,omitempty"` CvssScoreSets *CVSSScoreSets `xml:"CVSSScoreSets,omitempty"` Remediations *UnRemediations `xml:"Remediations,omitempty"` @@ -432,30 +434,60 @@ func BuildUnaffectVulnerabilitySet(unaffectCvrfsa *UnaffectCvrfSa, v models.Exce func BuildUnaffVulnerabilitySlice(vulnerability []UnaffectVulnerability, v models.ExcelExport, affectBranch string, componentMap map[string]ComponentInfo) []UnaffectVulnerability { cpe := affectBranch + affectType := v.AffectType(cpe) if vulnerability != nil && len(vulnerability) > 0 { cveExist := false for i, vl := range vulnerability { if vl.Cve == v.CveNum && vl.Remediations != nil && len(vl.Remediations.Remediation) > 0 && vl.Remediations.Remediation[0].Description == v.InfluenceComponent { + statusTypeExist := false cpeExist := false - for _, pid := range vl.ProductStatuses.Status.ProductId { - if pid.ProductId == cpe { - cpeExist = true - break + + for si, status := range vl.ProductStatuses.Status { + if status.Type == affectType { + statusTypeExist = true + for _, pid := range status.ProductId { + if pid.ProductId == cpe { + cpeExist = true + break + } + } + + if !cpeExist { + var productId ProductId + productId.ProductId = cpe + vl.ProductStatuses.Status[si].ProductId = append(vl.ProductStatuses.Status[si].ProductId, productId) + var remediation UnRemediation + remediation.Type = status.Type + remediation.Description = v.InfluenceComponent + remediation.Date = common.GetCurDate() + remediation.ProductId = cpe + remediation.Reason = v.GetReasonByVersion(cpe) + vl.Remediations.Remediation = append(vl.Remediations.Remediation, remediation) + vulnerability[i] = vl + } } } - if !cpeExist { + + if !statusTypeExist { + var status Status + status.Type = affectType var productId ProductId productId.ProductId = cpe - vl.ProductStatuses.Status.ProductId = append(vl.ProductStatuses.Status.ProductId, productId) + productIdSlice := make([]ProductId, 0) + productIdSlice = append(productIdSlice, productId) + status.ProductId = productIdSlice + vl.ProductStatuses.Status = append(vl.ProductStatuses.Status, &status) var remediation UnRemediation - remediation.Type = "Unaffected" + remediation.Type = status.Type remediation.Description = v.InfluenceComponent remediation.Date = common.GetCurDate() remediation.ProductId = cpe + remediation.Reason = v.GetReasonByVersion(cpe) vl.Remediations.Remediation = append(vl.Remediations.Remediation, remediation) vulnerability[i] = vl } + cveExist = true break } @@ -478,6 +510,9 @@ func BuildUnaffVulnerabilitySlice(vulnerability []UnaffectVulnerability, v model func BuildUnaffVulnerability(vlLenth int, v models.ExcelExport, componentMap map[string]ComponentInfo, cpe string) []UnaffectVulnerability { vulnerabilitySlice := make([]UnaffectVulnerability, 0) + + affectType := v.AffectType(cpe) + var vulnerability UnaffectVulnerability vulnerability.Xmlns = "http://www.icasi.org/CVRF/schema/vuln/1.1" vulnerability.Ordinal = strconv.Itoa(vlLenth) @@ -492,15 +527,18 @@ func BuildUnaffVulnerability(vlLenth int, v models.ExcelExport, cveNotes.CveNote = &cveNote vulnerability.CveNotes = &cveNotes vulnerability.Cve = v.CveNum + + vulnerability.Threats = &Threats{Threat: &Threat{Type: "Impact", Description: v.CveLevel}} + var productStatuses ProductStatuses var status Status - status.Type = "Unaffected" + status.Type = affectType var productId ProductId productId.ProductId = cpe productIdSlice := make([]ProductId, 0) productIdSlice = append(productIdSlice, productId) status.ProductId = productIdSlice - productStatuses.Status = &status + productStatuses.Status = append(productStatuses.Status, &status) vulnerability.ProductStatuses = &productStatuses var cVSSScoreSets CVSSScoreSets var scoreSet ScoreSet @@ -511,10 +549,11 @@ func BuildUnaffVulnerability(vlLenth int, v models.ExcelExport, var remediations UnRemediations remediationSlice := make([]UnRemediation, 0) var remediation UnRemediation - remediation.Type = "Unaffected" + remediation.Type = affectType remediation.Description = v.InfluenceComponent remediation.Date = common.GetCurDate() remediation.ProductId = cpe + remediation.Reason = v.GetReasonByVersion(cpe) remediationSlice = append(remediationSlice, remediation) remediations.Remediation = remediationSlice vulnerability.Remediations = &remediations @@ -1113,7 +1152,7 @@ func BuildVulnerability(vlLenth int, v models.ExcelExport, productIdSlice := make([]ProductId, 0) productIdSlice = append(productIdSlice, productId) status.ProductId = productIdSlice - productStatuses.Status = &status + productStatuses.Status = append(productStatuses.Status, &status) vulnerability.ProductStatuses = &productStatuses var threats Threats var threat Threat @@ -1162,21 +1201,24 @@ func BuildVulnerabilitySlice(vulnerability []Vulnerability, v models.ExcelExport for _, vl := range vulnerability { if vl.Cve == v.CveNum && strings.Contains(vl.Remediations.Remediation.Description, v.InfluenceComponent) { cpeExist := false - for _, pid := range vl.ProductStatuses.Status.ProductId { - if pid.ProductId == cpe { - cpeExist = true - break + + for si, status := range vl.ProductStatuses.Status { + for _, pid := range status.ProductId { + if pid.ProductId == cpe { + cpeExist = true + break + } } - } - if !cpeExist { - var productId ProductId - productId.ProductId = cpe - if branchFlag == 1 { - vl.ProductStatuses.Status.ProductId = append(vl.ProductStatuses.Status.ProductId, productId) - } else { - productIdSlice := make([]ProductId, 0) - productIdSlice = append(productIdSlice, productId) - vl.ProductStatuses.Status.ProductId = productIdSlice + if !cpeExist { + var productId ProductId + productId.ProductId = cpe + if branchFlag == 1 { + vl.ProductStatuses.Status[si].ProductId = append(vl.ProductStatuses.Status[si].ProductId, productId) + } else { + productIdSlice := make([]ProductId, 0) + productIdSlice = append(productIdSlice, productId) + vl.ProductStatuses.Status[si].ProductId = productIdSlice + } } } cveExist = true diff --git a/cve-vulner-manager/taskhandler/excel.go b/cve-vulner-manager/taskhandler/excel.go index fc12ba7f82f17aa73f9b1b0e163fa218be7cd207..1c2d39976b8ff24b0f426a44b25b87dad2e69e28 100644 --- a/cve-vulner-manager/taskhandler/excel.go +++ b/cve-vulner-manager/taskhandler/excel.go @@ -34,7 +34,7 @@ const UNAFFECTFLAG = 2 var releaseDate map[string]int64 -//CveExcel Excel export client +// CveExcel Excel export client type CveExcel struct { ExcelName string //excel name ExcelHandel *excelize.File //excel File handle @@ -60,10 +60,10 @@ type IssueAndPkg struct { var fillLock sync.Mutex var wgTrigger sync.WaitGroup -//GenerateCveExcel Generate Excel documents based on data. -//param snPrefix means security notice prefix. -//param snSuffix means security notice suffix append start value. -//param forceRewrite means whether to force the document to be rewritten. +// GenerateCveExcel Generate Excel documents based on data. +// param snPrefix means security notice prefix. +// param snSuffix means security notice suffix append start value. +// param forceRewrite means whether to force the document to be rewritten. func GenerateCveExcel(excelName, snPrefix string, snSuffix int64, forceRewrite bool) (err error) { //Query the data to be exported. count := models.GetCanExportVulnCenterCount() @@ -89,7 +89,7 @@ func GenerateCveExcel(excelName, snPrefix string, snSuffix int64, forceRewrite b return ec.Save(mode) } -//GenerateCveExcelByTrigger Generate cve&security notice excel file by trigger +// GenerateCveExcelByTrigger Generate cve&security notice excel file by trigger func GenerateCveExcelByTrigger(affectBranch, excelName, snPrefix, startTime string, snSuffix int64, forceRewrite bool, pkgList []models.ExcelPackage, cvrfFileList map[string][]string, componentMap map[string]ComponentInfo, cvfrFileMap map[string]CvrfSa, cvexml *[]CveXml, @@ -119,7 +119,7 @@ func GenerateCveExcelByTrigger(affectBranch, excelName, snPrefix, startTime stri return ec.Save(mode) } -//Init init excel client +// Init init excel client func (ec *CveExcel) Init(excelName, snPrefix string, snSuffix int64) (err error) { if excelName == "" || !(strings.HasSuffix(excelName, ".xlsx") || strings.HasSuffix(excelName, "xls")) { err = errors.New("excel name illegal") @@ -139,9 +139,9 @@ func (ec *CveExcel) Init(excelName, snPrefix string, snSuffix int64) (err error) return nil } -//InitFileHandle Initialize the file handle. -//param forceRewrite is true it means will create a new file otherwise it means append or new. -//the return value wm is 0 for new creation, and 1 for append. +// InitFileHandle Initialize the file handle. +// param forceRewrite is true it means will create a new file otherwise it means append or new. +// the return value wm is 0 for new creation, and 1 for append. func (ec *CveExcel) InitFileHandle(forceRewrite bool) (wm int8) { if forceRewrite { ec.ExcelHandel = excelize.NewFile() @@ -167,7 +167,7 @@ func (ec *CveExcel) InitFileHandle(forceRewrite bool) (wm int8) { } -//InitSheet init excel sheet +// InitSheet init excel sheet func (ec *CveExcel) InitSheet() { ec.SecNoticeSheetIdx = ec.ExcelHandel.NewSheet(ec.SecNoticeSheetName) ec.InfProductSheetIndex = ec.ExcelHandel.NewSheet(ec.InfProductSheetName) @@ -176,7 +176,7 @@ func (ec *CveExcel) InitSheet() { ec.ExcelHandel.SetSheetName(sn, ec.CveSheetName) } -//FillHeader fill the excel sheet header +// FillHeader fill the excel sheet header func (ec *CveExcel) FillHeader() (err error) { err = ec.ExcelHandel.SetCellValue(ec.CveSheetName, "A1", "CVE编号") if err != nil { @@ -382,7 +382,7 @@ func (ec *CveExcel) FillHeader() (err error) { return nil } -//FillContent fill the excel content +// FillContent fill the excel content func (ec *CveExcel) FillContent(count int64) { pageSize := 50 pageCount := count / int64(pageSize) @@ -632,6 +632,10 @@ func affectBrachRep(xmlp *models.ExcelExport, affectBranch string) bool { func FindUnaffectBrach(xmlp *models.ExcelExport, affectBranch, accessToken, owner string) bool { branchArry, _ := GetBranchesInfo(accessToken, owner, xmlp.OwnedComponent, 1) + if xmlp.IsIssueWithAnalysisVersion() { + return xmlp.IsNotWillFixVersion(affectBranch) + } + affectBool := false if xmlp.AffectedVersion != "" && len(xmlp.AffectedVersion) > 1 { affSlice := strings.Split(xmlp.AffectedVersion, ",") @@ -1063,7 +1067,7 @@ func (ec *CveExcel) fillPackageSheet(row []interface{}) (err error) { return err } -//Save save the excel content to file +// Save save the excel content to file func (ec *CveExcel) Save(md int8) error { if md == 0 { return ec.ExcelHandel.SaveAs(ec.ExcelName) @@ -1072,7 +1076,7 @@ func (ec *CveExcel) Save(md int8) error { } -//ExtractPackageData extract the package data by csv file +// ExtractPackageData extract the package data by csv file func ExtractPackageData(lp string) (pkgList []models.ExcelPackage, err error) { pkgLock.Lock() defer pkgLock.Unlock() @@ -1233,7 +1237,7 @@ func UnaffectIssueProc(affectBranch string, cvrfFileList map[string][]string, } } -//filter existing data +// filter existing data func filterDataInSlice(data string, filterList []string) bool { for _, v := range filterList { if strings.EqualFold(data, v) { @@ -1245,6 +1249,10 @@ func filterDataInSlice(data string, filterList []string) bool { // if cve exist affected and label exist CVE/FIXED return true func filterFixBranch(data *models.ExcelExport, cve, branch string) (has bool) { + if data.IsIssueWithAnalysisVersion() { + return data.IsWillFixVersion(branch) + } + has = false if !strings.Contains(data.IssueLabel, "CVE/FIXED") { return diff --git a/cve-vulner-manager/util/parsepayload.go b/cve-vulner-manager/util/parsepayload.go index 02d27591dba2cb3af166f17a1467aeb9f88a6a05..68cf7d2c5a8b377ac3ea22f784752b937ac2be03 100644 --- a/cve-vulner-manager/util/parsepayload.go +++ b/cve-vulner-manager/util/parsepayload.go @@ -28,6 +28,8 @@ const ( //KwAbiVersion Keywords of the abi version KwAbiVersion = "修复是否涉及abi变化(是/否):" + KwAnalysisVersion = "原因说明:" + Openeuler = 1 OpenGauss = 2 MindSpore = 3 @@ -53,7 +55,7 @@ var ( IW = "IW" //CommentKeys Keyword parsed by the new version of comments CommentKeys = []string{KwAnalysisDesc, KwOpenEulerScore, KwEffectVersion} - EulerCommentKeys = []string{KwAnalysisDesc, KwOpenEulerScore, KwEffectVersion, KwAbiVersion} + EulerCommentKeys = []string{KwAnalysisDesc, KwOpenEulerScore, KwEffectVersion, KwAbiVersion, KwAnalysisVersion} ) var ( @@ -101,19 +103,30 @@ var ( RegexpDigital = regexp.MustCompile(`(\d){1,}(\.\d+)?`) RegexpSpecialDigital = regexp.MustCompile(`(cvssv3.[0-9]|cvssv2.[0-9]|CVSSV3.[0-9]|CVSSV2.[0-9]|CVSS[::]3.[0-9]|CVSS[::]2.[0-9]|cvss[::]3.[0-9]|cvss[::]2.[0-9]|3.[0-9]/|2.[0-9]/|3.[0-9] /|2.[0-9] /)*`) //^((CVSS:3.0|CVSS:2.0|3.0/|2.0/|3.0 /|2.0 /).)*$ //RegexpSpecialDigital = regexp.MustCompile(`(cvssv[1-9].[0-9]|CVSSV[1-9].[0-9]|CVSS[::][1-9].[0-9]|cvss[::][1-9].[0-9]|[1-9].[0-9]/|[1-9].[0-9] /)*`) //^((CVSS:3.0|CVSS:2.0|3.0/|2.0/|3.0 /|2.0 /).)*$ - RegexpVector = regexp.MustCompile(`AV:[NLAP](?s:(.*?))/A:[LNH]`) - RegexpVectorV2 = regexp.MustCompile(`AV:[LAN](?s:(.*))/Au:[MSN](?s:(.*))/A:[NPC]`) - RegexpScoreTypeV2 = regexp.MustCompile(`(?mi)^CVSS[vV]2.[0-9xX]\s*`) // CVSS V3.0分值: - RegexpScoreTypeV3 = regexp.MustCompile(`(?mi)^CVSS[vV]3.[0-9xX]\s*`) - RegexpIsNewTpl = regexp.MustCompile(`(?mi)^原理分析[::]\s*`) - RegexpIsNewTpl2 = regexp.MustCompile(`(?mi)^规避方案或消减措施[::]\s*`) - regexpEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]受影响`) - regexpNoEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响`) - regexpOtherEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]$`) - regexpEffectAbiVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]是`) - regexpNoEffectAbiVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]否`) - RegexpIsAbiTpl = regexp.MustCompile(`(?mi)^(修复)?是否涉及abi变化\(是/否\)[::]\s*`) - RegexpIsFixTpl = regexp.MustCompile(`(?mi)^三、漏洞修复\s*`) + RegexpVector = regexp.MustCompile(`AV:[NLAP](?s:(.*?))/A:[LNH]`) + RegexpVectorV2 = regexp.MustCompile(`AV:[LAN](?s:(.*))/Au:[MSN](?s:(.*))/A:[NPC]`) + RegexpScoreTypeV2 = regexp.MustCompile(`(?mi)^CVSS[vV]2.[0-9xX]\s*`) // CVSS V3.0分值: + RegexpScoreTypeV3 = regexp.MustCompile(`(?mi)^CVSS[vV]3.[0-9xX]\s*`) + RegexpIsNewTpl = regexp.MustCompile(`(?mi)^原理分析[::]\s*`) + RegexpIsNewTpl2 = regexp.MustCompile(`(?mi)^规避方案或消减措施[::]\s*`) + regexpEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]受影响`) + regexpNoEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响`) + regexpOtherEffectVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]$`) + regexpEffectAbiVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]是`) + regexpNoEffectAbiVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]否`) + regexpWillFixVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]正常修复`) + regexpStill = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]暂不修复-漏洞仍在分析中`) + regexpNoPatchVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]暂不修复-暂无解决方案或补丁`) + regexpUpgradeVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]暂不修复-待升级版本修复`) + regexpOutScopeVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不修复-超出修复范围`) + regexpNotFixVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不修复-特殊原因导致不再修复`) + regexpComponentNotPresentVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响-组件不存在`) + regexpMitigationsExistVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响-已有内置的内联控制或缓解措施`) + regexpCannotControlledVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响-漏洞代码不能被攻击者触发`) + regexpNotExecuteVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响-漏洞代码不在执行路径`) + regexpCodeNotPresentVersion = regexp.MustCompile(`(?mi)[\d]{1,}\.(.*?)[::]不受影响-漏洞代码不存在`) + RegexpIsAbiTpl = regexp.MustCompile(`(?mi)^(修复)?是否涉及abi变化\(是/否\)[::]\s*`) + RegexpIsFixTpl = regexp.MustCompile(`(?mi)^三、漏洞修复\s*`) //RegexpCveAbiVersionNew new tpl influences version regexp RegexpCveAbiVersionNew = regexp.MustCompile(`受影响版本排查\(受影响/不受影响\)[::](?s:(.*?))(修复)?是否涉及abi变化\(是/否\)[::]`) //RegexpCveAbiNew new tpl influences version regexp @@ -132,14 +145,33 @@ var ( mutex sync.Mutex ) -//CommentAnalysis issue comment analysis keyword and value container +var matchers = []RegexpMatcher{ + {Regexp: regexpWillFixVersion, Analysis: common.AnalysisWillFix}, + {Regexp: regexpStill, Analysis: common.AnalysisStill}, + {Regexp: regexpNoPatchVersion, Analysis: common.AnalysisNoPatch}, + {Regexp: regexpUpgradeVersion, Analysis: common.AnalysisUpgrade}, + {Regexp: regexpOutScopeVersion, Analysis: common.AnalysisOutScope}, + {Regexp: regexpNotFixVersion, Analysis: common.AnalysisNotFix}, + {Regexp: regexpComponentNotPresentVersion, Analysis: common.AnalysisComponentNotPresent}, + {Regexp: regexpMitigationsExistVersion, Analysis: common.AnalysisMitigationsExist}, + {Regexp: regexpCannotControlledVersion, Analysis: common.AnalysisCannotControlled}, + {Regexp: regexpNotExecuteVersion, Analysis: common.AnalysisNotExecute}, + {Regexp: regexpCodeNotPresentVersion, Analysis: common.AnalysisCodeNotPresent}, +} + +type RegexpMatcher struct { + Regexp *regexp.Regexp + Analysis string +} + +// CommentAnalysis issue comment analysis keyword and value container type CommentAnalysis struct { KeyName string KeyIdx int KeyValue string } -//CaSlice define the CommentAnalysis slice +// CaSlice define the CommentAnalysis slice type CaSlice []CommentAnalysis func (a CaSlice) Len() int { @@ -226,7 +258,7 @@ func init() { VectorMapV2["A"] = mAi } -//GenerateCommentAnalysis Generate analytical entities based on comments. +// GenerateCommentAnalysis Generate analytical entities based on comments. func GenerateCommentAnalysis(content string, organizationID int8) (ca CaSlice) { if content == "" { return @@ -264,7 +296,7 @@ func GenerateCommentAnalysis(content string, organizationID int8) (ca CaSlice) { return ca } -//ParseCommentContent extract comment content based on tags. +// ParseCommentContent extract comment content based on tags. func ParseCommentContent(content string, label string) (res string, ok bool) { ret := regexp.MustCompile(genCommentRegexpStr(label)) sm := ret.FindAllStringSubmatch(content, 1) @@ -275,13 +307,13 @@ func ParseCommentContent(content string, label string) (res string, ok bool) { return } -//ParseCommentVector extract vector from issue comment +// ParseCommentVector extract vector from issue comment func ParseCommentVector(content string) string { sm := RegexpVector.Find([]byte(content)) return string(sm) } -//ExtractVector extract vector from issue body +// ExtractVector extract vector from issue body func ExtractVector(body, scoreType string) string { if body == "" { return body @@ -301,7 +333,7 @@ func ExtractVector(body, scoreType string) string { return "" } -//ReadVMValue get vector v3 value from the vector map by keyword +// ReadVMValue get vector v3 value from the vector map by keyword func ReadVMValue(kStr string) (value string) { if kStr == "" { return "" @@ -322,7 +354,7 @@ func ReadVMValue(kStr string) (value string) { return } -//ReadVMValueV2 get vector v2 value from the vector map by keyword +// ReadVMValueV2 get vector v2 value from the vector map by keyword func ReadVMValueV2(kStr string) (value string) { if kStr == "" { return "" @@ -343,7 +375,7 @@ func ReadVMValueV2(kStr string) (value string) { return } -//VctToMap Convert vector string value to map +// VctToMap Convert vector string value to map func VctToMap(vct string) (vctMap map[string]string, ok bool) { if vct == "" { return nil, false @@ -367,7 +399,7 @@ func VctToMap(vct string) (vctMap map[string]string, ok bool) { } -//ParseCommentWithAllLabel extract comment value with custom label +// ParseCommentWithAllLabel extract comment value with custom label func ParseCommentWithAllLabel(content string) map[string]string { res := make(map[string]string, 0) s, ok := ParseCommentContent(content, IAD) @@ -405,7 +437,7 @@ func ParseCommentWithAllLabel(content string) map[string]string { return res } -//ExtractCommentAnalysisAllValue Extract all value by issue comment +// ExtractCommentAnalysisAllValue Extract all value by issue comment func ExtractCommentAnalysisAllValue(content string, organizationID int8) map[string]string { res := make(map[string]string, 0) ca := GenerateCommentAnalysis(content, organizationID) @@ -435,12 +467,18 @@ func ExtractCommentAnalysisAllValue(content string, organizationID int8) map[str value = ExtractCommentAbiVersion(value) res["abi_version"] = value } + + value, ext = ExtractCommentValue(ca, KwAnalysisVersion) + if ext { + value = ExtractCommentAnalysisVersion(value) + res["analysis_version"] = value + } } } return res } -//ExtractCommentEffectVersion Extract the affected version from the issue comment +// ExtractCommentEffectVersion Extract the affected version from the issue comment func ExtractCommentEffectVersion(str string) string { str = strings.Trim(str, " ") str = strings.ReplaceAll(str, " ", "") @@ -483,7 +521,7 @@ func ExtractCommentEffectVersion(str string) string { return "" } -//ExtractCommentAbiVersion Extract the abi version from the issue comment +// ExtractCommentAbiVersion Extract the abi version from the issue comment func ExtractCommentAbiVersion(str string) string { str = strings.Trim(str, " ") str = strings.ReplaceAll(str, " ", "") @@ -526,7 +564,50 @@ func ExtractCommentAbiVersion(str string) string { return "" } -//ExtractCommentValue Get the value of CaSlice by keyword +// ExtractCommentAnalysisVersion Extract the analysis reason version from the issue comment +func ExtractCommentAnalysisVersion(str string) string { + str = strings.Trim(str, " ") + str = strings.ReplaceAll(str, " ", "") + var res []string + + for _, matcher := range matchers { + matches := matcher.Regexp.FindAllStringSubmatch(str, -1) + if len(matches) > 0 { + matchRes := processMatches(matches, matcher.Analysis) + res = append(res, matchRes...) + } + } + + matchOther := regexpOtherEffectVersion.FindAllStringSubmatch(str, -1) + if len(matchOther) > 0 { + for _, v := range matchOther { + if len(v) > 1 { + tmpV := TrimString(v[1]) + ":" + tmpV = common.BranchVersionRep(tmpV) + res = append(res, tmpV) + } + } + } + if len(res) > 0 { + return strings.Join(res, ",") + } + return "" +} + +func processMatches(matches [][]string, analysisType string) []string { + var matchRes []string + for _, v := range matches { + if len(v) > 1 { + tmpV := TrimString(v[1]) + ":" + analysisType + tmpV = common.BranchVersionRep(tmpV) + matchRes = append(matchRes, tmpV) + } + } + + return matchRes +} + +// ExtractCommentValue Get the value of CaSlice by keyword func ExtractCommentValue(ca CaSlice, keyWord string) (string, bool) { for _, v := range ca { if v.KeyName == keyWord { @@ -536,7 +617,7 @@ func ExtractCommentValue(ca CaSlice, keyWord string) (string, bool) { return "", false } -//ExtractCommentOpenEulerScore Extract openEuler score from issue comment +// ExtractCommentOpenEulerScore Extract openEuler score from issue comment func ExtractCommentOpenEulerScore(str string) (score, vector string) { str = TrimString(str) score = ExtractDigital(str) @@ -556,7 +637,7 @@ func genCommentRegexpStr(label string) string { return fmt.Sprintf(`\[%s\](?s:(.*?))\[/%s\]`, label, label) } -//TrimString Remove the \n \r \t spaces in the string +// TrimString Remove the \n \r \t spaces in the string func TrimString(str string) string { str = strings.Replace(str, " ", "", -1) str = strings.Replace(str, "\n", "", -1) @@ -565,7 +646,7 @@ func TrimString(str string) string { return str } -//TrimStringNR Remove the \n \r in the string +// TrimStringNR Remove the \n \r in the string func TrimStringNR(str string) string { str = strings.Replace(str, "\n", "", -1) str = strings.Replace(str, "\r", "", -1) @@ -573,7 +654,7 @@ func TrimStringNR(str string) string { return str } -//ExtractDigital remove "cvss 3.0" or "cvss 2.0" +// ExtractDigital remove "cvss 3.0" or "cvss 2.0" func RemoveSpecialDigital(body string) string { if body == "" { return body @@ -586,7 +667,7 @@ func RemoveSpecialDigital(body string) string { return "" } -//ExtractDigital Get number in string +// ExtractDigital Get number in string func ExtractDigital(body string) string { if body == "" { return body @@ -605,7 +686,7 @@ func ExtractDigital(body string) string { return "" } -//GetCveNumber Extract cveNum from the issue body cveNumber link +// GetCveNumber Extract cveNum from the issue body cveNumber link func GetCveNumber(ov string) string { if v := regexpCveNumberLink.Find([]byte(ov)); len(v) > 0 { sv := string(v) @@ -616,7 +697,7 @@ func GetCveNumber(ov string) string { return ov } -//GetCveNumber Extract cve package from the issue body whe package is a link +// GetCveNumber Extract cve package from the issue body whe package is a link func GetCvePkg(str string) string { origin := TrimString(str)