detectExecuteScan: count violations only for license policy rules (#5778)

This commit is contained in:
piotrskindel-sap
2026-05-20 16:07:20 +02:00
committed by GitHub
parent 73fdec38a3
commit a70e5f8d56
3 changed files with 198 additions and 37 deletions
+33 -7
View File
@@ -1004,7 +1004,10 @@ func writeIpPolicyJson(config detectExecuteScanOptions, utils detectUtils, paths
return fmt.Errorf("failed to get License Policy Violations: %w", err), 0
}
violationCount := getActivePolicyViolations(components)
violationCount, err := countComponentsActiveLicensePolicyViolations(sys, components)
if err != nil {
return fmt.Errorf("failed to get License Policy Violations: %w", err), 0
}
violations := struct {
PolicyViolations int `json:"policyViolations"`
Reports []string `json:"reports"`
@@ -1034,21 +1037,44 @@ func writeIpPolicyJson(config detectExecuteScanOptions, utils detectUtils, paths
return nil, violationCount
}
func getActivePolicyViolations(components *bd.Components) int {
func countComponentsActiveLicensePolicyViolations(sys *blackduckSystem, components *bd.Components) (int, error) {
if components.TotalCount == 0 {
return 0
return 0, nil
}
activeViolations := 0
for _, component := range components.Items {
if isActivePolicyViolation(component.PolicyStatus) {
hasActiveViolations, err := hasActiveLicensePolicyViolations(sys, component)
if err != nil {
return 0, err
}
if hasActiveViolations {
activeViolations++
}
}
return activeViolations
return activeViolations, nil
}
func isActivePolicyViolation(status string) bool {
return status == "IN_VIOLATION"
func hasActiveLicensePolicyViolations(sys *blackduckSystem, component bd.Component) (bool, error) {
if component.PolicyStatus != "IN_VIOLATION" {
return false, nil
}
policyRules, err := sys.Client.GetComponentPolicyRules(component)
if err != nil {
return false, err
}
for _, policyRule := range policyRules {
policyRuleDetails, err := sys.Client.GetPolicyRuleDetails(policyRule)
if err != nil {
return false, err
}
if policyRuleDetails.Category == "LICENSE" && policyRule.PolicyApprovalStatus == "IN_VIOLATION" {
return true, nil
}
}
return false, nil
}
// create toolrecord file for detectExecute
+98 -19
View File
@@ -99,6 +99,10 @@ func newBlackduckMockSystem(config detectExecuteScanOptions) blackduckSystem {
"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/policy-status": policyStatusContent,
"https://my.blackduck.system/api/projects?q=name%3ARapid_scan_on_PRs": projectContentRapidScan,
"https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions?limit=100&offset=0": projectVersionContentRapid,
"https://my.blackduck.system/api/projects/33716152-0d8a-42b4-a08e-433043f1069f/versions/519053ed-e753-4962-b396-f6e263337ddc/components/2e03fc5f-55b8-43b8-80cd-b3f2cee4bd41/versions/f19941ef-4421-4f08-9675-a905ed076e6e/policy-rules": componentPolicyRules,
"https://my.blackduck.system/api/policy-rules/f3e0eb8f-3af2-4656-827e-97769211e19a": policyRuleLicense,
"https://my.blackduck.system/api/policy-rules/112ba222-3bc0-4341-89c3-1b1053760722": policyRuleOperational,
"https://my.blackduck.system/api/policy-rules/212ba222-3bc0-4341-89c3-1b1053760722": policyRuleLicense,
},
header: map[string]http.Header{},
}
@@ -161,15 +165,41 @@ const (
{
"componentName": "Spring Framework",
"componentVersionName": "5.3.9",
"policyStatus": "IN_VIOLATION"
}, {
"policyStatus": "IN_VIOLATION",
"_meta": {
"links": [
{
"rel": "policy-rules",
"href": "https://my.blackduck.system/api/projects/33716152-0d8a-42b4-a08e-433043f1069f/versions/519053ed-e753-4962-b396-f6e263337ddc/components/2e03fc5f-55b8-43b8-80cd-b3f2cee4bd41/versions/f19941ef-4421-4f08-9675-a905ed076e6e/policy-rules"
}
]
}
},
{
"componentName": "Apache Tomcat",
"componentVersionName": "9.0.52",
"policyStatus": "IN_VIOLATION"
}, {
"policyStatus": "IN_VIOLATION",
"_meta": {
"links": [
{
"rel": "policy-rules",
"href": "https://my.blackduck.system/api/projects/33716152-0d8a-42b4-a08e-433043f1069f/versions/519053ed-e753-4962-b396-f6e263337ddc/components/2e03fc5f-55b8-43b8-80cd-b3f2cee4bd41/versions/f19941ef-4421-4f08-9675-a905ed076e6e/policy-rules"
}
]
}
},
{
"componentName": "Apache Log4j",
"componentVersionName": "4.5.16",
"policyStatus": "UNKNOWN"
"policyStatus": "UNKNOWN",
"_meta": {
"links": [
{
"rel": "policy-rules",
"href": "https://my.blackduck.system/api/projects/33716152-0d8a-42b4-a08e-433043f1069f/versions/519053ed-e753-4962-b396-f6e263337ddc/components/3e03fc5f-55b8-43b8-80cd-b3f2cee4bd41/versions/f19941ef-4421-4f08-9675-a905ed076e6e/policy-rules"
}
]
}
}
]
}`
@@ -261,6 +291,61 @@ const (
}
]
}`
componentPolicyRules = `{
"totalCount": 2,
"items": [
{
"name": "license policy rule overridden",
"enabled": true,
"policyApprovalStatus": "IN_VIOLATION_OVERRIDDEN",
"_meta": {
"allow": [
"DELETE",
"GET",
"PUT"
],
"href": "https://my.blackduck.system/api/policy-rules/f3e0eb8f-3af2-4656-827e-97769211e19a",
"links": []
}
},
{
"name": "operational policy rule",
"enabled": true,
"policyApprovalStatus": "IN_VIOLATION",
"_meta": {
"allow": [
"DELETE",
"GET",
"PUT"
],
"href": "https://my.blackduck.system/api/policy-rules/112ba222-3bc0-4341-89c3-1b1053760722",
"links": []
}
},
{
"name": "license policy rule violated",
"enabled": true,
"policyApprovalStatus": "IN_VIOLATION",
"_meta": {
"allow": [
"DELETE",
"GET",
"PUT"
],
"href": "https://my.blackduck.system/api/policy-rules/212ba222-3bc0-4341-89c3-1b1053760722",
"links": []
}
}
]
}`
policyRuleLicense = `{
"name": "license policy rule",
"category": "LICENSE"
}`
policyRuleOperational = `{
"name": "operational policy rule",
"category": "OPERATIONAL"
}`
)
func (c *detectTestUtilsBundle) RunExecutable(string, ...string) error {
@@ -885,26 +970,20 @@ func TestIsActiveVulnerability(t *testing.T) {
})
}
func TestIsActivePolicyViolation(t *testing.T) {
func TestCountComponentsActiveLicensePolicyViolations(t *testing.T) {
t.Parallel()
t.Run("Case true", func(t *testing.T) {
assert.True(t, isActivePolicyViolation("IN_VIOLATION"))
})
t.Run("Case False", func(t *testing.T) {
assert.False(t, isActivePolicyViolation("NOT_IN_VIOLATION"))
})
}
func TestGetActivePolicyViolations(t *testing.T) {
t.Parallel()
t.Run("Case true", func(t *testing.T) {
t.Run("components with active license policy violations", func(t *testing.T) {
config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: "SHC-PiperTest", Version: "", CustomScanVersion: "1.0"}
sys := newBlackduckMockSystem(config)
components, err := sys.Client.GetComponents("SHC-PiperTest", "1.0")
assert.NoError(t, err)
assert.Equal(t, 2, getActivePolicyViolations(components))
require.NoError(t, err)
violationCount, err := countComponentsActiveLicensePolicyViolations(&sys, components)
require.NoError(t, err)
assert.Equal(t, 2, violationCount)
})
}
func TestGetVulnerabilitiesWithComponents(t *testing.T) {
+67 -11
View File
@@ -23,6 +23,8 @@ const (
HEADER_PROJECT_DETAILS_V4 = "application/vnd.blackducksoftware.project-detail-4+json"
HEADER_USER_V4 = "application/vnd.blackducksoftware.user-4+json"
HEADER_BOM_V6 = "application/vnd.blackducksoftware.bill-of-materials-6+json"
HEADER_BOM_V7 = "application/vnd.blackducksoftware.bill-of-materials-7+json"
HEADER_POLICY_DETAILS_V5 = "application/vnd.blackducksoftware.policy-5+json"
)
// Projects defines the response to a BlackDuck project API request
@@ -82,6 +84,21 @@ type ComponentOrigin struct {
ExternalID string `json:"externalId,omitempty"`
}
type PaginatedResponse[T any] struct {
TotalCount int `json:"totalCount,omitempty"`
Items []T `json:"items,omitempty"`
}
type ComponentPolicyRule struct {
Metadata `json:"_meta,omitempty"`
Name string `json:"name,omitempty"`
PolicyApprovalStatus string `json:"policyApprovalStatus,omitempty"`
}
type PolicyRule struct {
Category string `json:"category,omitempty"`
}
// ToPackageUrl creates the package URL for the component
func (c *Component) ToPackageUrl() *packageurl.PackageURL {
purlParts := transformComponentOriginToPurlParts(c)
@@ -433,19 +450,58 @@ func (b *Client) GetComponentsWithLicensePolicyRule(projectName, versionName str
return &components, nil
}
// func (b *Client) GetComponentPolicyStatus(component Component) (ComponentPolicyStatus, error) {
// var policyStatusUrl string
// for _, link := range component.Links {
// if link.Rel == "policy-status" {
// policyStatusUrl = urlPath(link.Href)
// }
// }
func (b *Client) GetComponentPolicyRules(component Component) ([]ComponentPolicyRule, error) {
var policyRulesUrl string
for _, link := range component.Links {
if link.Rel == "policy-rules" {
policyRulesUrl = urlPath(link.Href)
break
}
}
if policyRulesUrl == "" {
return nil, fmt.Errorf("no Policy Rules found for component '%v'", component.Name)
}
// headers := http.Header{}
// headers.Add("Accept", HEADER_BOM_V6)
headers := http.Header{}
headers.Add("Accept", HEADER_BOM_V7)
// respBody, err := b.sendRequest("GET", policyStatusUrl, map[string]string{}, nil, headers)
// }
respBody, err := b.sendRequest(http.MethodGet, policyRulesUrl, map[string]string{}, nil, headers)
if err != nil {
return nil, fmt.Errorf("failed to get policy rules for component '%v': %w", component.Name, err)
}
response := PaginatedResponse[ComponentPolicyRule]{}
err = json.Unmarshal(respBody, &response)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal component policy rules for component '%v': %w", component.Name, err)
}
return response.Items, nil
}
func (b *Client) GetPolicyRuleDetails(policyRule ComponentPolicyRule) (PolicyRule, error) {
var policyRuleDetails PolicyRule
policyRuleDetailsUrl := urlPath(policyRule.Href)
if policyRuleDetailsUrl == "" {
return policyRuleDetails, fmt.Errorf("no policy rule details URL found for policy '%v'", policyRule.Name)
}
headers := http.Header{}
headers.Add("Accept", HEADER_POLICY_DETAILS_V5)
respBody, err := b.sendRequest(http.MethodGet, policyRuleDetailsUrl, map[string]string{}, nil, headers)
if err != nil {
return policyRuleDetails, fmt.Errorf("failed to get policy rule details for policy '%v': %w", policyRule.Name, err)
}
err = json.Unmarshal(respBody, &policyRuleDetails)
if err != nil {
return policyRuleDetails, fmt.Errorf("failed to unmarshal policy rule details for policy '%v': %w", policyRule.Name, err)
}
return policyRuleDetails, nil
}
func (b *Client) GetVulnerabilities(projectName, versionName string) (*Vulnerabilities, error) {
projectVersion, err := b.GetProjectVersion(projectName, versionName)