internal/vulns: add affected symbols
Only exported symbols are reported.
In case there are no exported symbols, we present as if all symbols in
the package are completely vulnerable.
Updates golang/go#54812
Change-Id: I4555af8f27ae50fcb1a9e3b9e1c2ec29e750a9ad
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/429678
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/vulns/vulns.go b/internal/vulns/vulns.go
index 2190269..9885196 100644
--- a/internal/vulns/vulns.go
+++ b/internal/vulns/vulns.go
@@ -8,6 +8,7 @@
import (
"context"
"fmt"
+ "go/token"
"strings"
"golang.org/x/mod/semver"
@@ -65,6 +66,9 @@
type AffectedPackage struct {
PackagePath string
Versions string
+ // List of exported affected symbols. Empty list
+ // implies all symbols in the package are affected.
+ Symbols []string
}
// OSVEntry holds an OSV entry and provides additional methods.
@@ -203,8 +207,26 @@
affs = append(affs, &AffectedPackage{
PackagePath: p.Path,
Versions: strings.Join(vs, ", "),
+ Symbols: exportedSymbols(p.Symbols),
+ // TODO(hyangah): where to place GOOS/GOARCH info
})
}
}
return affs
}
+
+func exportedSymbols(in []string) []string {
+ var out []string
+ for _, s := range in {
+ exported := true
+ for _, part := range strings.Split(s, ".") {
+ if !token.IsExported(part) {
+ exported = false // exported only all parts in the symbol name are exported.
+ }
+ }
+ if exported {
+ out = append(out, s)
+ }
+ }
+ return out
+}
diff --git a/internal/vulns/vulns_test.go b/internal/vulns/vulns_test.go
index 2b3d249..1007c7d 100644
--- a/internal/vulns/vulns_test.go
+++ b/internal/vulns/vulns_test.go
@@ -11,6 +11,7 @@
"testing"
"github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"golang.org/x/vuln/osv"
)
@@ -86,7 +87,7 @@
}
-func TestAffectedPackages(t *testing.T) {
+func TestAffectedPackages_Versions(t *testing.T) {
for _, test := range []struct {
name string
in []osv.RangeEvent
@@ -139,3 +140,109 @@
})
}
}
+
+func TestAffectedPackagesPackagesSymbols(t *testing.T) {
+ tests := []struct {
+ name string
+ in *osv.Entry
+ want []*AffectedPackage
+ }{
+ {
+ name: "one symbol",
+ in: &osv.Entry{
+ ID: "GO-2022-01",
+ Affected: []osv.Affected{{
+ Package: osv.Package{Name: "example.com/mod"},
+ EcosystemSpecific: osv.EcosystemSpecific{
+ Imports: []osv.EcosystemSpecificImport{{
+ Path: "example.com/mod/pkg",
+ Symbols: []string{"F"},
+ }},
+ },
+ }},
+ },
+ want: []*AffectedPackage{{
+ PackagePath: "example.com/mod/pkg",
+ Symbols: []string{"F"},
+ }},
+ },
+ {
+ name: "multiple symbols",
+ in: &osv.Entry{
+ ID: "GO-2022-02",
+ Affected: []osv.Affected{{
+ Package: osv.Package{Name: "example.com/mod"},
+ EcosystemSpecific: osv.EcosystemSpecific{
+ Imports: []osv.EcosystemSpecificImport{{
+ Path: "example.com/mod/pkg",
+ Symbols: []string{"F", "g", "S.f", "S.F", "s.F", "s.f"},
+ }},
+ },
+ }},
+ },
+ want: []*AffectedPackage{{
+ PackagePath: "example.com/mod/pkg",
+ Symbols: []string{"F", "S.F"}, // unexported symbols are excluded.
+ }},
+ },
+ {
+ name: "no symbol",
+ in: &osv.Entry{
+ ID: "GO-2022-03",
+ Affected: []osv.Affected{{
+ Package: osv.Package{Name: "example.com/mod"},
+ EcosystemSpecific: osv.EcosystemSpecific{
+ Imports: []osv.EcosystemSpecificImport{{
+ Path: "example.com/mod/pkg",
+ }},
+ },
+ }},
+ },
+ want: []*AffectedPackage{{
+ PackagePath: "example.com/mod/pkg",
+ }},
+ },
+ {
+ name: "multiple pkgs and modules",
+ in: &osv.Entry{
+ ID: "GO-2022-04",
+ Affected: []osv.Affected{{
+ Package: osv.Package{Name: "example.com/mod1"},
+ EcosystemSpecific: osv.EcosystemSpecific{
+ Imports: []osv.EcosystemSpecificImport{{
+ Path: "example.com/mod1/pkg1",
+ }, {
+ Path: "example.com/mod1/pkg2",
+ Symbols: []string{"F"},
+ }},
+ },
+ }, {
+ Package: osv.Package{Name: "example.com/mod2"},
+ EcosystemSpecific: osv.EcosystemSpecific{
+ Imports: []osv.EcosystemSpecificImport{{
+ Path: "example.com/mod2/pkg3",
+ Symbols: []string{"g", "H"},
+ }},
+ },
+ }},
+ },
+ want: []*AffectedPackage{{
+ PackagePath: "example.com/mod1/pkg1",
+ }, {
+ PackagePath: "example.com/mod1/pkg2",
+ Symbols: []string{"F"},
+ }, {
+ PackagePath: "example.com/mod2/pkg3",
+ Symbols: []string{"H"},
+ }},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := AffectedPackages(tt.in)
+ if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreUnexported(AffectedPackage{})); diff != "" {
+ t.Errorf("mismatch (-want, +got):\n%s", diff)
+ }
+ })
+ }
+}
diff --git a/static/frontend/vuln/entry/entry.css b/static/frontend/vuln/entry/entry.css
index 394bbed..46eab8e 100644
--- a/static/frontend/vuln/entry/entry.css
+++ b/static/frontend/vuln/entry/entry.css
@@ -17,26 +17,68 @@
.VulnEntry h2 {
font-size: 1.25rem;
}
-.VulnEntry-table {
- margin-bottom: 0.5rem;
- text-align: left;
- width: 100%;
+.VulnEntryPackages-attr {
+ /* package and symbol names can be pretty long */
+ overflow-wrap: anywhere;
}
-.VulnEntry-table thead {
- background-color: var(--color-background-accented);
+/* One column by default */
+.VulnEntryPackages-container {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-gap: 0.5rem;
}
-.VulnEntry-table tbody {
- word-break: break-all;
+/* Don't display the first item - the headers for multi-col layout */
+.VulnEntryPackages-container>li:first-child {
+ display: none;
}
-.VulnEntry-table td,
-.VulnEntry-table th {
- padding: 0.75rem 1rem;
+.VulnEntryPackages-attr::before {
+ content: attr(data-name);
+ color: var(--color-text-subtle);
}
-.VulnEntry-table tbody > tr {
- border-bottom: var(--border);
+/* Attribute name for first column, and attribute value for second column. */
+.VulnEntryPackages-attr {
+ display: grid;
+ grid-template-columns: minmax(5em, 10%) 1fr;
+ padding: 0.2rem;
+}
+/* Three columns for wider screen */
+@media screen and (min-width: 46rem) {
+ /* Undo what's done by default */
+ .VulnEntryPackages-container {
+ grid-gap: 0;
+ }
+ .VulnEntryPackages-item {
+ padding: inherit;
+ }
+ .VulnEntryPackages-container>li:first-child {
+ display: grid; /* undo display: none setfor default */
+ }
+ .VulnEntryPackages-attr::before {
+ content: none;
+ }
+ .VulnEntryPackages-attr {
+ grid-template-columns: 1fr;
+ }
+ .VulnEntryPackages-item-container {
+ display: grid;
+ grid-template-columns: minmax(10em, 50%) minmax(5em, 20%) 1fr;
+ padding: 0.5rem;
+ }
+ /* Header */
+ .VulnEntryPackages-item-container:first-child {
+ background-color: var(--color-background-accented);
+ }
+ /* Header text */
+ .VulnEntryPackages-item-container:first-child .VulnEntryPackages-attr {
+ display: flex;
+ text-overflow: initial;
+ overflow: auto;
+ white-space: normal;
+ font-weight: bold;
+ }
}
.VulnEntry-referenceList,
.VulnEntry-aliases {
line-height: 1.75rem;
word-break: break-all;
-}
+}
\ No newline at end of file
diff --git a/static/frontend/vuln/entry/entry.min.css b/static/frontend/vuln/entry/entry.min.css
index 5b6deda..ae4a973 100644
--- a/static/frontend/vuln/entry/entry.min.css
+++ b/static/frontend/vuln/entry/entry.min.css
@@ -3,5 +3,5 @@
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
-.Vuln-alias{display:none}.VulnEntry{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.VulnEntry h2{font-size:1.25rem}.VulnEntry-table{margin-bottom:.5rem;text-align:left;width:100%}.VulnEntry-table thead{background-color:var(--color-background-accented)}.VulnEntry-table tbody{word-break:break-all}.VulnEntry-table td,.VulnEntry-table th{padding:.75rem 1rem}.VulnEntry-table tbody>tr{border-bottom:var(--border)}.VulnEntry-referenceList,.VulnEntry-aliases{line-height:1.75rem;word-break:break-all}
+.Vuln-alias{display:none}.VulnEntry{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.VulnEntry h2{font-size:1.25rem}.VulnEntryPackages-attr{overflow-wrap:anywhere}.VulnEntryPackages-container{display:grid;grid-template-columns:1fr;grid-gap:.5rem}.VulnEntryPackages-container>li:first-child{display:none}.VulnEntryPackages-attr:before{content:attr(data-name);color:var(--color-text-subtle)}.VulnEntryPackages-attr{display:grid;grid-template-columns:minmax(5em,10%) 1fr;padding:.2rem}@media screen and (min-width: 46rem){.VulnEntryPackages-container{grid-gap:0}.VulnEntryPackages-item{padding:inherit}.VulnEntryPackages-container>li:first-child{display:grid}.VulnEntryPackages-attr:before{content:none}.VulnEntryPackages-attr{grid-template-columns:1fr}.VulnEntryPackages-item-container{display:grid;grid-template-columns:minmax(10em,50%) minmax(5em,20%) 1fr;padding:.5rem}.VulnEntryPackages-item-container:first-child{background-color:var(--color-background-accented)}.VulnEntryPackages-item-container:first-child .VulnEntryPackages-attr{display:flex;text-overflow:initial;overflow:auto;white-space:normal;font-weight:700}}.VulnEntry-referenceList,.VulnEntry-aliases{line-height:1.75rem;word-break:break-all}
/*# sourceMappingURL=entry.min.css.map */
diff --git a/static/frontend/vuln/entry/entry.min.css.map b/static/frontend/vuln/entry/entry.min.css.map
index eb9273d..26204a2 100644
--- a/static/frontend/vuln/entry/entry.min.css.map
+++ b/static/frontend/vuln/entry/entry.min.css.map
@@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["entry.css"],
- "sourcesContent": ["/*\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n.Vuln-alias {\n display: none;\n}\n\n.VulnEntry {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n margin-top: 0.5rem;\n}\n.VulnEntry h2 {\n font-size: 1.25rem;\n}\n.VulnEntry-table {\n margin-bottom: 0.5rem;\n text-align: left;\n width: 100%;\n}\n.VulnEntry-table thead {\n background-color: var(--color-background-accented);\n}\n.VulnEntry-table tbody {\n word-break: break-all;\n}\n.VulnEntry-table td,\n.VulnEntry-table th {\n padding: 0.75rem 1rem;\n}\n.VulnEntry-table tbody > tr {\n border-bottom: var(--border);\n}\n.VulnEntry-referenceList,\n.VulnEntry-aliases {\n line-height: 1.75rem;\n word-break: break-all;\n}\n"],
- "mappings": ";;;;;AAMA,YACE,aAGF,WACE,aACA,sBACA,SACA,iBAEF,cACE,kBAEF,iBACE,oBACA,gBACA,WAEF,uBACE,kDAEF,uBACE,qBAEF,wCA9BA,oBAkCA,0BACE,4BAEF,4CAEE,oBACA",
+ "sourcesContent": ["/*\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n.Vuln-alias {\n display: none;\n}\n\n.VulnEntry {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n margin-top: 0.5rem;\n}\n.VulnEntry h2 {\n font-size: 1.25rem;\n}\n.VulnEntryPackages-attr {\n /* package and symbol names can be pretty long */\n overflow-wrap: anywhere;\n}\n/* One column by default */\n.VulnEntryPackages-container {\n display: grid;\n grid-template-columns: 1fr;\n grid-gap: 0.5rem;\n}\n/* Don't display the first item - the headers for multi-col layout */\n.VulnEntryPackages-container>li:first-child {\n display: none;\n}\n.VulnEntryPackages-attr::before {\n content: attr(data-name);\n color: var(--color-text-subtle);\n}\n/* Attribute name for first column, and attribute value for second column. */\n.VulnEntryPackages-attr {\n display: grid;\n grid-template-columns: minmax(5em, 10%) 1fr;\n padding: 0.2rem;\n}\n/* Three columns for wider screen */\n@media screen and (min-width: 46rem) {\n /* Undo what's done by default */\n .VulnEntryPackages-container {\n grid-gap: 0;\n }\n .VulnEntryPackages-item {\n padding: inherit;\n }\n .VulnEntryPackages-container>li:first-child {\n display: grid; /* undo display: none setfor default */\n }\n .VulnEntryPackages-attr::before {\n content: none;\n }\n .VulnEntryPackages-attr {\n grid-template-columns: 1fr;\n }\n .VulnEntryPackages-item-container {\n display: grid;\n grid-template-columns: minmax(10em, 50%) minmax(5em, 20%) 1fr;\n padding: 0.5rem;\n }\n /* Header */\n .VulnEntryPackages-item-container:first-child {\n background-color: var(--color-background-accented);\n }\n /* Header text */\n .VulnEntryPackages-item-container:first-child .VulnEntryPackages-attr {\n display: flex;\n text-overflow: initial;\n overflow: auto;\n white-space: normal;\n font-weight: bold;\n }\n}\n.VulnEntry-referenceList,\n.VulnEntry-aliases {\n line-height: 1.75rem;\n word-break: break-all;\n}"],
+ "mappings": ";;;;;AAMA,YACE,aAGF,WACE,aACA,sBACA,SACA,iBAEF,cACE,kBAEF,wBAEE,uBAGF,6BACE,aACA,0BACA,eAGF,4CACE,aAEF,+BACE,wBACA,+BAGF,wBACE,aACA,0CAxCF,cA4CA,qCAEE,6BACE,WAEF,wBACE,gBAEF,4CACE,aAEF,+BACE,aAEF,wBACE,0BAEF,kCACE,aACA,2DA/DJ,cAmEE,8CACE,kDAGF,sEACE,aACA,sBACA,cACA,mBACA,iBAGJ,4CAEE,oBACA",
"names": []
}
diff --git a/static/frontend/vuln/entry/entry.tmpl b/static/frontend/vuln/entry/entry.tmpl
index 7ffdd15..6250707 100644
--- a/static/frontend/vuln/entry/entry.tmpl
+++ b/static/frontend/vuln/entry/entry.tmpl
@@ -45,22 +45,26 @@
{{define "affected"}}
<h2>Affected Packages</h2>
- <table class="VulnEntry-table">
- <thead>
- <tr>
- <th>Package</th>
- <th>Affected Versions</th>
- </tr>
- </thead>
- <tbody>
- {{range .}}
- <tr>
- <td><a href="/{{.PackagePath}}">{{.PackagePath}}</a></td>
- <td>{{if .Versions}}{{.Versions}}{{else}}all versions, no known fixed{{end}}</td>
- </tr>
- {{end}}
- </tbody>
- </table>
+ <ul class="VulnEntryPackages VulnEntryPackages-container">
+ <li class="VulnEntryPackages-item VulnEntryPackages-item-container">
+ <div class="VulnEntryPackages-attr">Path</div>
+ <div class="VulnEntryPackages-attr">Versions</div>
+ <div class="VulnEntryPackages-attr">Symbols</div>
+ </li>
+ {{range .}}
+ <li class="VulnEntryPackages-item VulnEntryPackages-item-container">
+ <div class="VulnEntryPackages-attr" data-name="Path"><a href="/{{.PackagePath}}">{{.PackagePath}}</a></div>
+ <div class="VulnEntryPackages-attr" data-name="Versions">{{if .Versions}}{{.Versions}}{{else}}all versions, no known fixed{{end}}</div>
+ <div class="VulnEntryPackages-attr VulnEntryPackages-symbols" data-name="Symbols">
+ {{if .Symbols}}{{ $length := len .Symbols}}
+ {{if lt $length 5}}<ul>{{range .Symbols}}<li>{{.}}</li>{{end}}</ul>
+ {{else}}<details><summary>{{len .Symbols}} affected symbols</summary><ul>{{range .Symbols}}<li>{{.}}</li>{{end}}</ul></details>
+ {{end}}
+ {{else}}all symbols{{end}}
+ </div>
+ </li>
+ {{end}}
+ </ul>
{{end}}
{{define "entry"}}
@@ -90,4 +94,4 @@
Suggest an edit to this report.
</a>
</div>
-{{end}}
+{{end}}
\ No newline at end of file