From 6b9ecead89c5b40e86928c6e7f416903e0a495e1 Mon Sep 17 00:00:00 2001 From: tschmidtb51 <65305130+tschmidtb51@users.noreply.github.com> Date: Sat, 24 Sep 2022 18:53:03 +0200 Subject: [PATCH] refactor: remove temporary solution joinUrlPath and use joinPath from Go 1.19.1 net/url --- cmd/csaf_checker/processor.go | 13 +- csaf/advisories.go | 4 +- util/joinpath.go | 301 ---------------------------------- util/joinpath_go119.go | 20 --- 4 files changed, 9 insertions(+), 329 deletions(-) delete mode 100644 util/joinpath.go delete mode 100644 util/joinpath_go119.go diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 53993c04..e92ccb21 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -392,7 +392,7 @@ func (p *processor) integrity( if u.IsAbs() { return u } - return util.JoinURLPath(b, u.String()) + return b.JoinPath(u.String()) } for _, f := range files { @@ -715,7 +715,7 @@ func (p *processor) checkIndex(base string, mask whereType) error { return err } - index := util.JoinURLPath(bu, "index.txt").String() + index := bu.JoinPath("index.txt").String() p.checkTLS(index) @@ -773,7 +773,7 @@ func (p *processor) checkChanges(base string, mask whereType) error { if err != nil { return err } - changes := util.JoinURLPath(bu, "changes.csv").String() + changes := bu.JoinPath("changes.csv").String() p.checkTLS(changes) @@ -1195,11 +1195,12 @@ func (p *processor) checkWellknown(domain string) string { // 1. checks if the provider-metadata.json file is // available under the /.well-known/csaf/ directory. // 2. Then it checks the security.txt file by making HTTP request to fetch it. -// 3. After that it checks the existence of the CSAF field in the file -// content and tries to fetch the value of this field. +// 3. After that it checks the existence of the CSAF field in the file +// content and tries to fetch the value of this field. // 4. Finally it checks if the "csaf.data.security.domain.tld" DNS record // is available and serves the "provider-metadata.json". -/// +// +// / // If all three checks fail, errors are given, // otherwise warnings for all failed checks. // The function returns nil, unless errors outside the checks were found. diff --git a/csaf/advisories.go b/csaf/advisories.go index 66f713b2..a640f153 100644 --- a/csaf/advisories.go +++ b/csaf/advisories.go @@ -198,7 +198,7 @@ func (afp *AdvisoryFileProcessor) loadIndex( return nil, err } - indexURL := util.JoinURLPath(base, "index.txt").String() + indexURL := base.JoinPath("index.txt").String() resp, err := afp.client.Get(indexURL) if err != nil { return nil, err @@ -215,7 +215,7 @@ func (afp *AdvisoryFileProcessor) loadIndex( continue } files = append(files, - PlainAdvisoryFile(util.JoinURLPath(base, u).String())) + PlainAdvisoryFile(base.JoinPath(u).String())) } if err := scanner.Err(); err != nil { diff --git a/util/joinpath.go b/util/joinpath.go deleted file mode 100644 index 9016e5ac..00000000 --- a/util/joinpath.go +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Go119-BSD-Patentgrant -// SPDX-FileCopyrightText: 2009 The Go Authors, Google Inc. - -// The code of this file was extracted and adjusted from -// https://cs.opensource.google/go/go/+/refs/tags/go1.19rc2:src/net/url/url.go -// by Intevation 2022 - -//go:build !go1.19 - -package util - -import ( - "net/url" - "path" - "strings" -) - -type encoding int - -const ( - encodePath encoding = 1 + iota - encodePathSegment - encodeHost - encodeZone - encodeUserPassword - encodeQueryComponent - encodeFragment -) - -const upperhex = "0123456789ABCDEF" - -func ishex(c byte) bool { - switch { - case '0' <= c && c <= '9': - return true - case 'a' <= c && c <= 'f': - return true - case 'A' <= c && c <= 'F': - return true - } - return false -} - -func unhex(c byte) byte { - switch { - case '0' <= c && c <= '9': - return c - '0' - case 'a' <= c && c <= 'f': - return c - 'a' + 10 - case 'A' <= c && c <= 'F': - return c - 'A' + 10 - } - return 0 -} - -// Return true if the specified character should be escaped when -// appearing in a URL string, according to RFC 3986. -// -// Please be informed that for now shouldEscape does not check all -// reserved characters correctly. See golang.org/issue/5684. -func shouldEscape(c byte, mode encoding) bool { - // §2.3 Unreserved characters (alphanum) - if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { - return false - } - - if mode == encodeHost || mode == encodeZone { - // §3.2.2 Host allows - // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" - // as part of reg-name. - // We add : because we include :port as part of host. - // We add [ ] because we include [ipv6]:port as part of host. - // We add < > because they're the only characters left that - // we could possibly allow, and Parse will reject them if we - // escape them (because hosts can't use %-encoding for - // ASCII bytes). - switch c { - case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"': - return false - } - } - - switch c { - case '-', '_', '.', '~': // §2.3 Unreserved characters (mark) - return false - - case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved) - // Different sections of the URL allow a few of - // the reserved characters to appear unescaped. - switch mode { - case encodePath: // §3.3 - // The RFC allows : @ & = + $ but saves / ; , for assigning - // meaning to individual path segments. This package - // only manipulates the path as a whole, so we allow those - // last three as well. That leaves only ? to escape. - return c == '?' - - case encodePathSegment: // §3.3 - // The RFC allows : @ & = + $ but saves / ; , for assigning - // meaning to individual path segments. - return c == '/' || c == ';' || c == ',' || c == '?' - - case encodeUserPassword: // §3.2.1 - // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in - // userinfo, so we must escape only '@', '/', and '?'. - // The parsing of userinfo treats ':' as special so we must escape - // that too. - return c == '@' || c == '/' || c == '?' || c == ':' - - case encodeQueryComponent: // §3.4 - // The RFC reserves (so we must escape) everything. - return true - - case encodeFragment: // §4.1 - // The RFC text is silent but the grammar allows - // everything, so escape nothing. - return false - } - } - - if mode == encodeFragment { - // RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are - // included in reserved from RFC 2396 §2.2. The remaining sub-delims do not - // need to be escaped. To minimize potential breakage, we apply two restrictions: - // (1) we always escape sub-delims outside of the fragment, and (2) we always - // escape single quote to avoid breaking callers that had previously assumed that - // single quotes would be escaped. See issue #19917. - switch c { - case '!', '(', ')', '*': - return false - } - } - - // Everything else must be escaped. - return true -} - -// unescape unescapes a string; the mode specifies -// which section of the URL string is being unescaped. -func unescape(s string, mode encoding) (string, error) { - // Count %, check that they're well-formed. - n := 0 - hasPlus := false - for i := 0; i < len(s); { - switch s[i] { - case '%': - n++ - if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { - s = s[i:] - if len(s) > 3 { - s = s[:3] - } - return "", url.EscapeError(s) - } - // Per https://tools.ietf.org/html/rfc3986#page-21 - // in the host component %-encoding can only be used - // for non-ASCII bytes. - // But https://tools.ietf.org/html/rfc6874#section-2 - // introduces %25 being allowed to escape a percent sign - // in IPv6 scoped-address literals. Yay. - if mode == encodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" { - return "", url.EscapeError(s[i : i+3]) - } - if mode == encodeZone { - // RFC 6874 says basically "anything goes" for zone identifiers - // and that even non-ASCII can be redundantly escaped, - // but it seems prudent to restrict %-escaped bytes here to those - // that are valid host name bytes in their unescaped form. - // That is, you can use escaping in the zone identifier but not - // to introduce bytes you couldn't just write directly. - // But Windows puts spaces here! Yay. - v := unhex(s[i+1])<<4 | unhex(s[i+2]) - if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, encodeHost) { - return "", url.EscapeError(s[i : i+3]) - } - } - i += 3 - case '+': - hasPlus = mode == encodeQueryComponent - i++ - default: - if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) { - return "", url.InvalidHostError(s[i : i+1]) - } - i++ - } - } - - if n == 0 && !hasPlus { - return s, nil - } - - var t strings.Builder - t.Grow(len(s) - 2*n) - for i := 0; i < len(s); i++ { - switch s[i] { - case '%': - t.WriteByte(unhex(s[i+1])<<4 | unhex(s[i+2])) - i += 2 - case '+': - if mode == encodeQueryComponent { - t.WriteByte(' ') - } else { - t.WriteByte('+') - } - default: - t.WriteByte(s[i]) - } - } - return t.String(), nil -} - -func escape(s string, mode encoding) string { - spaceCount, hexCount := 0, 0 - for i := 0; i < len(s); i++ { - c := s[i] - if shouldEscape(c, mode) { - if c == ' ' && mode == encodeQueryComponent { - spaceCount++ - } else { - hexCount++ - } - } - } - - if spaceCount == 0 && hexCount == 0 { - return s - } - - var buf [64]byte - var t []byte - - required := len(s) + 2*hexCount - if required <= len(buf) { - t = buf[:required] - } else { - t = make([]byte, required) - } - - if hexCount == 0 { - copy(t, s) - for i := 0; i < len(s); i++ { - if s[i] == ' ' { - t[i] = '+' - } - } - return string(t) - } - - j := 0 - for i := 0; i < len(s); i++ { - switch c := s[i]; { - case c == ' ' && mode == encodeQueryComponent: - t[j] = '+' - j++ - case shouldEscape(c, mode): - t[j] = '%' - t[j+1] = upperhex[c>>4] - t[j+2] = upperhex[c&15] - j += 3 - default: - t[j] = s[i] - j++ - } - } - return string(t) -} - -func setPath(u *url.URL, p string) error { - path, err := unescape(p, encodePath) - if err != nil { - return err - } - u.Path = path - if escp := escape(path, encodePath); p == escp { - // Default encoding is fine. - u.RawPath = "" - } else { - u.RawPath = p - } - return nil -} - -// JoinURLPath returns a new URL with the provided path elements joined to -// any existing path and the resulting path cleaned of any ./ or ../ elements. -// Any sequences of multiple / characters will be reduced to a single /. -func JoinURLPath(u *url.URL, elem ...string) *url.URL { - - url := *u - if len(elem) > 0 { - elem = append([]string{u.EscapedPath()}, elem...) - p := path.Join(elem...) - // path.Join will remove any trailing slashes. - // Preserve at least one. - if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") { - p += "/" - } - setPath(&url, p) - } - return &url -} diff --git a/util/joinpath_go119.go b/util/joinpath_go119.go deleted file mode 100644 index 96262b72..00000000 --- a/util/joinpath_go119.go +++ /dev/null @@ -1,20 +0,0 @@ -// 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 - -//go:build go1.19 - -package util - -import "net/url" - -// JoinURLPath returns a new URL with the provided path elements joined to -// any existing path and the resulting path cleaned of any ./ or ../ elements. -// Any sequences of multiple / characters will be reduced to a single /. -func JoinURLPath(u *url.URL, elem ...string) *url.URL { - return u.JoinPath(elem...) -}