diff --git a/3rdpartylicenses.md b/3rdpartylicenses.md index 26f92b5..4713aa1 100644 --- a/3rdpartylicenses.md +++ b/3rdpartylicenses.md @@ -19,3 +19,5 @@ | golang.org/x/term | BSD-3-Clause | | golang.org/x/text | BSD-3-Clause | | github.com/gofrs/flock | BSD-3-Clause | +| github.com/PuerkitoBio/goquery | BSD-3-Clause | +| github.com/andybalholm/cascadia | BSD-2-Clause | diff --git a/cmd/csaf_checker/links.go b/cmd/csaf_checker/links.go new file mode 100644 index 0000000..7d7ad44 --- /dev/null +++ b/cmd/csaf_checker/links.go @@ -0,0 +1,110 @@ +// This file is Free Software under the MIT License +// without warranty, see README.md and LICENSES/MIT.txt for details. +// +// SPDX-License-Identifier: MIT +// +// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) +// Software-Engineering: 2022 Intevation GmbH + +package main + +import ( + "io" + "net/http" + "net/url" + + "github.com/PuerkitoBio/goquery" + "github.com/csaf-poc/csaf_distribution/util" +) + +type ( + pageContent struct { + err error + links map[string]struct{} + } + pages map[string]*pageContent +) + +func (pgs pages) listed(path string, pro *processor) (bool, error) { + base, err := util.BaseURL(path) + if err != nil { + return false, err + } + + content := pgs[base] + if content != nil { // already loaded + if content.err != nil { + return false, nil + } + _, ok := content.links[path] + return ok, nil + } + + baseURL, err := url.Parse(base) + if err != nil { + return false, err + } + + // load page + client := pro.httpClient() + pro.checkTLS(base) + res, err := client.Get(base) + + pro.badDirListings.use() + + if err != nil { + pro.badDirListings.add("Fetching %s failed: %v", base, err) + return false, errContinue + } + if res.StatusCode != http.StatusOK { + pro.badDirListings.add("Fetching %s failed. Status code %d (%s)", + base, res.StatusCode, res.Status) + return false, errContinue + } + + content = &pageContent{ + links: map[string]struct{}{}, + } + + pgs[base] = content + + // Build link index for this page. + + if err := func() error { + defer res.Body.Close() + return linksOnPage(res.Body, func(link string) error { + u, err := url.Parse(link) + if err != nil { + return err + } + // Links may be relative + abs := baseURL.ResolveReference(u).String() + content.links[abs] = struct{}{} + return nil + }) + }(); err != nil { + return false, errContinue + } + + _, ok := content.links[path] + return ok, nil +} + +func linksOnPage(r io.Reader, visit func(string) error) error { + + doc, err := goquery.NewDocumentFromReader(r) + if err != nil { + return err + } + + doc.Find("a").Each(func(_ int, s *goquery.Selection) { + if err != nil { + return + } + if link, ok := s.Attr("href"); ok { + err = visit(link) + } + }) + + return err +} diff --git a/cmd/csaf_checker/links_test.go b/cmd/csaf_checker/links_test.go new file mode 100644 index 0000000..96e7e62 --- /dev/null +++ b/cmd/csaf_checker/links_test.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "strings" + "testing" +) + +const page0 = ` + + Not a JSON + link0 +
    +
  1. link1
  2. +
  3. link1
  4. +
+

+

+
  • link1
  • +
    +

    + +` + +func TestLinksOnPage(t *testing.T) { + + var links []string + + err := linksOnPage( + strings.NewReader(page0), + func(s string) error { + if strings.HasSuffix(s, ".json") { + links = append(links, s) + } + return nil + }, + ) + if err != nil { + t.Fatal(err) + } + + if l := len(links); l != 4 { + t.Fatalf("Expected 4 links, go %d\n", l) + } + + for i, link := range links { + href := fmt.Sprintf("link%d.json", i) + if href != link { + t.Fatalf("Expected link '%s', got '%s'\n", href, link) + } + } +} diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index fd2e0a4..d0ca72d 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -59,6 +59,7 @@ type processor struct { badFolders topicMessages badWellknownMetadata topicMessages badDNSPath topicMessages + badDirListings topicMessages expr *util.PathEval } @@ -85,6 +86,8 @@ const ( rolieChangesMask indexMask changesMask + listingMask + rolieListingMask ) func (wt whereType) String() string { @@ -99,6 +102,10 @@ func (wt whereType) String() string { return "index.txt" case changesMask: return "changes.csv" + case listingMask: + return "directory listing" + case rolieListingMask: + return "directory listing [ROLIE]" default: var mixed []string for mask := rolieMask; mask <= changesMask; mask <<= 1 { @@ -157,6 +164,10 @@ func (p *processor) clean() { p.badSecurity.reset() p.badIndices.reset() p.badChanges.reset() + p.badFolders.reset() + p.badWellknownMetadata.reset() + p.badDNSPath.reset() + p.badDirListings.reset() } // run calls checkDomain function for each domain in the given "domains" parameter. @@ -193,6 +204,7 @@ func (p *processor) checkDomain(domain string) error { (*processor).checkSecurity, (*processor).checkCSAFs, (*processor).checkMissing, + (*processor).checkListing, (*processor).checkWellknownMetadataReporter, (*processor).checkDNSPathReporter, } { @@ -241,7 +253,7 @@ func (p *processor) checkRedirect(r *http.Request, via []*http.Request) error { p.redirects[url] = path.String() if len(via) > 10 { - return errors.New("Too many redirections") + return errors.New("too many redirections") } return nil } @@ -696,7 +708,7 @@ func (p *processor) checkMissing(string) error { for _, f := range files { v := p.alreadyChecked[f] var where []string - for mask := rolieMask; mask <= changesMask; mask <<= 1 { + for mask := rolieMask; mask <= rolieListingMask; mask <<= 1 { if maxMask&mask == mask { var in string if v&mask == mask { @@ -712,6 +724,35 @@ func (p *processor) checkMissing(string) error { return nil } +// checkListing wents over all found adivisories URLs and checks, +// if their parent directory is listable. +func (p *processor) checkListing(string) error { + + p.badDirListings.use() + + pgs := pages{} + + var unlisted []string + + for f := range p.alreadyChecked { + found, err := pgs.listed(f, p) + if err != nil && err != errContinue { + return err + } + if !found { + unlisted = append(unlisted, f) + } + } + + if len(unlisted) > 0 { + sort.Strings(unlisted) + p.badDirListings.add("Not listed advisories: %s", + strings.Join(unlisted, ", ")) + } + + return nil +} + var providerMetadataLocations = [...]string{ ".well-known/csaf", "security/data/csaf", @@ -803,7 +844,7 @@ func (p *processor) extractProviderURL(r io.Reader) (string, error) { return "", err } if len(urls) == 0 { - return "", errors.New("No provider-metadata.json found") + return "", errors.New("no provider-metadata.json found") } if len(urls) > 1 { diff --git a/cmd/csaf_checker/reporters.go b/cmd/csaf_checker/reporters.go index 5ec824a..d9a89c8 100644 --- a/cmd/csaf_checker/reporters.go +++ b/cmd/csaf_checker/reporters.go @@ -186,10 +186,17 @@ func (r *changesReporter) report(p *processor, domain *Domain) { req.Messages = p.badChanges } -func (r *directoryListingsReporter) report(_ *processor, domain *Domain) { - // TODO: Implement me! +func (r *directoryListingsReporter) report(p *processor, domain *Domain) { req := r.requirement(domain) - _ = req + if !p.badDirListings.used() { + req.message("No directory listings checked.") + return + } + if len(p.badDirListings) == 0 { + req.message("All directory listings are valid.") + return + } + req.Messages = p.badDirListings } func (r *integrityReporter) report(p *processor, domain *Domain) { diff --git a/go.mod b/go.mod index e06f252..71832f8 100644 --- a/go.mod +++ b/go.mod @@ -3,25 +3,26 @@ module github.com/csaf-poc/csaf_distribution go 1.17 require ( - github.com/BurntSushi/toml v0.4.1 + github.com/BurntSushi/toml v1.1.0 github.com/PaesslerAG/gval v1.1.2 github.com/PaesslerAG/jsonpath v0.1.1 - github.com/ProtonMail/gopenpgp/v2 v2.3.0 + github.com/ProtonMail/gopenpgp/v2 v2.4.7 + github.com/PuerkitoBio/goquery v1.8.0 github.com/gofrs/flock v0.8.1 github.com/jessevdk/go-flags v1.5.0 github.com/mitchellh/go-homedir v1.1.0 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 - golang.org/x/crypto v0.0.0-20211202192323-5770296d904e - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 + golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 + golang.org/x/time v0.0.0-20220411224347-583f2d630306 ) require ( - github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect - github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220512085406-902f79d34c9f // indirect + github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.4.2 // indirect - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect + golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect + golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index ab36a84..dd9c7a9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= github.com/PaesslerAG/gval v1.1.2 h1:EROKxV4/fAKWb0Qoj7NOxmHZA7gcpjOV9XgiRZMRCUU= @@ -7,12 +7,18 @@ github.com/PaesslerAG/gval v1.1.2/go.mod h1:Fa8gfkCmUsELXgayr8sfL/sw+VzCVoa03dcO github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk= github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= -github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g= -github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4= -github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= -github.com/ProtonMail/gopenpgp/v2 v2.3.0 h1:eniutitHk02Yn3GtaDfJTVm/Ca1e8s6zkS0SpeaocXI= -github.com/ProtonMail/gopenpgp/v2 v2.3.0/go.mod h1:F62x0m3akQuisX36pOgAtKOHZ1E7/MpnX8bZWCK+5dA= +github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20220512085406-902f79d34c9f h1:CP4ZCltXExnPiLQFuj0oucZxRRH5ZLL+lT5Bl7Eh37g= +github.com/ProtonMail/go-crypto v0.0.0-20220512085406-902f79d34c9f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= +github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc= +github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= +github.com/ProtonMail/gopenpgp/v2 v2.4.7 h1:V3xeelvXgJiZXZuPtSSE+uYbtPw4RmbmyPqXDAESPhg= +github.com/ProtonMail/gopenpgp/v2 v2.4.7/go.mod h1:ZW1KxHNG6q5LMgFKf9Ap/d2eVYeyGf5+fAUEAjJWtmo= +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -20,7 +26,6 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -30,7 +35,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -41,8 +45,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -55,6 +59,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -63,17 +69,20 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= +golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=