From 09f0489464d032d3b6a171975308addb7cbf3c7a Mon Sep 17 00:00:00 2001 From: asafambar Date: Mon, 3 Jul 2023 17:06:23 +0300 Subject: [PATCH 1/6] Add support for multiple policy in on block event. --- utils/coreutils/tableutils.go | 3 +- xray/commands/curation/audit.go | 76 ++++++++++++++++++---------- xray/commands/curation/audit_test.go | 8 +-- 3 files changed, 56 insertions(+), 31 deletions(-) diff --git a/utils/coreutils/tableutils.go b/utils/coreutils/tableutils.go index 35bdec231..3c8e6898a 100644 --- a/utils/coreutils/tableutils.go +++ b/utils/coreutils/tableutils.go @@ -140,6 +140,7 @@ func PrepareTable(rows interface{}, emptyTableMessage string, printExtended bool columnName, columnNameExist := field.Tag.Lookup("col-name") embedTable, embedTableExist := field.Tag.Lookup("embed-table") extended, extendedExist := field.Tag.Lookup("extended") + _, autoMergeExist := field.Tag.Lookup("auto-merge") _, omitEmptyColumn := field.Tag.Lookup("omitempty") if !printExtended && extendedExist && extended == "true" { continue @@ -161,7 +162,7 @@ func PrepareTable(rows interface{}, emptyTableMessage string, printExtended bool } else { columnsNames = append(columnsNames, columnName) fieldsProperties = append(fieldsProperties, fieldProperties{index: i}) - columnConfigs = append(columnConfigs, table.ColumnConfig{Name: columnName}) + columnConfigs = append(columnConfigs, table.ColumnConfig{Name: columnName, AutoMerge: autoMergeExist}) } } tableWriter.AppendHeader(columnsNames) diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index 970df2076..7c6bdc28c 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -74,25 +74,32 @@ type PackageStatus struct { } type Policy struct { - Policy string `json:"policy"` - Condition string `json:"condition"` + Policy string `json:"policy"` + Condition string `json:"condition"` + Explanation string `json:"explanation"` + Recommendation string `json:"recommendation"` } type PackageStatusTable struct { - Status string `col-name:"Action"` - ParentName string `col-name:"Direct Dependency\nPackage Name"` - ParentVersion string `col-name:"Direct Dependency\nPackage Version"` - BlockedPackageUrl string `col-name:"Blocked Package URL"` - PackageName string `col-name:"Blocked Package\nName"` - PackageVersion string `col-name:"Blocked Package\nVersion"` - BlockingReason string `col-name:"Blocking Reason"` - PkgType string `col-name:"Package Type"` - Policy []policyTable `embed-table:"true"` + ParentName string `col-name:"Direct\nDependency\nPackage\nName" auto-merge:"true"` + ParentVersion string `col-name:"Direct\nDependency\nPackage\nVersion" auto-merge:"true"` + // BlockedPackageUrl string `col-name:"Blocked\nPackage \nURL"` + PackageName string `col-name:"Blocked\nPackage\nName" auto-merge:"true"` + PackageVersion string `col-name:"Blocked\nPackage\nVersion" auto-merge:"true"` + BlockingReason string `col-name:"Blocking Reason" auto-merge:"true"` + PkgType string `col-name:"Package\nType" auto-merge:"true"` + Policy string `col-name:"Violated\nPolicy\nName"` + Condition string `col-name:"Violated Condition\nName"` + Explanation string `col-name:"Explanation Name"` + Recommendation string `col-name:"Recommendation Name"` + //policyTable } type policyTable struct { - Policy string `col-name:"Violated Policy\nName"` - Condition string `col-name:"Violated Condition\nName"` + Policy string `col-name:"Violated\nPolicy\nName"` + Condition string `col-name:"Violated\nCondition\nName"` + Explanation string `col-name:"Explanation\nName"` + Recommendation string `col-name:"Recommendation\nName"` } type treeAnalyzer struct { @@ -279,23 +286,30 @@ func printResult(format utils.OutputFormat, projectPath string, packagesStatus [ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatusTable { var pkgStatusTable []PackageStatusTable - for _, pkgStatus := range packagesStatus { + for index, pkgStatus := range packagesStatus { + // The "go-pretty" library doesn't have an option to merge lines by group of uniq fields. + // we make an uniq of group by adding space to a group of lines we want to merge. + uniqLineSep := "" + if index%2 == 0 { + uniqLineSep = " " + } pkgTable := PackageStatusTable{ - Status: pkgStatus.Action, - ParentName: pkgStatus.ParentName, - ParentVersion: pkgStatus.ParentVersion, - BlockedPackageUrl: pkgStatus.BlockedPackageUrl, - PackageName: pkgStatus.PackageName, - PackageVersion: pkgStatus.PackageVersion, - BlockingReason: pkgStatus.BlockingReason, - PkgType: pkgStatus.PkgType, + ParentName: pkgStatus.ParentName + uniqLineSep, + ParentVersion: pkgStatus.ParentVersion + uniqLineSep, + PackageName: pkgStatus.PackageName + uniqLineSep, + PackageVersion: pkgStatus.PackageVersion + uniqLineSep, + BlockingReason: pkgStatus.BlockingReason + uniqLineSep, + PkgType: pkgStatus.PkgType + uniqLineSep, } - var policiesCondTable []policyTable for _, policyCond := range pkgStatus.Policy { - policiesCondTable = append(policiesCondTable, policyTable(policyCond)) + pkgTable.Policy = policyCond.Policy + pkgTable.Explanation = policyCond.Explanation + pkgTable.Recommendation = policyCond.Recommendation + pkgTable.Condition = policyCond.Condition + pkgStatusTable = append(pkgStatusTable, pkgTable) } - pkgTable.Policy = policiesCondTable - pkgStatusTable = append(pkgStatusTable, pkgTable) + //pkgTable.Policy = policiesCondTable + } return pkgStatusTable } @@ -450,7 +464,7 @@ func (nc *treeAnalyzer) getBlockedPackageDetails(packageUrl string, name string, } // Return policies and conditions names from the FORBIDDEN HTTP error message. -// Message structure: Package %s:%s download was blocked by JFrog Packages Curation service due to the following policies violated {%s, %s},{%s, %s}. +// Message structure: Package %s:%s download was blocked by JFrog Packages Curation service due to the following policies violated {%s, %s, %s, %s},{%s, %s, %s, %s}. func (nc *treeAnalyzer) extractPoliciesFromMsg(respError *ErrorsResp) []Policy { var policies []Policy msg := respError.Errors[0].Message @@ -463,6 +477,14 @@ func (nc *treeAnalyzer) extractPoliciesFromMsg(respError *ErrorsResp) []Policy { cond := polCond[1] policies = append(policies, Policy{Policy: strings.TrimSpace(pol), Condition: strings.TrimSpace(cond)}) } + if len(polCond) == 4 { + pol := polCond[0] + cond := polCond[1] + exp := strings.Replace(strings.Replace(polCond[2], ": ", ":\n", 1), " | ", "\n", -1) + rec := strings.Replace(strings.Replace(polCond[3], ": ", ":\n", 1), " | ", "\n", -1) + policies = append(policies, Policy{Policy: strings.TrimSpace(pol), + Condition: strings.TrimSpace(cond), Explanation: strings.TrimSpace(exp), Recommendation: strings.TrimSpace(rec)}) + } } return policies } diff --git a/xray/commands/curation/audit_test.go b/xray/commands/curation/audit_test.go index f4323d2ff..1dc96f38a 100644 --- a/xray/commands/curation/audit_test.go +++ b/xray/commands/curation/audit_test.go @@ -67,14 +67,16 @@ func getTestCasesForExtractPoliciesFromMsg() []struct { Errors: []ErrorResp{ { Status: 403, - Message: "Package test:1.0.0 download was blocked by JFrog Packages Curation service due to the following policies violated {policy1, condition1}.", + Message: "Package test:1.0.0 download was blocked by JFrog Packages Curation service due to the following policies violated {policy1, condition1, Package is 3339 days old, Upgrade to version 0.2.4 (latest)}.", }, }, }, expect: []Policy{ { - Policy: "policy1", - Condition: "condition1", + Policy: "policy1", + Condition: "condition1", + Explanation: "Package is 3339 days old", + Recommendation: "Upgrade to version 0.2.4 (latest)", }, }, }, From b4043501d57399ce3d12979a9a50f722ec91b260 Mon Sep 17 00:00:00 2001 From: asafambar Date: Tue, 4 Jul 2023 15:53:19 +0300 Subject: [PATCH 2/6] Fix sca issues. --- xray/commands/curation/audit.go | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index 7c6bdc28c..4b3ee38db 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -81,9 +81,8 @@ type Policy struct { } type PackageStatusTable struct { - ParentName string `col-name:"Direct\nDependency\nPackage\nName" auto-merge:"true"` - ParentVersion string `col-name:"Direct\nDependency\nPackage\nVersion" auto-merge:"true"` - // BlockedPackageUrl string `col-name:"Blocked\nPackage \nURL"` + ParentName string `col-name:"Direct\nDependency\nPackage\nName" auto-merge:"true"` + ParentVersion string `col-name:"Direct\nDependency\nPackage\nVersion" auto-merge:"true"` PackageName string `col-name:"Blocked\nPackage\nName" auto-merge:"true"` PackageVersion string `col-name:"Blocked\nPackage\nVersion" auto-merge:"true"` BlockingReason string `col-name:"Blocking Reason" auto-merge:"true"` @@ -92,14 +91,6 @@ type PackageStatusTable struct { Condition string `col-name:"Violated Condition\nName"` Explanation string `col-name:"Explanation Name"` Recommendation string `col-name:"Recommendation Name"` - //policyTable -} - -type policyTable struct { - Policy string `col-name:"Violated\nPolicy\nName"` - Condition string `col-name:"Violated\nCondition\nName"` - Explanation string `col-name:"Explanation\nName"` - Recommendation string `col-name:"Recommendation\nName"` } type treeAnalyzer struct { @@ -288,7 +279,7 @@ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatu var pkgStatusTable []PackageStatusTable for index, pkgStatus := range packagesStatus { // The "go-pretty" library doesn't have an option to merge lines by group of uniq fields. - // we make an uniq of group by adding space to a group of lines we want to merge. + // we make an uniq group by adding space to a group of lines we want to merge. uniqLineSep := "" if index%2 == 0 { uniqLineSep = " " @@ -301,6 +292,10 @@ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatu BlockingReason: pkgStatus.BlockingReason + uniqLineSep, PkgType: pkgStatus.PkgType + uniqLineSep, } + if len(pkgStatus.Policy) == 0 { + pkgStatusTable = append(pkgStatusTable, pkgTable) + continue + } for _, policyCond := range pkgStatus.Policy { pkgTable.Policy = policyCond.Policy pkgTable.Explanation = policyCond.Explanation @@ -308,9 +303,8 @@ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatu pkgTable.Condition = policyCond.Condition pkgStatusTable = append(pkgStatusTable, pkgTable) } - //pkgTable.Policy = policiesCondTable - } + return pkgStatusTable } @@ -480,8 +474,8 @@ func (nc *treeAnalyzer) extractPoliciesFromMsg(respError *ErrorsResp) []Policy { if len(polCond) == 4 { pol := polCond[0] cond := polCond[1] - exp := strings.Replace(strings.Replace(polCond[2], ": ", ":\n", 1), " | ", "\n", -1) - rec := strings.Replace(strings.Replace(polCond[3], ": ", ":\n", 1), " | ", "\n", -1) + exp := strings.ReplaceAll(strings.Replace(polCond[2], ": ", ":\n", 1), " | ", "\n") + rec := strings.ReplaceAll(strings.Replace(polCond[3], ": ", ":\n", 1), " | ", "\n") policies = append(policies, Policy{Policy: strings.TrimSpace(pol), Condition: strings.TrimSpace(cond), Explanation: strings.TrimSpace(exp), Recommendation: strings.TrimSpace(rec)}) } From 4dcff18ded9a91628f7e2f60d9707dab208643ab Mon Sep 17 00:00:00 2001 From: asafambar Date: Wed, 5 Jul 2023 15:12:02 +0300 Subject: [PATCH 3/6] Fix comments, add new tag examples with descriptions. --- utils/coreutils/tableutils.go | 32 ++++++++++++++++++++++++++++++++ xray/commands/curation/audit.go | 4 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/utils/coreutils/tableutils.go b/utils/coreutils/tableutils.go index 3c8e6898a..d1e4b0048 100644 --- a/utils/coreutils/tableutils.go +++ b/utils/coreutils/tableutils.go @@ -28,6 +28,7 @@ var DefaultMaxColWidth = 25 // In case the struct you want to print contains a field that is a slice of other structs, // you can print it in the table too with the 'embed-table' tag which can be set on slices of structs only. // Fields with the 'extended' tag will be printed iff the 'printExtended' bool input is true. +// You can merge cells horizontally with the 'auto-merge' tag, it will merge cells with the same value. // // Example: // These are the structs Customer and Product: @@ -91,6 +92,37 @@ var DefaultMaxColWidth = 25 // ┌─────────────────────────┐ // │ No customers were found │ // └─────────────────────────┘ +// +// Example(auto-merge): +// These are the structs Customer: +// +// type Customer struct { +// name string `col-name:"Name" auto-merge:"true"` +// age string `col-name:"Age" auto-merge:"true"` +// title string `col-name:"Product Title" auto-merge:"true"` +// CatNumber string `col-name:"Product\nCatalog #" auto-merge:"true"` +// Color string `col-name:"Color" extended:"true" auto-merge:"true"` +// } +// customersSlice := []Customer{ +// {name: "Gai", age: "350", title: "SpiderFrog Shirt - Medium", CatNumber: "123456", Color: "Green"}, +// {name: "Gai", age: "350", title: "Floral Bottle", CatNumber: "147585", Color: "Blue"}, +// {name: "Noah", age: "21", title: "Pouch", CatNumber: "456789", Color: "Red"}, +// } +// +// Customers +// ┌──────┬─────┬───────────────────────────┬───────────┐ +// │ NAME │ AGE │ PRODUCT TITLE │ PRODUCT │ +// │ │ │ │ CATALOG # │ +// ├──────┼─────┼───────────────────────────┼───────────┤ +// │ Gai │ 350 │ SpiderFrog Shirt - Medium │ 123456 │ +// │ │ ├───────────────────────────┼───────────┤ +// │ │ │ Floral Bottle │ 147585 │ +// ├──────┼─────┼───────────────────────────┼───────────┤ +// │ Noah │ 21 │ Pouch │ 456789 │ +// └──────┴─────┴───────────────────────────┴───────────┘ +// +// + func PrintTable(rows interface{}, title string, emptyTableMessage string, printExtended bool) (err error) { tableWriter, err := PrepareTable(rows, emptyTableMessage, printExtended) if err != nil || tableWriter == nil { diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index 4b3ee38db..29ec0822e 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -278,8 +278,8 @@ func printResult(format utils.OutputFormat, projectPath string, packagesStatus [ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatusTable { var pkgStatusTable []PackageStatusTable for index, pkgStatus := range packagesStatus { - // The "go-pretty" library doesn't have an option to merge lines by group of uniq fields. - // we make an uniq group by adding space to a group of lines we want to merge. + // We use auto-merge supported by "go-pretty" library. It doesn't have an option to merge lines by group of uniq fields. + // we make each group to be merged only with itself by adding/not adding space, this way it won't be merged with the next group. uniqLineSep := "" if index%2 == 0 { uniqLineSep = " " From 17aa42685b688064add3ecfa2093ab2661d49efd Mon Sep 17 00:00:00 2001 From: asafambar Date: Sun, 9 Jul 2023 11:49:01 +0300 Subject: [PATCH 4/6] Fix CR. --- utils/coreutils/tableutils.go | 5 +++-- xray/commands/curation/audit.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/coreutils/tableutils.go b/utils/coreutils/tableutils.go index d1e4b0048..a7257f15a 100644 --- a/utils/coreutils/tableutils.go +++ b/utils/coreutils/tableutils.go @@ -103,6 +103,7 @@ var DefaultMaxColWidth = 25 // CatNumber string `col-name:"Product\nCatalog #" auto-merge:"true"` // Color string `col-name:"Color" extended:"true" auto-merge:"true"` // } +// // customersSlice := []Customer{ // {name: "Gai", age: "350", title: "SpiderFrog Shirt - Medium", CatNumber: "123456", Color: "Green"}, // {name: "Gai", age: "350", title: "Floral Bottle", CatNumber: "147585", Color: "Blue"}, @@ -172,7 +173,7 @@ func PrepareTable(rows interface{}, emptyTableMessage string, printExtended bool columnName, columnNameExist := field.Tag.Lookup("col-name") embedTable, embedTableExist := field.Tag.Lookup("embed-table") extended, extendedExist := field.Tag.Lookup("extended") - _, autoMergeExist := field.Tag.Lookup("auto-merge") + _, autoMerge := field.Tag.Lookup("auto-merge") _, omitEmptyColumn := field.Tag.Lookup("omitempty") if !printExtended && extendedExist && extended == "true" { continue @@ -194,7 +195,7 @@ func PrepareTable(rows interface{}, emptyTableMessage string, printExtended bool } else { columnsNames = append(columnsNames, columnName) fieldsProperties = append(fieldsProperties, fieldProperties{index: i}) - columnConfigs = append(columnConfigs, table.ColumnConfig{Name: columnName, AutoMerge: autoMergeExist}) + columnConfigs = append(columnConfigs, table.ColumnConfig{Name: columnName, AutoMerge: autoMerge}) } } tableWriter.AppendHeader(columnsNames) diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index 29ec0822e..d6118799a 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -278,8 +278,8 @@ func printResult(format utils.OutputFormat, projectPath string, packagesStatus [ func convertToPackageStatusTable(packagesStatus []*PackageStatus) []PackageStatusTable { var pkgStatusTable []PackageStatusTable for index, pkgStatus := range packagesStatus { - // We use auto-merge supported by "go-pretty" library. It doesn't have an option to merge lines by group of uniq fields. - // we make each group to be merged only with itself by adding/not adding space, this way it won't be merged with the next group. + // We use auto-merge supported by the 'go-pretty' library. It doesn't have an option to merge lines by a group of unique fields. + // In order to so, we make each group merge only with itself by adding or not adding space. This way, it won't be merged with the next group. uniqLineSep := "" if index%2 == 0 { uniqLineSep = " " From a9d29820fab777224ca9c10bedc4dc96641fa9cd Mon Sep 17 00:00:00 2001 From: asafambar Date: Mon, 10 Jul 2023 20:17:45 +0300 Subject: [PATCH 5/6] Fix CR. --- utils/coreutils/tableutils.go | 2 -- xray/commands/curation/audit.go | 24 +++++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/utils/coreutils/tableutils.go b/utils/coreutils/tableutils.go index a7257f15a..52d3a616c 100644 --- a/utils/coreutils/tableutils.go +++ b/utils/coreutils/tableutils.go @@ -121,8 +121,6 @@ var DefaultMaxColWidth = 25 // ├──────┼─────┼───────────────────────────┼───────────┤ // │ Noah │ 21 │ Pouch │ 456789 │ // └──────┴─────┴───────────────────────────┴───────────┘ -// -// func PrintTable(rows interface{}, title string, emptyTableMessage string, printExtended bool) (err error) { tableWriter, err := PrepareTable(rows, emptyTableMessage, printExtended) diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index d6118799a..e3bbb06ee 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -466,23 +466,29 @@ func (nc *treeAnalyzer) extractPoliciesFromMsg(respError *ErrorsResp) []Policy { for _, match := range allMatches { match = strings.TrimSuffix(strings.TrimPrefix(match, "{"), "}") polCond := strings.Split(match, ",") - if len(polCond) == 2 { + if len(polCond) >= 2 { pol := polCond[0] cond := polCond[1] + + if len(polCond) == 4 { + exp, rec := makeLegiblePolicyDetails(polCond[2], polCond[3]) + policies = append(policies, Policy{Policy: strings.TrimSpace(pol), + Condition: strings.TrimSpace(cond), Explanation: strings.TrimSpace(exp), Recommendation: strings.TrimSpace(rec)}) + continue + } policies = append(policies, Policy{Policy: strings.TrimSpace(pol), Condition: strings.TrimSpace(cond)}) } - if len(polCond) == 4 { - pol := polCond[0] - cond := polCond[1] - exp := strings.ReplaceAll(strings.Replace(polCond[2], ": ", ":\n", 1), " | ", "\n") - rec := strings.ReplaceAll(strings.Replace(polCond[3], ": ", ":\n", 1), " | ", "\n") - policies = append(policies, Policy{Policy: strings.TrimSpace(pol), - Condition: strings.TrimSpace(cond), Explanation: strings.TrimSpace(exp), Recommendation: strings.TrimSpace(rec)}) - } } return policies } +// Adding a new line after the headline and replace every "|" with a new line. +func makeLegiblePolicyDetails(explanation, recommendation string) (string, string) { + explanation = strings.ReplaceAll(strings.Replace(explanation, ": ", ":\n", 1), " | ", "\n") + recommendation = strings.ReplaceAll(strings.Replace(recommendation, ": ", ":\n", 1), " | ", "\n") + return explanation, recommendation +} + func getUrlNameAndVersionByTech(tech coreutils.Technology, nodeId, artiUrl, repo string) (downloadUrl string, name string, version string) { if tech == coreutils.Npm { return getNameScopeAndVersion(nodeId, artiUrl, repo, coreutils.Npm.ToString()) From a608c5c5b10bd80d517ecddaf500b2b8138c9b81 Mon Sep 17 00:00:00 2001 From: asafambar Date: Tue, 11 Jul 2023 11:04:54 +0300 Subject: [PATCH 6/6] Fix CR. --- xray/commands/curation/audit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xray/commands/curation/audit.go b/xray/commands/curation/audit.go index e3bbb06ee..6703f7e1c 100644 --- a/xray/commands/curation/audit.go +++ b/xray/commands/curation/audit.go @@ -64,13 +64,13 @@ type PackageStatus struct { Action string `json:"action"` ParentName string `json:"direct_dependency_package_name"` ParentVersion string `json:"direct_dependency_package_version"` - BlockedPackageUrl string `json:"blocked_package_url"` + BlockedPackageUrl string `json:"blocked_package_url,omitempty"` PackageName string `json:"blocked_package_name"` PackageVersion string `json:"blocked_package_version"` BlockingReason string `json:"blocking_reason"` DepRelation string `json:"dependency_relation"` PkgType string `json:"type"` - Policy []Policy `json:"policies"` + Policy []Policy `json:"policies,omitempty"` } type Policy struct {