Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple policy in on block event #857

Merged
merged 7 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion utils/coreutils/tableutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -91,6 +92,36 @@ var DefaultMaxColWidth = 25
// ┌─────────────────────────┐
// │ No customers were found │
// └─────────────────────────┘
//
// Example(auto-merge):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Example(auto-merge): --> // 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{
asafambar marked this conversation as resolved.
Show resolved Hide resolved
// {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 {
Expand Down Expand Up @@ -140,6 +171,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")
_, autoMerge := field.Tag.Lookup("auto-merge")
_, omitEmptyColumn := field.Tag.Lookup("omitempty")
if !printExtended && extendedExist && extended == "true" {
continue
Expand All @@ -161,7 +193,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: autoMerge})
}
}
tableWriter.AppendHeader(columnsNames)
Expand Down
88 changes: 55 additions & 33 deletions xray/commands/curation/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,35 +64,33 @@ 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 {
asafambar marked this conversation as resolved.
Show resolved Hide resolved
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"`
}

type policyTable struct {
Policy string `col-name:"Violated Policy\nName"`
Condition string `col-name:"Violated Condition\nName"`
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"`
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"`
}

type treeAnalyzer struct {
Expand Down Expand Up @@ -279,24 +277,34 @@ 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 {
// 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 = " "
}
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,
}
if len(pkgStatus.Policy) == 0 {
pkgStatusTable = append(pkgStatusTable, pkgTable)
continue
}
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)
}

return pkgStatusTable
}

Expand Down Expand Up @@ -450,23 +458,37 @@ 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
allMatches := nc.extractPoliciesRegex.FindAllString(msg, -1)
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)})
}
}
return policies
}

// Adding a new line after the headline and replace every "|" with a new line.
func makeLegiblePolicyDetails(explanation, recommendation string) (string, string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's a unit test for this frunction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have unit test for the function which using it, anyway, I can add dedicated one

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())
Expand Down
8 changes: 5 additions & 3 deletions xray/commands/curation/audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
},
},
},
Expand Down
Loading