From 70b3e3f19ddff2a8713d86b69fbdd9ab742c1ff9 Mon Sep 17 00:00:00 2001 From: attiasas Date: Wed, 20 May 2026 21:19:23 +0300 Subject: [PATCH 1/2] Support parsing SCA issues without components --- policy/enforcer/policyenforcer.go | 6 +- policy/local/localconvertor.go | 61 +++++++++++++++---- policy/local/localconvertor_test.go | 2 +- utils/formats/cdxutils/cyclonedxutils.go | 1 + utils/formats/violationutils/violations.go | 2 +- utils/results/common.go | 49 +++++++++++---- utils/results/conversion/convertor_test.go | 8 +-- .../cyclonedxparser/cyclonedxparser.go | 8 ++- .../conversion/sarifparser/sarifparser.go | 27 ++++++-- .../simplejsonparser/simplejsonparser.go | 25 +++++--- .../conversion/summaryparser/summaryparser.go | 2 +- 11 files changed, 140 insertions(+), 51 deletions(-) diff --git a/policy/enforcer/policyenforcer.go b/policy/enforcer/policyenforcer.go index 43c68cb61..624125b4d 100644 --- a/policy/enforcer/policyenforcer.go +++ b/policy/enforcer/policyenforcer.go @@ -193,11 +193,7 @@ func convertToScaViolation(cmdResults *results.SecurityCommandResults, impactedC scaViolation = violationutils.ScaViolation{ Violation: convertToBasicViolation(getScaViolationType(violation), violation), } - affectedComponent, scaViolation.DirectComponents, scaViolation.ImpactPaths = locateBomComponentInfo(cmdResults, impactedComponentXrayId, violation) - if affectedComponent == nil { - return - } - scaViolation.ImpactedComponent = *affectedComponent + scaViolation.ImpactedComponent, scaViolation.DirectComponents, scaViolation.ImpactPaths = locateBomComponentInfo(cmdResults, impactedComponentXrayId, violation) return } diff --git a/policy/local/localconvertor.go b/policy/local/localconvertor.go index 874c79dc2..d1c4b569d 100644 --- a/policy/local/localconvertor.go +++ b/policy/local/localconvertor.go @@ -219,7 +219,7 @@ func getScaViolationType(violation services.Violation) violationutils.ViolationI return "" } -func convertToScaViolation(violation services.Violation, severity severityutils.Severity, affectedComponent cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) violationutils.ScaViolation { +func convertToScaViolation(violation services.Violation, severity severityutils.Severity, affectedComponent *cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) violationutils.ScaViolation { return violationutils.ScaViolation{ Violation: convertToBasicViolation(violation, severity), ImpactedComponent: affectedComponent, @@ -231,8 +231,13 @@ func convertToScaViolation(violation services.Violation, severity severityutils. func convertScaSecurityViolationToPolicyViolation(convertedViolations *violationutils.Violations) ParseScanGraphViolationFunc { xrayService := results.GetXrayService() return func(violation services.Violation, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesId string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) (err error) { - // Create the CycloneDX component for the impacted package - affectedComponent := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + var affectedComponent *cyclonedx.Component + var fixedVersions *[]cyclonedx.AffectedVersions + if impactedPackagesId != "" { + // Create the CycloneDX component for the impacted package + component := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + affectedComponent = &component + } // Extract the vulnerability CVE's information and create the SCA vulnerability for each cveIds, applicability, cwes, ratings := results.ExtractIssuesInfoForCdx(violation.IssueId, cves, severity, applicabilityStatus, xrayService) extendedInformation := "" @@ -250,15 +255,18 @@ func convertScaSecurityViolationToPolicyViolation(convertedViolations *violation References: violation.References, Service: xrayService, }) - // Attach the affected impacted library component to the vulnerability - cdxutils.AttachComponentAffects(&vulnerability, affectedComponent, func(affectedComponent cyclonedx.Component) cyclonedx.Affects { - return cdxutils.CreateScaImpactedAffects(affectedComponent, fixedVersion) - }) + if affectedComponent != nil { + // Attach the affected impacted library component to the vulnerability + cdxutils.AttachComponentAffects(&vulnerability, *affectedComponent, func(affectedComponent cyclonedx.Component) cyclonedx.Affects { + return cdxutils.CreateScaImpactedAffects(affectedComponent, fixedVersion) + }) + fixedVersions = cdxutils.ConvertToAffectedVersions(*affectedComponent, fixedVersion) + } convertedViolations.Sca = append(convertedViolations.Sca, violationutils.CveViolation{ ScaViolation: convertToScaViolation(violation, severity, affectedComponent, directComponents, impactPaths), CveVulnerability: vulnerability, ContextualAnalysis: applicability[i], - FixedVersions: cdxutils.ConvertToAffectedVersions(affectedComponent, fixedVersion), + FixedVersions: fixedVersions, JfrogResearchInformation: results.ConvertJfrogResearchInformation(violation.ExtendedInformation), }) } @@ -268,8 +276,12 @@ func convertScaSecurityViolationToPolicyViolation(convertedViolations *violation func convertScaLicenseViolationToPolicyViolation(convertedViolations *violationutils.Violations) ParseScanGraphViolationFunc { return func(violation services.Violation, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesId string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) (err error) { - // Create the CycloneDX component for the impacted package - affectedComponent := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + var affectedComponent *cyclonedx.Component + if impactedPackagesId != "" { + // Create the CycloneDX component for the impacted package + component := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + affectedComponent = &component + } // Add the license violation convertedViolations.License = append(convertedViolations.License, violationutils.LicenseViolation{ ScaViolation: convertToScaViolation(violation, severity, affectedComponent, directComponents, impactPaths), @@ -282,8 +294,12 @@ func convertScaLicenseViolationToPolicyViolation(convertedViolations *violationu func convertOperationalRiskViolationToPolicyViolation(convertedViolations *violationutils.Violations) ParseScanGraphViolationFunc { return func(violation services.Violation, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesId string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) (err error) { - // Create the CycloneDX component for the impacted package - affectedComponent := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + var affectedComponent *cyclonedx.Component + if impactedPackagesId != "" { + // Create the CycloneDX component for the impacted package + component := results.CreateScaComponentFromXrayCompId(impactedPackagesId) + affectedComponent = &component + } // Add the operational risk violation convertedViolations.OpRisk = append(convertedViolations.OpRisk, violationutils.OperationalRiskViolation{ ScaViolation: convertToScaViolation(violation, severity, affectedComponent, directComponents, impactPaths), @@ -331,6 +347,13 @@ func ForEachScanGraphViolation(target results.ScanTarget, descriptors []string, // No handler was provided for security violations continue } + if len(impactedPackagesIds) == 0 { + // Security violation without any impacted packages, we pass an empty string as the impacted package ID + if e := securityHandler(violation, cves, applicabilityStatus, severity, "", []string{}, []formats.ComponentRow{}, [][]formats.ComponentRow{}); e != nil { + err = errors.Join(err, e) + continue + } + } for compIndex := 0; compIndex < len(impactedPackagesIds); compIndex++ { if e := securityHandler(violation, cves, applicabilityStatus, severity, impactedPackagesIds[compIndex], fixedVersions[compIndex], directComponents[compIndex], impactPaths[compIndex]); e != nil { err = errors.Join(err, e) @@ -342,6 +365,13 @@ func ForEachScanGraphViolation(target results.ScanTarget, descriptors []string, // No handler was provided for license violations continue } + if len(impactedPackagesIds) == 0 { + // License violation without any impacted packages, we pass an empty string as the impacted package ID + if e := licenseHandler(violation, cves, applicabilityStatus, severity, "", []string{}, []formats.ComponentRow{}, [][]formats.ComponentRow{}); e != nil { + err = errors.Join(err, e) + continue + } + } for compIndex := range impactedPackagesIds { if impactedPackagesName, _, _ := techutils.SplitComponentId(impactedPackagesIds[compIndex]); impactedPackagesName == "root" { // No Need to output 'root' as impacted package for license since we add this as the root node for the scan @@ -357,6 +387,13 @@ func ForEachScanGraphViolation(target results.ScanTarget, descriptors []string, // No handler was provided for operational risk violations continue } + if len(impactedPackagesIds) == 0 { + // Operational risk violation without any impacted packages, we pass an empty string as the impacted package ID + if e := operationalRiskHandler(violation, cves, applicabilityStatus, severity, "", []string{}, []formats.ComponentRow{}, [][]formats.ComponentRow{}); e != nil { + err = errors.Join(err, e) + continue + } + } for compIndex := range impactedPackagesIds { if e := operationalRiskHandler(violation, cves, applicabilityStatus, severity, impactedPackagesIds[compIndex], fixedVersions[compIndex], directComponents[compIndex], impactPaths[compIndex]); e != nil { err = errors.Join(err, e) diff --git a/policy/local/localconvertor_test.go b/policy/local/localconvertor_test.go index fdb2890c9..242fe8e56 100644 --- a/policy/local/localconvertor_test.go +++ b/policy/local/localconvertor_test.go @@ -208,7 +208,7 @@ func createScaTestViolation(id, component string, vioType violationutils.Violati Severity: severity, Watch: watch, }, - ImpactedComponent: cyclonedx.Component{ + ImpactedComponent: &cyclonedx.Component{ BOMRef: fmt.Sprintf("pkg:generic/%s", component), PackageURL: fmt.Sprintf("pkg:generic/%s", component), Type: cyclonedx.ComponentTypeLibrary, diff --git a/utils/formats/cdxutils/cyclonedxutils.go b/utils/formats/cdxutils/cyclonedxutils.go index 7236773bc..005418161 100644 --- a/utils/formats/cdxutils/cyclonedxutils.go +++ b/utils/formats/cdxutils/cyclonedxutils.go @@ -284,6 +284,7 @@ func SearchComponentByRef(components *[]cyclonedx.Component, ref string) (compon if components == nil || len(*components) == 0 { return } + // TODO: Support BOM-Link Element: https://cyclonedx.org/docs/1.6/json/#vulnerabilities_items_affects_items_ref_anyOf_i1 (for now we only support BOMRef) for i, comp := range *components { if comp.BOMRef == ref { return &(*components)[i] diff --git a/utils/formats/violationutils/violations.go b/utils/formats/violationutils/violations.go index eaa67c1c2..58fad0e1f 100644 --- a/utils/formats/violationutils/violations.go +++ b/utils/formats/violationutils/violations.go @@ -206,7 +206,7 @@ type JasViolation struct { type ScaViolation struct { Violation - ImpactedComponent cyclonedx.Component `json:"impacted_component"` + ImpactedComponent *cyclonedx.Component `json:"impacted_component,omitempty"` DirectComponents []formats.ComponentRow `json:"direct_components,omitempty"` ImpactPaths [][]formats.ComponentRow `json:"impact_paths,omitempty"` } diff --git a/utils/results/common.go b/utils/results/common.go index 67d9ce1e7..227b6e80e 100644 --- a/utils/results/common.go +++ b/utils/results/common.go @@ -54,7 +54,7 @@ type ParseScanGraphVulnerabilityFunc func(vulnerability services.Vulnerability, type ParseLicenseFunc func(license services.License, impactedPackagesId string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) error type ParseJasIssueFunc func(run *sarif.Run, rule *sarif.ReportingDescriptor, severity severityutils.Severity, result *sarif.Result, location *sarif.Location) error type ParseSbomComponentFunc func(component cyclonedx.Component, relatedDependencies *cyclonedx.Dependency, relation cdxutils.ComponentRelation) error -type ParseBomScaVulnerabilityFunc func(vulnerability cyclonedx.Vulnerability, component cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) error +type ParseBomScaVulnerabilityFunc func(vulnerability cyclonedx.Vulnerability, component *cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) error // Allows to iterate over the provided SARIF runs and call the provided handler for each issue to process it. func ForEachJasIssue(runs []*sarif.Run, entitledForJas bool, handler ParseJasIssueFunc) (err error) { @@ -110,6 +110,13 @@ func ForEachScanGraphVulnerability(target ScanTarget, descriptors []string, vuln err = errors.Join(err, e) continue } + if len(impactedPackagesIds) == 0 { + // Vulnerability without any impacted packages, we pass an empty string as the impacted package ID + if e := handler(vulnerability, cves, applicabilityStatus, severity, "", []string{}, []formats.ComponentRow{}, [][]formats.ComponentRow{}); e != nil { + err = errors.Join(err, e) + continue + } + } for compIndex := 0; compIndex < len(impactedPackagesIds); compIndex++ { if e := handler(vulnerability, cves, applicabilityStatus, severity, impactedPackagesIds[compIndex], fixedVersions[compIndex], directComponents[compIndex], impactPaths[compIndex]); e != nil { err = errors.Join(err, e) @@ -154,13 +161,8 @@ func ForEachScaBomVulnerability(_ ScanTarget, bom *cyclonedx.BOM, entitledForJas } // Get the related components for the vulnerability for _, affectedComponent := range *vulnerability.Affects { - relatedComponent := cdxutils.SearchComponentByRef(bom.Components, affectedComponent.Ref) - if relatedComponent == nil { - log.Verbose(fmt.Sprintf("Skipping vulnerability %s as it has no related component with BOMRef %s", vulnerability.BOMRef, affectedComponent.Ref)) - continue - } // Pass the vulnerability to the handler with its related information - if e := handler(vulnerability, *relatedComponent, GetFixedVersions(affectedComponent), applicability, cdxRatingToSeverity(vulnerability.Ratings)); e != nil { + if e := handler(vulnerability, cdxutils.SearchComponentByRef(bom.Components, affectedComponent.Ref), GetFixedVersions(affectedComponent), applicability, cdxRatingToSeverity(vulnerability.Ratings)); e != nil { err = errors.Join(err, e) continue } @@ -216,6 +218,13 @@ func ForEachLicense(target ScanTarget, licenses []services.License, handler Pars err = errors.Join(err, e) continue } + if len(impactedPackagesIds) == 0 { + // License without any impacted packages, we pass an empty string as the impacted package ID + if e := handler(license, "", []formats.ComponentRow{}, [][]formats.ComponentRow{}); e != nil { + err = errors.Join(err, e) + continue + } + } for compIndex := range impactedPackagesIds { if e := handler(license, impactedPackagesIds[compIndex], directComponents[compIndex], impactPaths[compIndex]); e != nil { err = errors.Join(err, e) @@ -654,6 +663,10 @@ func GetDependencyId(depName, version string) string { } func GetScaIssueId(depName, version, issueId string) string { + if depName == "" && version == "" { + depName = "unknown" + version = "unknown" + } return fmt.Sprintf("%s_%s_%s", issueId, depName, version) } @@ -1277,6 +1290,11 @@ func ScanResponseToSbom(destination *cyclonedx.BOM, scanResponse services.ScanRe func ParseScanGraphLicenseToSbom(destination *cyclonedx.BOM) ParseLicenseFunc { return func(license services.License, impactedPackagesId string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) error { + if impactedPackagesId == "" { + // License without any impacted packages, we skip for now + log.Warn(fmt.Sprintf("License %s without any impacted component, skip attaching it to the SBOM", license.Key)) + return nil + } // Add the license related component if it is not already existing affectedComponent := GetOrCreateScaComponent(destination, impactedPackagesId) // Attach the license to the component @@ -1298,8 +1316,11 @@ func ParseScanGraphVulnerabilityToSbom(destination *cyclonedx.BOM) ParseScanGrap // Prepare the information needed to create the SCA vulnerability xrayService := GetXrayService() return func(vulnerability services.Vulnerability, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesId string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) error { - // Add the vulnerability related component if it is not already existing - affectedComponent := GetOrCreateScaComponent(destination, impactedPackagesId) + var affectedComponent *cyclonedx.Component + if impactedPackagesId != "" { + // Add the vulnerability related component if it is not already existing + affectedComponent = GetOrCreateScaComponent(destination, impactedPackagesId) + } // Extract the vulnerability CVE's information and create the SCA vulnerability for each cveIds, applicability, cwes, ratings := ExtractIssuesInfoForCdx(vulnerability.IssueId, cves, severity, applicabilityStatus, xrayService) extendedInformation := "" @@ -1318,10 +1339,12 @@ func ParseScanGraphVulnerabilityToSbom(destination *cyclonedx.BOM) ParseScanGrap Service: xrayService, } vulnerability := cdxutils.GetOrCreateScaIssue(destination, params) - // Attach the affected impacted library component to the vulnerability - cdxutils.AttachComponentAffects(vulnerability, *affectedComponent, func(affectedComponent cyclonedx.Component) cyclonedx.Affects { - return cdxutils.CreateScaImpactedAffects(affectedComponent, fixedVersion) - }) + if affectedComponent != nil { + // Attach the affected impacted library component to the vulnerability + cdxutils.AttachComponentAffects(vulnerability, *affectedComponent, func(affectedComponent cyclonedx.Component) cyclonedx.Affects { + return cdxutils.CreateScaImpactedAffects(affectedComponent, fixedVersion) + }) + } // Attach JAS information to the vulnerability AttachApplicabilityToVulnerability(destination, vulnerability, applicability[i]) } diff --git a/utils/results/conversion/convertor_test.go b/utils/results/conversion/convertor_test.go index d08950c85..2c117ad57 100644 --- a/utils/results/conversion/convertor_test.go +++ b/utils/results/conversion/convertor_test.go @@ -422,7 +422,7 @@ func getAuditTestResults(unique bool) (*results.SecurityCommandResults, validati ViolationId: "XRAY-609848", Severity: severityutils.Unknown, }, - ImpactedComponent: cyclonedx.Component{ + ImpactedComponent: &cyclonedx.Component{ BOMRef: "pkg:npm/async@3.2.4", PackageURL: "pkg:npm/async@3.2.4", }, @@ -462,7 +462,7 @@ func getAuditTestResults(unique bool) (*results.SecurityCommandResults, validati ViolationId: "98yhnmju7654rfvbnj", Severity: severityutils.Medium, }, - ImpactedComponent: cyclonedx.Component{ + ImpactedComponent: &cyclonedx.Component{ BOMRef: "pkg:npm/lodash@4.17.0", PackageURL: "pkg:npm/lodash@4.17.0", }, @@ -503,7 +503,7 @@ func getAuditTestResults(unique bool) (*results.SecurityCommandResults, validati ViolationId: "12ee2e134edqwe234", Severity: severityutils.High, }, - ImpactedComponent: cyclonedx.Component{ + ImpactedComponent: &cyclonedx.Component{ BOMRef: "pkg:npm/lodash@4.17.0", PackageURL: "pkg:npm/lodash@4.17.0", }, @@ -751,7 +751,7 @@ func getDockerScanTestResults(unique bool) (*results.SecurityCommandResults, val ViolationId: "XRAY-632747", Severity: severityutils.Unknown, }, - ImpactedComponent: cyclonedx.Component{ + ImpactedComponent: &cyclonedx.Component{ BOMRef: "pkg:deb/debian/bookworm/libssl3@3.0.13-1~deb12u1", PackageURL: "pkg:deb/debian/bookworm/libssl3@3.0.13-1~deb12u1", }, diff --git a/utils/results/conversion/cyclonedxparser/cyclonedxparser.go b/utils/results/conversion/cyclonedxparser/cyclonedxparser.go index 4d47c2771..5a2422080 100644 --- a/utils/results/conversion/cyclonedxparser/cyclonedxparser.go +++ b/utils/results/conversion/cyclonedxparser/cyclonedxparser.go @@ -142,9 +142,11 @@ func (cdc *CmdResultsCycloneDxConverter) ParseCVEs(enrichedSbom *cyclonedx.BOM, } cdc.addJasService(applicableScan) return results.ForEachScaBomVulnerability(cdc.currentTarget, enrichedSbom, cdc.entitledForJas, results.CollectRuns(applicableScan...), - func(vulnToParse cyclonedx.Vulnerability, compToParse cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { - // Add the vulnerability related component if it is not already existing - cdc.getOrCreateScaComponent(compToParse) + func(vulnToParse cyclonedx.Vulnerability, compToParse *cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { + if compToParse != nil { + // Add the vulnerability related component if it is not already existing + cdc.getOrCreateScaComponent(*compToParse) + } // Add the vulnerability to the BOM if it is not already existing vulnerability := cdc.getOrCreateScaIssue(vulnToParse) // Attach JAS information to the vulnerability diff --git a/utils/results/conversion/sarifparser/sarifparser.go b/utils/results/conversion/sarifparser/sarifparser.go index 923a0e5a2..6128f727d 100644 --- a/utils/results/conversion/sarifparser/sarifparser.go +++ b/utils/results/conversion/sarifparser/sarifparser.go @@ -349,14 +349,19 @@ func (sc *CmdResultsSarifConverter) ParseCVEs(enrichedSbom *cyclonedx.BOM, appli func addCdxScaVulnerability(cmdType utils.CommandType, enrichedSbom *cyclonedx.BOM, sarifResults *[]*sarif.Result, rules *map[string]*sarif.ReportingDescriptor) results.ParseBomScaVulnerabilityFunc { bomIndex := cdxutils.NewBOMIndex(enrichedSbom, true) - return func(vulnerability cyclonedx.Vulnerability, component cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { - impactPaths := results.BuildImpactPath(component, bomIndex) - directDependencies := results.ExtractComponentDirectComponentsInBOM(bomIndex, component, impactPaths) + return func(vulnerability cyclonedx.Vulnerability, component *cyclonedx.Component, fixedVersion *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { + var impactPaths [][]formats.ComponentRow + var directDependencies []formats.ComponentRow + var compName, compVersion string + if component != nil { + impactPaths = results.BuildImpactPath(*component, bomIndex) + directDependencies = results.ExtractComponentDirectComponentsInBOM(bomIndex, *component, impactPaths) + compName, compVersion, _ = techutils.SplitPackageURL(component.PackageURL) + } applicabilityStatus, maxCveScore, cves, fixedVersions, markdownDescription, e := prepareCdxInfoForSarif(vulnerability, severity, applicability, directDependencies, fixedVersion) if e != nil { return } - compName, compVersion, _ := techutils.SplitPackageURL(component.PackageURL) createAndAddScaIssue(scaParseParams{ CmdType: cmdType, IssueId: vulnerability.ID, @@ -589,6 +594,20 @@ func parseScaToSarifFormat(params scaParseParams) (sarifResults []*sarif.Result, params.Summary, params.MarkdownDescription, ) + if len(params.DirectComponents) == 0 && params.ImpactedPackagesName == "" && params.ImpactedPackagesVersion == "" { + log.Debug(fmt.Sprintf("Issue %s without any components, adding a result with the issue id only without any location", issueId)) + // Issue without any components, lets add a result with the issue id only + issueResult := sarif.NewRuleResult(cveImpactedComponentRuleId). + WithMessage(sarif.NewTextMessage(params.GenerateTitleFunc("unknown", "unknown", issueId, watch))). + WithLevel(level.String()) + // Add properties + issueResult = appendScaVulnerabilityPropertiesToSarifResult(issueResult, params.ApplicabilityStatus, params.FixedVersions, params.AddFixedVersionProperty) + if isViolation { + issueResult = appendViolationContextToSarifResult(issueResult, *params.Violation) + } + sarifResults = append(sarifResults, issueResult) + return + } for _, directDependency := range params.DirectComponents { // Create result for each direct dependency issueResult := sarif.NewRuleResult(cveImpactedComponentRuleId). diff --git a/utils/results/conversion/simplejsonparser/simplejsonparser.go b/utils/results/conversion/simplejsonparser/simplejsonparser.go index 84280b41c..cf805fe65 100644 --- a/utils/results/conversion/simplejsonparser/simplejsonparser.go +++ b/utils/results/conversion/simplejsonparser/simplejsonparser.go @@ -131,8 +131,13 @@ func (sjc *CmdResultsSimpleJsonConverter) ParseCVEs(enrichedSbom *cyclonedx.BOM, } bomIndex := cdxutils.NewBOMIndex(enrichedSbom, true) return results.ForEachScaBomVulnerability(sjc.currentTarget, enrichedSbom, sjc.entitledForJas, results.CollectRuns(applicableScan...), - func(vulnerability cyclonedx.Vulnerability, component cyclonedx.Component, fixedVersions *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { - impactPaths := results.BuildImpactPath(component, bomIndex) + func(vulnerability cyclonedx.Vulnerability, component *cyclonedx.Component, fixedVersions *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (e error) { + var impactPaths [][]formats.ComponentRow + var directComponents []formats.ComponentRow + if component != nil { + impactPaths = results.BuildImpactPath(*component, bomIndex) + directComponents = results.ExtractComponentDirectComponentsInBOM(bomIndex, *component, impactPaths) + } // Convert the CycloneDX vulnerability to a simple JSON vulnerability row sjc.current.Vulnerabilities = append(sjc.current.Vulnerabilities, sjc.createVulnerabilityOrViolationRowFromCdx( vulnerability.ID, @@ -141,7 +146,7 @@ func (sjc *CmdResultsSimpleJsonConverter) ParseCVEs(enrichedSbom *cyclonedx.BOM, applicability, vulnerability, component, - results.ExtractComponentDirectComponentsInBOM(bomIndex, component, impactPaths), + directComponents, impactPaths, fixedVersions, // TODO: implement JfrogResearchInformation conversion @@ -223,12 +228,15 @@ func (sjc *CmdResultsSimpleJsonConverter) ParseViolations(violationsScanResults return nil } -func (sjc *CmdResultsSimpleJsonConverter) createVulnerabilityOrViolationRowFromCdx(issueId, summary string, severity severityutils.Severity, contextualAnalysis *formats.Applicability, vulnerability cyclonedx.Vulnerability, component cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow, fixedVersions *[]cyclonedx.AffectedVersions, jfrogResearch *formats.JfrogResearchInformation) formats.VulnerabilityOrViolationRow { +func (sjc *CmdResultsSimpleJsonConverter) createVulnerabilityOrViolationRowFromCdx(issueId, summary string, severity severityutils.Severity, contextualAnalysis *formats.Applicability, vulnerability cyclonedx.Vulnerability, component *cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow, fixedVersions *[]cyclonedx.AffectedVersions, jfrogResearch *formats.JfrogResearchInformation) formats.VulnerabilityOrViolationRow { applicabilityStatus := jasutils.NotScanned if contextualAnalysis != nil { applicabilityStatus = jasutils.ConvertToApplicabilityStatus(contextualAnalysis.Status) } - compName, compVersion, compType := techutils.SplitPackageURL(component.PackageURL) + var compName, compVersion, compType string + if component != nil { + compName, compVersion, compType = techutils.SplitPackageURL(component.PackageURL) + } return formats.VulnerabilityOrViolationRow{ IssueId: issueId, Summary: summary, @@ -263,8 +271,11 @@ func toReferences(vulnerability cyclonedx.Vulnerability) (references []string) { return } -func (sjc *CmdResultsSimpleJsonConverter) createLicenseViolationRow(licenseKey, licenseName string, severity severityutils.Severity, component cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow, violationContext formats.ViolationContext) formats.LicenseViolationRow { - compName, compVersion, compType := techutils.SplitPackageURL(component.PackageURL) +func (sjc *CmdResultsSimpleJsonConverter) createLicenseViolationRow(licenseKey, licenseName string, severity severityutils.Severity, component *cyclonedx.Component, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow, violationContext formats.ViolationContext) formats.LicenseViolationRow { + var compName, compVersion, compType string + if component != nil { + compName, compVersion, compType = techutils.SplitPackageURL(component.PackageURL) + } return formats.LicenseViolationRow{ ViolationContext: violationContext, LicenseRow: formats.LicenseRow{ diff --git a/utils/results/conversion/summaryparser/summaryparser.go b/utils/results/conversion/summaryparser/summaryparser.go index a8b4ac9e9..9fd9ec4c0 100644 --- a/utils/results/conversion/summaryparser/summaryparser.go +++ b/utils/results/conversion/summaryparser/summaryparser.go @@ -198,7 +198,7 @@ func (sc *CmdResultsSummaryConverter) ParseCVEs(enrichedSbom *cyclonedx.BOM, app func (sc *CmdResultsSummaryConverter) getBomScaVulnerabilityHandler() results.ParseBomScaVulnerabilityFunc { parsed := datastructures.MakeSet[string]() - return func(vulnerability cyclonedx.Vulnerability, _ cyclonedx.Component, _ *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (err error) { + return func(vulnerability cyclonedx.Vulnerability, _ *cyclonedx.Component, _ *[]cyclonedx.AffectedVersions, applicability *formats.Applicability, severity severityutils.Severity) (err error) { if parsed.Exists(vulnerability.BOMRef) { return } From 8aa5eb1110ec104b516673f06ac3b5d6d02ba2b8 Mon Sep 17 00:00:00 2001 From: attiasas Date: Wed, 20 May 2026 21:26:01 +0300 Subject: [PATCH 2/2] remove todo --- utils/formats/cdxutils/cyclonedxutils.go | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/formats/cdxutils/cyclonedxutils.go b/utils/formats/cdxutils/cyclonedxutils.go index 005418161..7236773bc 100644 --- a/utils/formats/cdxutils/cyclonedxutils.go +++ b/utils/formats/cdxutils/cyclonedxutils.go @@ -284,7 +284,6 @@ func SearchComponentByRef(components *[]cyclonedx.Component, ref string) (compon if components == nil || len(*components) == 0 { return } - // TODO: Support BOM-Link Element: https://cyclonedx.org/docs/1.6/json/#vulnerabilities_items_affects_items_ref_anyOf_i1 (for now we only support BOMRef) for i, comp := range *components { if comp.BOMRef == ref { return &(*components)[i]