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

Matches should be sorted by package name for template output #696

Closed
sparrowt opened this issue Mar 24, 2022 · 4 comments · Fixed by #699
Closed

Matches should be sorted by package name for template output #696

sparrowt opened this issue Mar 24, 2022 · 4 comments · Fixed by #699
Assignees
Labels
bug Something isn't working

Comments

@sparrowt
Copy link
Contributor

sparrowt commented Mar 24, 2022

Issue

When using a custom output template -o template -t my.tmpl the data available in .Matches is sorted in a non-intuitive order, different from the default (-o table) output.

Currently it seems to be sorted alphabetically by the Vulnerability ID (i.e. CVE-1, CVE-2, GHSA-a, GHSZ-b) which doesn't seem particularly useful and means that multiple vulns for the same package are not necessarily grouped together.

What I expected

I expected them to come out ordered by Package.Name (a.k.a. Artifact.Name) the same order used by the default table output (which sorts the rows by the first column which is Package.Name).

I investigated various of the sprig functions to see if I could do the sorting myself in the template, but couldn't find a way to sort Matches by a specific sub-key before passing it into the range.

Environment

  • Output of grype version: 0.34.6
  • OS (e.g: cat /etc/os-release or similar): official docker image

Repro

Use a custom template which adds extra columns such as package type (yes it's horrible to use printf with fixed padding but I can't find a way to do tabulation in a custom template, c.f. rejected proposal golang/go#45752)

TYPE   NAME                 INSTALLED    VULN                 SEVERITY     FIXED?   FIXED IN     UPSTREAM PACKAGE
{{- range .Matches}}
{{printf "%-6s %-20s %-12s %-20s %-12s %-8s %-12s %s" .Artifact.Type .Artifact.Name .Artifact.Version .Vulnerability.ID .Vulnerability.Severity .Vulnerability.Fix.State (.Vulnerability.Fix.Versions | join ", ") (.Artifact.Upstreams | join ", ")}}
{{- end}}
grype alpine:3.14.1 -o template -t mycustom.tmpl

Output: (not grouped very helpfully - even harder to read in cases with more vulns)

TYPE   NAME                 INSTALLED    VULN                 SEVERITY     FIXED?   FIXED IN     UPSTREAM PACKAGE
apk    libcrypto1.1         1.1.1k-r0    CVE-2021-3711        Critical     fixed    1.1.1l-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2021-3711        Critical     fixed    1.1.1l-r0    {openssl }
apk    libcrypto1.1         1.1.1k-r0    CVE-2021-3712        High         fixed    1.1.1l-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2021-3712        High         fixed    1.1.1l-r0    {openssl }
apk    busybox              1.33.1-r3    CVE-2021-42374       Medium       fixed    1.33.1-r4    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42374       Medium       fixed    1.33.1-r4    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42375       Medium       fixed    1.33.1-r5    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42375       Medium       fixed    1.33.1-r5    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42378       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42378       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42379       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42379       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42380       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42380       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42381       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42381       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42382       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42382       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42383       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42383       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42384       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42384       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42385       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42385       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42386       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42386       High         fixed    1.33.1-r6    {busybox }
apk    libcrypto1.1         1.1.1k-r0    CVE-2022-0778        High         fixed    1.1.1n-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2022-0778        High         fixed    1.1.1n-r0    {openssl }

What would be more readable instead:

TYPE   NAME                 INSTALLED    VULN                 SEVERITY     FIXED?   FIXED IN     UPSTREAM PACKAGE
apk    busybox              1.33.1-r3    CVE-2021-42374       Medium       fixed    1.33.1-r4    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42375       Medium       fixed    1.33.1-r5    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42378       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42379       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42380       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42381       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42382       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42383       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42384       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42385       High         fixed    1.33.1-r6    {busybox }
apk    busybox              1.33.1-r3    CVE-2021-42386       High         fixed    1.33.1-r6    {busybox }
apk    libcrypto1.1         1.1.1k-r0    CVE-2021-3711        Critical     fixed    1.1.1l-r0    {openssl }
apk    libcrypto1.1         1.1.1k-r0    CVE-2021-3712        High         fixed    1.1.1l-r0    {openssl }
apk    libcrypto1.1         1.1.1k-r0    CVE-2022-0778        High         fixed    1.1.1n-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2021-3711        Critical     fixed    1.1.1l-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2021-3712        High         fixed    1.1.1l-r0    {openssl }
apk    libssl1.1            1.1.1k-r0    CVE-2022-0778        High         fixed    1.1.1n-r0    {openssl }
apk    ssl_client           1.33.1-r3    CVE-2021-42374       Medium       fixed    1.33.1-r4    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42375       Medium       fixed    1.33.1-r5    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42378       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42379       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42380       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42381       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42382       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42383       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42384       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42385       High         fixed    1.33.1-r6    {busybox }
apk    ssl_client           1.33.1-r3    CVE-2021-42386       High         fixed    1.33.1-r6    {busybox }
@sparrowt sparrowt added the bug Something isn't working label Mar 24, 2022
@spiffcs spiffcs self-assigned this Mar 30, 2022
@spiffcs
Copy link
Contributor

spiffcs commented Mar 30, 2022

Thanks @sparrowt! Let me take a look at the template presenter and see if there is an option through sprig to make this happen.

There is already a custom getLastIndex function, but I'll check if we could expose other functions to allow users to sort the collections as they see fit in the templates.

@spiffcs
Copy link
Contributor

spiffcs commented Mar 30, 2022

Just confirmed the initial comments here:
Sort by vulnerability ID happens here:

func (m ByElements) Less(i, j int) bool {
if m[i].Vulnerability.ID == m[j].Vulnerability.ID {
if m[i].Package.Name == m[j].Package.Name {
if m[i].Package.Version == m[j].Package.Version {
return m[i].Package.Type < m[j].Package.Type
}
return m[i].Package.Version < m[j].Package.Version
}
return m[i].Package.Name < m[j].Package.Name
}
return m[i].Vulnerability.ID < m[j].Vulnerability.ID
}

Sorted call happens here in NewDocument which is used by the template presenter:

func NewDocument(packages []pkg.Package, context pkg.Context, matches match.Matches, ignoredMatches []match.IgnoredMatch, metadataProvider vulnerability.MetadataProvider, appConfig interface{}, dbStatus interface{}) (Document, error) {
// we must preallocate the findings to ensure the JSON document does not show "null" when no matches are found
var findings = make([]Match, 0)
for _, m := range matches.Sorted() {
p := pkg.ByID(m.Package.ID, packages)
if p == nil {
return Document{}, fmt.Errorf("unable to find package in collection: %+v", p)
}
matchModel, err := newMatch(m, *p, metadataProvider)
if err != nil {
return Document{}, err
}
findings = append(findings, *matchModel)
}

I don't think I'll keep digging down the sprig path, but I'll see if we can get more composability here rather than have Vulnerability.ID be the default first pass for sorting.

@spiffcs spiffcs linked a pull request Mar 30, 2022 that will close this issue
@spiffcs
Copy link
Contributor

spiffcs commented Mar 30, 2022

PR linked here:
#699

@sparrowt
Copy link
Contributor Author

Wow, many thanks @spiffcs, really appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants