-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
fix(sbom): use PURL or Group and Name in case of Java #5154
Conversation
pkg/sbom/cyclonedx/unmarshal.go
Outdated
@@ -410,8 +411,26 @@ func toTrivyCdxComponent(component cdx.Component) ftypes.Component { | |||
|
|||
func getPackageName(typ string, component cdx.Component) string { | |||
// Jar uses `Group` field for `GroupID` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is no longer needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Done.
pkg/sbom/cyclonedx/unmarshal.go
Outdated
// Split the package into its parts | ||
parts := strings.Split(pkg, "/") | ||
|
||
// Get Group |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this comment? It almost duplicates the variable name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! Done.
pkg/sbom/cyclonedx/unmarshal.go
Outdated
|
||
pkgWithoutVersion := fmt.Sprintf("%s:%s", group, nameWOVersion) | ||
|
||
return pkgWithoutVersion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return pkgWithoutVersion | |
return fmt.Sprintf("%s:%s", group, nameWOVersion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again :) Done
@j1nka I left a couple of small comments. |
I don't remember, but there might be a reason why we use groups. I'd defer to @DmitriyLewen. He will be back next week. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @j1nka
Thanks for your work!
Some languages have nuances with lowercase in purl (e.g. Go
, npm
).
To avoid problem with that - we use original names (#4306 (comment)).
Maven plugin uses Name
and Group
field.
We try to adhere to the language format.
But i understand your point.
I left a comment. Take a look, please.
Regards, Dmitriy
pkg/sbom/cyclonedx/unmarshal.go
Outdated
func convertMavenPackage(pkg string) string { | ||
// Split the package into its parts | ||
parts := strings.Split(pkg, "/") | ||
|
||
group := parts[1] | ||
|
||
// Get FullName with Version | ||
nameWithVersion := parts[len(parts)-1] | ||
|
||
// Remove the Version from the package | ||
nameWOVersion := strings.Split(nameWithVersion, "@")[0] | ||
|
||
return fmt.Sprintf("%s:%s", group, nameWOVersion) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we handle all possible ways?
Something like that:
func convertMavenPackage(pkg string) string { | |
// Split the package into its parts | |
parts := strings.Split(pkg, "/") | |
group := parts[1] | |
// Get FullName with Version | |
nameWithVersion := parts[len(parts)-1] | |
// Remove the Version from the package | |
nameWOVersion := strings.Split(nameWithVersion, "@")[0] | |
return fmt.Sprintf("%s:%s", group, nameWOVersion) | |
} | |
func convertMavenPackage(component cdx.Component) string { | |
// By default, Jar uses `Group` field for `GroupID` | |
// Use original `ArtifactID` and `GroupID` | |
if component.Group != "" { | |
return fmt.Sprintf("%s:%s", component.Group, component.Name) | |
} | |
// if `Name` and `Group` fields don't exist - unmarshal purl | |
if component.Name == "" { | |
// Split the package into its parts | |
parts := strings.Split(component.PackageURL, "/") | |
group := parts[1] | |
// Get FullName with Version | |
nameWithVersion := parts[len(parts)-1] | |
// Remove the Version from the package | |
nameWOVersion := strings.Split(nameWithVersion, "@")[0] | |
return fmt.Sprintf("%s:%s", group, nameWOVersion) | |
} | |
// in some cases `GroupID:ArtifactID` is stored in `Name` | |
return component.Name | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the specification, name is a required field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh... right. Thanks!
Then, we can check that the "Name" field contains "GroupID".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikpivkin @DmitriyLewen thanks for your advices! Added some more checks. Main idea is to catch smth like:
1st Version of SBOM (we don't have Group and that's why we can get empty list of vulners):
{
"bom-ref": "",
"name": "",
"purl": "",
"scope": "",
"type": "",
"version": ""
}
2nd Version of SBOM (we have both - Group and Name):
{
"group" : "",
"name" : "",
"version" : "",
"cpe" : "",
"purl" : "",
"externalReferences" : [
{
"type" : "",
"url" : ""
}
],
"type" : "",
"bom-ref" : ""
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your examples are not valid.
e.g. Name
is required field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your examples are not valid. e.g.
Name
is required field.
Hm, i removed all data in this two examples just to show fields. In both cases we have name
, but in first we don't have group
. Maybe i didn't understand you comment abiut "name
is required".
Yes if purl
and group
are missing we miss the package. In last commit I've added checks like you told me - check the group
. You want me to add some checks to identify if purl
is present? Because at the moment I don't check if purl
is present and trying to parse it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh... i didn't understand that you removed all values from these fields ( i thought these field are empty).
You want me to add some checks to identify if purl is present
I think we need to add that.
group
and purl
can be omitted. So in this case we need to get package name from name
field.
It is strange, but it is possible:
{
"bom-ref": "",
"name": "",
"scope": "",
"type": "",
"version": ""
}
I
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok! Will do it. But if we don't have group
or purl
what we need in return? In case of Java only name
will be not enough to get vulnerability data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use name
field. It is better than nothing.
It is also possible that name
will use <GroupID>:<ArtifactID>
format.
e.g. we used this format in name
before #4674.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope i get it right :) Added checks and name
as default behavior if there is no group
or purl
@DmitriyLewen thanks for the clarification! But i'm talking only about Java projects and nothing else. It may happen that we don't get vulnerability data from SBOM because sometimes we don't have "Group" |
Can you share some example for this case? e.g. before #4674 we save |
Sure! Two examples above are the real SBOMs generated by different tools (not Trivy) |
pkg/sbom/cyclonedx/unmarshal.go
Outdated
// Split the package into its parts | ||
parts := strings.Split(component.PackageURL, "/") | ||
|
||
group := parts[1] | ||
|
||
// Get FullName with Version | ||
nameWithVersion := parts[len(parts)-1] | ||
|
||
// Remove the Version from the package | ||
nameWOVersion := strings.Split(nameWithVersion, "@")[0] | ||
|
||
return fmt.Sprintf("%s:%s", group, nameWOVersion) | ||
// In case we don't have Group or PURL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already parsed purl
here -
trivy/pkg/sbom/cyclonedx/unmarshal.go
Line 347 in 14d3f02
pkg := p.Package() |
We can use this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already parsed
purl
here -trivy/pkg/sbom/cyclonedx/unmarshal.go
Line 347 in 14d3f02
pkg := p.Package() We can use this.
Great idea, thanks! Made some changes
pkg/sbom/cyclonedx/unmarshal.go
Outdated
func getPackageName(purl *purl.PackageURL, component cdx.Component) string { | ||
if purl.Type == packageurl.TypeMaven { | ||
return convertMavenPackage(purl, component) | ||
} | ||
return component.Name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i rechecked code before this line and found that we don't parse CycloneDX without purl
-
trivy/pkg/sbom/cyclonedx/unmarshal.go
Lines 337 to 340 in eea3320
if component.PackageURL == "" { | |
log.Logger.Warnf("Skip the component (BOM-Ref: %s) as the PURL is empty", component.BOMRef) | |
return false, "", nil, ErrPURLEmpty | |
} |
So we can remove this check (sorry for confusing you).
I think we can remove purl == ""
check and move logic to getPackageName
and use parsed package name (pkg.Name = getPackageName(p.Type, pkg.Name, component)). Something like that:
func getPackageName(purl *purl.PackageURL, component cdx.Component) string { | |
if purl.Type == packageurl.TypeMaven { | |
return convertMavenPackage(purl, component) | |
} | |
return component.Name | |
func getPackageName(typ, pkgNameFromPurl string, component cdx.Component) string { | |
if typ == packageurl.TypeMaven { | |
// Jar uses `Group` field for `GroupID` | |
if component.Group != "" { | |
return fmt.Sprintf("%s:%s", component.Group, component.Name) | |
} else { | |
// use name derived from purl if `Group` doesn't exist | |
return pkgNameFromPurl | |
} | |
} | |
return component.Name | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow :) Great idea! Thanks a lot! Done :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now.
@j1nka Thanks for your work!
Can you update title of this PR?
(we don't use purl
instead of name
and group
name => we use one of these fields.
Hi! Sure. Smth like that? Thanks for your help and patience, Dmitriy! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Thanks!
@knqyf263 i approved this PR.
@knqyf263 @DmitriyLewen Hi! Is there anything else I should to to merge this PR? Thanks! |
@nikpivkin requested changes. We cannot merge the PR until @nikpivkin approves it. |
Description
If another SCA generates CycloneDX file, it may does not have fields like "group" and "name" in SBOM file. As example, an Dependency Track. There's problem in handling SBOM files, where Trivy trying to get data from specific SBOM fields, but not from "purl" field of SBOM (CycloneDX-json) file.
Related issues
Checklist