ipn/ipnlocal: simplify redactErr ()

Use multierr.Range to iterate through an error tree
instead of multiple invocations of errors.As.
This scales better as we add more Go error types to the switch.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
Joe Tsai 2022-12-12 17:51:03 -08:00 committed by GitHub
parent c47578b528
commit bd2995c14b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -48,6 +48,7 @@ import (
"tailscale.com/net/netutil" "tailscale.com/net/netutil"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/util/clientmetric" "tailscale.com/util/clientmetric"
"tailscale.com/util/multierr"
"tailscale.com/util/strs" "tailscale.com/util/strs"
"tailscale.com/wgengine" "tailscale.com/wgengine"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
@ -365,58 +366,45 @@ func redactString(s string) string {
return string(b) return string(b)
} }
func redactErr(err error) error { func redactErr(root error) error {
// redactStrings is a list of sensitive strings that were redacted.
// It is not sufficient to just snub out sensitive fields in Go errors
// since some wrapper errors like fmt.Errorf pre-cache the error string,
// which would unfortunately remain unaffected.
var redactStrings []string var redactStrings []string
var pe *os.PathError // Redact sensitive fields in known Go error types.
if errors.As(err, &pe) { var unknownErrors int
// If this is the root error, then we can redact it directly. multierr.Range(root, func(err error) bool {
if err == pe { switch err := err.(type) {
pe.Path = redactString(pe.Path) case *os.PathError:
return pe redactStrings = append(redactStrings, err.Path)
err.Path = redactString(err.Path)
case *os.LinkError:
redactStrings = append(redactStrings, err.New, err.Old)
err.New = redactString(err.New)
err.Old = redactString(err.Old)
default:
unknownErrors++
} }
return true
})
// Otherwise, we have a *PathError somewhere in the error // If there are no redacted strings or no unknown error types,
// chain, and we can't redact it because something later in the // then we can return the possibly modified root error verbatim.
// chain may have cached the Error() return already (as // Otherwise, we must replace redacted strings from any wrappers.
// fmt.Errorf does). if len(redactStrings) == 0 || unknownErrors == 0 {
// return root
// Add this path to the set of paths that we will redact, below.
redactStrings = append(redactStrings, pe.Path)
// Also redact the Path value so that anything that calls
// Unwrap in the future gets the redacted value.
pe.Path = redactString(pe.Path)
} }
var le *os.LinkError // Stringify and replace any paths that we found above, then return
if errors.As(err, &le) {
// If this is the root error, then we can redact it directly.
if err == le {
le.New = redactString(le.New)
le.Old = redactString(le.Old)
return le
}
// As above
redactStrings = append(redactStrings, le.New, le.Old)
le.New = redactString(le.New)
le.Old = redactString(le.Old)
}
if len(redactStrings) == 0 {
return err
}
s := err.Error()
for _, toRedact := range redactStrings {
s = strings.Replace(s, toRedact, redactString(toRedact), -1)
}
// Stringify and and replace any paths that we found above, then return
// the error wrapped in a type that uses the newly-redacted string // the error wrapped in a type that uses the newly-redacted string
// while also allowing Unwrap()-ing to the inner error type(s). // while also allowing Unwrap()-ing to the inner error type(s).
return &redactedErr{msg: s, inner: err} s := root.Error()
for _, toRedact := range redactStrings {
s = strings.ReplaceAll(s, toRedact, redactString(toRedact))
}
return &redactedErr{msg: s, inner: root}
} }
func touchFile(path string) error { func touchFile(path string) error {