Skip to content

Commit b0f54ae

Browse files
authored
Merge pull request #48 from knz/20200901-redact
2 parents 7540597 + 690e8f7 commit b0f54ae

23 files changed

+5671
-2381
lines changed

errbase/format_error_internal_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ func TestPrintEntryRedactable(t *testing.T) {
8787
// characters; they get enclosed in redaction markers in the final output.
8888
{formatEntry{}, ""},
8989
{formatEntry{head: b("abc")}, " " + q("abc")},
90-
{formatEntry{head: b("abc\nxyz")}, " " + q("abc\nxyz")},
90+
{formatEntry{head: b("abc\nxyz")}, " " + q("abc") + "\n" + q("xyz")},
9191
{formatEntry{details: b("def")}, " " + q("def")},
92-
{formatEntry{details: b("def\nxyz")}, " " + q("def\nxyz")},
92+
{formatEntry{details: b("def\nxyz")}, " " + q("def") + "\n" + q("xyz")},
9393
{formatEntry{head: b("abc"), details: b("def")}, " " + q("abc") + q("def")},
94-
{formatEntry{head: b("abc\nxyz"), details: b("def")}, " " + q("abc\nxyz") + q("def")},
95-
{formatEntry{head: b("abc"), details: b("def\n | xyz")}, " " + q("abc") + q("def\n | xyz")},
96-
{formatEntry{head: b("abc\nxyz"), details: b("def\n | xyz")}, " " + q("abc\nxyz") + q("def\n | xyz")},
94+
{formatEntry{head: b("abc\nxyz"), details: b("def")}, " " + q("abc") + "\n" + q("xyz") + q("def")},
95+
{formatEntry{head: b("abc"), details: b("def\n | xyz")}, " " + q("abc") + q("def") + "\n" + q(" | xyz")},
96+
{formatEntry{head: b("abc\nxyz"), details: b("def\n | xyz")}, " " + q("abc") + "\n" + q("xyz") + q("def") + "\n" + q(" | xyz")},
9797
// If there were markers in the entry, they get escaped in the output.
9898
{formatEntry{head: b("abc" + em + sm), details: b("def" + em + sm)}, " " + q("abc"+esc+esc) + q("def"+esc+esc)},
9999

@@ -141,7 +141,7 @@ func TestFormatSingleLineOutputRedactable(t *testing.T) {
141141
{[]formatEntry{{head: b(`a`)}, {}}, q(`a`)},
142142
{[]formatEntry{{head: b(`a`)}, {}, {head: b(`c`)}}, q(`c`) + ": " + q(`a`)},
143143
{[]formatEntry{{head: b(`a`), elideShort: true}, {head: b(`b`)}}, q(`b`)},
144-
{[]formatEntry{{head: b("abc\ndef")}, {head: b("ghi\nklm")}}, q("ghi\nklm") + ": " + q("abc\ndef")},
144+
{[]formatEntry{{head: b("abc\ndef")}, {head: b("ghi\nklm")}}, q("ghi") + "\n" + q("klm") + ": " + q("abc") + "\n" + q("def")},
145145

146146
// If some entries are redactable but not others, then
147147
// only those that are redactable are passed through.
@@ -157,8 +157,8 @@ func TestFormatSingleLineOutputRedactable(t *testing.T) {
157157
{[]formatEntry{{head: b(`a`)}, {}, {head: b(`c`)}}, q(`c`) + `: ` + q(`a`)},
158158
{[]formatEntry{{head: b(`a`)}, {redactable: true}, {head: b(`c`)}}, q(`c`) + `: ` + q(`a`)},
159159
{[]formatEntry{{head: b(`a`), elideShort: true, redactable: true}, {head: b(`b`)}}, q(`b`)},
160-
{[]formatEntry{{redactable: true, head: b("abc\ndef")}, {head: b("ghi\nklm")}}, q("ghi\nklm") + ": abc\ndef"},
161-
{[]formatEntry{{head: b("abc\ndef")}, {redactable: true, head: b("ghi\nklm")}}, "ghi\nklm: " + q("abc\ndef")},
160+
{[]formatEntry{{redactable: true, head: b("abc\ndef")}, {head: b("ghi\nklm")}}, q("ghi") + "\n" + q("klm") + ": abc\ndef"},
161+
{[]formatEntry{{head: b("abc\ndef")}, {redactable: true, head: b("ghi\nklm")}}, "ghi\nklm: " + q("abc") + "\n" + q("def")},
162162
// Entry already contains some markers.
163163
{[]formatEntry{{redactable: true, head: b(`a` + q(" b"))}, {redactable: true, head: b(`c ` + q("d"))}}, `c ` + q(`d`) + `: a` + q(` b`)},
164164
}

errutil/message_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ Error types: (1) *withstack.withStack (2) *errutil.leafError`,
5555

5656
{"newf-empty-arg",
5757
errutil.Newf(emptyString, 123),
58-
`%!(EXTRA *redact.escapeArg=123)`, `
59-
%!(EXTRA *redact.escapeArg=123)
58+
`%!(EXTRA int=123)`, `
59+
%!(EXTRA int=123)
6060
(1) attached stack trace
6161
-- stack trace:
6262
| github.com/cockroachdb/errors/errutil_test.TestFormat
@@ -65,7 +65,7 @@ Error types: (1) *withstack.withStack (2) *errutil.leafError`,
6565
| <tab><path>
6666
| runtime.goexit
6767
| <tab><path>
68-
Wraps: (2) %!(EXTRA *redact.escapeArg=123)
68+
Wraps: (2) %!(EXTRA int=123)
6969
Error types: (1) *withstack.withStack (2) *errutil.leafError`,
7070
},
7171

@@ -87,8 +87,8 @@ Error types: (1) *withstack.withStack (2) *errors.errorString`,
8787

8888
{"wrapf-empty-arg",
8989
errutil.Wrapf(goErr.New("woo"), emptyString, 123),
90-
`%!(EXTRA *redact.escapeArg=123): woo`, `
91-
%!(EXTRA *redact.escapeArg=123): woo
90+
`%!(EXTRA int=123): woo`, `
91+
%!(EXTRA int=123): woo
9292
(1) attached stack trace
9393
-- stack trace:
9494
| github.com/cockroachdb/errors/errutil_test.TestFormat
@@ -97,7 +97,7 @@ Error types: (1) *withstack.withStack (2) *errors.errorString`,
9797
| <tab><path>
9898
| runtime.goexit
9999
| <tab><path>
100-
Wraps: (2) %!(EXTRA *redact.escapeArg=123)
100+
Wraps: (2) %!(EXTRA int=123)
101101
Wraps: (3) woo
102102
Error types: (1) *withstack.withStack (2) *errutil.withPrefix (3) *errors.errorString`,
103103
},
@@ -153,8 +153,8 @@ Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *barr
153153

154154
{"assert + wrap empty+arg",
155155
errutil.NewAssertionErrorWithWrappedErrf(&werrFmt{goErr.New("woo"), "wuu"}, emptyString, 123),
156-
`%!(EXTRA *redact.escapeArg=123): wuu: woo`, `
157-
%!(EXTRA *redact.escapeArg=123): wuu: woo
156+
`%!(EXTRA int=123): wuu: woo`, `
157+
%!(EXTRA int=123): wuu: woo
158158
(1) assertion failure
159159
Wraps: (2) attached stack trace
160160
-- stack trace:
@@ -164,7 +164,7 @@ Wraps: (2) attached stack trace
164164
| <tab><path>
165165
| runtime.goexit
166166
| <tab><path>
167-
Wraps: (3) %!(EXTRA *redact.escapeArg=123)
167+
Wraps: (3) %!(EXTRA int=123)
168168
Wraps: (4) wuu: woo
169169
| -- cause hidden behind barrier
170170
| wuu: woo

errutil/redactable.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,56 @@ func init() {
108108
errbase.RegisterWrapperEncoder(errbase.GetTypeKey((*withPrefix)(nil)), encodeWithPrefix)
109109
errbase.RegisterWrapperDecoder(errbase.GetTypeKey((*withPrefix)(nil)), decodeWithPrefix)
110110
}
111+
112+
// withNewMessage is like withPrefix but the message completely
113+
// overrides that of the underlying error.
114+
type withNewMessage struct {
115+
cause error
116+
message redact.RedactableString
117+
}
118+
119+
var _ error = (*withNewMessage)(nil)
120+
var _ fmt.Formatter = (*withNewMessage)(nil)
121+
var _ errbase.SafeFormatter = (*withNewMessage)(nil)
122+
var _ errbase.SafeDetailer = (*withNewMessage)(nil)
123+
124+
func (l *withNewMessage) Error() string {
125+
return l.message.StripMarkers()
126+
}
127+
128+
func (l *withNewMessage) Cause() error { return l.cause }
129+
func (l *withNewMessage) Unwrap() error { return l.cause }
130+
131+
func (l *withNewMessage) Format(s fmt.State, verb rune) { errbase.FormatError(l, s, verb) }
132+
func (l *withNewMessage) SafeFormatError(p errbase.Printer) (next error) {
133+
p.Print(l.message)
134+
return nil /* nil here overrides the cause's message */
135+
}
136+
137+
func (l *withNewMessage) SafeDetails() []string {
138+
return []string{l.message.Redact().StripMarkers()}
139+
}
140+
141+
func encodeWithNewMessage(_ context.Context, err error) (string, []string, proto.Message) {
142+
l := err.(*withNewMessage)
143+
return l.Error(), l.SafeDetails(), &errorspb.StringPayload{Msg: string(l.message)}
144+
}
145+
146+
func decodeWithNewMessage(
147+
_ context.Context, cause error, _ string, _ []string, payload proto.Message,
148+
) error {
149+
m, ok := payload.(*errorspb.StringPayload)
150+
if !ok {
151+
// If this ever happens, this means some version of the library
152+
// (presumably future) changed the payload type, and we're
153+
// receiving this here. In this case, give up and let
154+
// DecodeError use the opaque type.
155+
return nil
156+
}
157+
return &withNewMessage{cause: cause, message: redact.RedactableString(m.Msg)}
158+
}
159+
160+
func init() {
161+
errbase.RegisterWrapperEncoder(errbase.GetTypeKey((*withNewMessage)(nil)), encodeWithNewMessage)
162+
errbase.RegisterWrapperDecoder(errbase.GetTypeKey((*withNewMessage)(nil)), decodeWithNewMessage)
163+
}

errutil/utilities.go

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
package errutil
1616

1717
import (
18-
"fmt"
19-
"regexp"
20-
21-
"github.com/cockroachdb/errors/safedetails"
2218
"github.com/cockroachdb/errors/secondary"
2319
"github.com/cockroachdb/errors/withstack"
2420
"github.com/cockroachdb/redact"
@@ -78,11 +74,11 @@ func NewWithDepthf(depth int, format string, args ...interface{}) error {
7874
errRefs = append(errRefs, e)
7975
}
8076
}
81-
if hasVerbW.MatchString(format) {
82-
err = fmt.Errorf(format, args...)
83-
err = safedetails.WithSafeDetails(err, format, args...)
77+
redactable, wrappedErr := redact.HelperForErrorf(format, args...)
78+
if wrappedErr != nil {
79+
err = &withNewMessage{cause: wrappedErr, message: redactable}
8480
} else {
85-
err = error(&leafError{redact.Sprintf(format, args...)})
81+
err = &leafError{redactable}
8682
}
8783
for _, e := range errRefs {
8884
err = secondary.WithSecondaryError(err, e)
@@ -91,36 +87,6 @@ func NewWithDepthf(depth int, format string, args ...interface{}) error {
9187
return err
9288
}
9389

94-
var hasVerbW = regexp.MustCompile(
95-
`^` +
96-
// Ignore any non-%w format directives and other characters:
97-
`([^%]+|%` +
98-
// A format directive starts with some flags.
99-
`[-#0+ ]*` +
100-
// Followed by an optional argument number [n].
101-
`(\[[0-9]+\])?` +
102-
// Followed by an optional width.
103-
`(\*|[0-9]+)?` +
104-
// followed by an optional precision.
105-
`(\.(\*|[0-9]))?` +
106-
// We're only interested in non-w verbs here.
107-
`[^w]` +
108-
// zero or more times.
109-
`)*` +
110-
// Followed with at least one occurrence of a format directive
111-
// with the 'w' verb. We replicate the rules above.
112-
`%` +
113-
// A format directive starts with some flags.
114-
`[-#0+ ]*` +
115-
// Followed by an optional argument number [n].
116-
`(\[[0-9]+\])?` +
117-
// Followed by an optional width.
118-
`(\*|[0-9]+)?` +
119-
// followed by an optional precision.
120-
`(\.(\*|[0-9]))?` +
121-
// Just the verb 'w'.
122-
`w`)
123-
12490
// Wrap wraps an error with a message prefix.
12591
// A stack trace is retained.
12692
//

errutil/utilities_test.go

Lines changed: 0 additions & 43 deletions
This file was deleted.

fmttests/datadriven_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ var wrapCommands = map[string]commandFn{
197197
// FormatError() method.
198198
"msg": func(err error, args []arg) error { return errutil.WithMessage(err, strfy(args)) },
199199

200+
// newfw is errors.Newf("%w") which is the fmt-standard way to wrap an error.
201+
"newfw": func(err error, args []arg) error { return errutil.Newf("new-style (%s) :: %w ::", strfy(args), err) },
202+
200203
// errutil.Wrap implements multi-layer wrappers.
201204
"wrapf": func(err error, args []arg) error { return errutil.Wrapf(err, "new-stylew %s", strfy(args)) },
202205
// assertions mask their cause from the barriers, but otherwise format as-is.

fmttests/format_error_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,25 @@ Error types: (1) *fmttests.werrDelegateEmpty (2) *errors.withStack (3) *fmttests
334334
}
335335
}
336336

337+
func TestHelperForErrorf(t *testing.T) {
338+
origErr := goErr.New("small\nuniverse")
339+
s, e := redact.HelperForErrorf("hello %s", origErr)
340+
if actual, expected := string(s), "hello ‹small›\n‹universe›"; actual != expected {
341+
t.Errorf("expected %q, got %q", expected, actual)
342+
}
343+
if e != nil {
344+
t.Errorf("expected no error, got %v", e)
345+
}
346+
347+
s, e = redact.HelperForErrorf("hello %w", origErr)
348+
if actual, expected := string(s), "hello ‹small›\n‹universe›"; actual != expected {
349+
t.Errorf("expected %q, got %q", expected, actual)
350+
}
351+
if e != origErr {
352+
t.Errorf("expected error %v, got %v (%T)", origErr, e, e)
353+
}
354+
}
355+
337356
func fmtClean(spv string) string {
338357
spv = fileref.ReplaceAllString(spv, "<path>:<lineno>")
339358
spv = libref.ReplaceAllString(spv, "<path>")

0 commit comments

Comments
 (0)