Skip to content

Commit feb9d32

Browse files
committed
barriers: use a redactable string as message payload
This changes the implementation of barriers to use a redactable string as message payload. This makes it possible to include more details (all the safe parts of the message payload) when printing out the barrier cause. In order to ensure safety when interacting with previous versions of the library without this logic, we must be careful to wrap/escape the message string received when decoding a network error. To force this additional wrapping, we rename the error type so that it can get a separate decode function.
1 parent a73e1b6 commit feb9d32

File tree

13 files changed

+499
-474
lines changed

13 files changed

+499
-474
lines changed

barriers/barriers.go

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func Handled(err error) error {
3737
if err == nil {
3838
return nil
3939
}
40-
return HandledWithMessage(err, err.Error())
40+
return HandledWithSafeMessage(err, redact.Sprint(err))
4141
}
4242

4343
// HandledWithMessage is like Handled except the message is overridden.
@@ -47,7 +47,14 @@ func HandledWithMessage(err error, msg string) error {
4747
if err == nil {
4848
return nil
4949
}
50-
return &barrierError{maskedErr: err, msg: msg}
50+
return HandledWithSafeMessage(err, redact.Sprint(msg))
51+
}
52+
53+
// HandledWithSafeMessage is like Handled except the message is overridden.
54+
// This can be used e.g. to hide message details or to prevent
55+
// downstream code to make assertions on the message's contents.
56+
func HandledWithSafeMessage(err error, msg redact.RedactableString) error {
57+
return &barrierErr{maskedErr: err, smsg: msg}
5158
}
5259

5360
// HandledWithMessagef is like HandledWithMessagef except the message
@@ -56,34 +63,34 @@ func HandledWithMessagef(err error, format string, args ...interface{}) error {
5663
if err == nil {
5764
return nil
5865
}
59-
return &barrierError{maskedErr: err, msg: fmt.Sprintf(format, args...)}
66+
return &barrierErr{maskedErr: err, smsg: redact.Sprintf(format, args...)}
6067
}
6168

62-
// barrierError is a leaf error type. It encapsulates a chain of
69+
// barrierErr is a leaf error type. It encapsulates a chain of
6370
// original causes, but these causes are hidden so that they inhibit
6471
// matching via Is() and the Cause()/Unwrap() recursions.
65-
type barrierError struct {
72+
type barrierErr struct {
6673
// Message for the barrier itself.
6774
// In the common case, the message from the masked error
6875
// is used as-is (see Handled() above) however it is
6976
// useful to cache it here since the masked error may
7077
// have a long chain of wrappers and its Error() call
7178
// may be expensive.
72-
msg string
79+
smsg redact.RedactableString
7380
// Masked error chain.
7481
maskedErr error
7582
}
7683

77-
var _ error = (*barrierError)(nil)
78-
var _ errbase.SafeDetailer = (*barrierError)(nil)
79-
var _ errbase.SafeFormatter = (*barrierError)(nil)
80-
var _ fmt.Formatter = (*barrierError)(nil)
84+
var _ error = (*barrierErr)(nil)
85+
var _ errbase.SafeDetailer = (*barrierErr)(nil)
86+
var _ errbase.SafeFormatter = (*barrierErr)(nil)
87+
var _ fmt.Formatter = (*barrierErr)(nil)
8188

82-
// barrierError is an error.
83-
func (e *barrierError) Error() string { return e.msg }
89+
// barrierErr is an error.
90+
func (e *barrierErr) Error() string { return e.smsg.StripMarkers() }
8491

8592
// SafeDetails reports the PII-free details from the masked error.
86-
func (e *barrierError) SafeDetails() []string {
93+
func (e *barrierErr) SafeDetails() []string {
8794
var details []string
8895
for err := e.maskedErr; err != nil; err = errbase.UnwrapOnce(err) {
8996
sd := errbase.GetSafeDetails(err)
@@ -94,10 +101,10 @@ func (e *barrierError) SafeDetails() []string {
94101
}
95102

96103
// Printing a barrier reveals the details.
97-
func (e *barrierError) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
104+
func (e *barrierErr) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
98105

99-
func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
100-
p.Print(e.msg)
106+
func (e *barrierErr) SafeFormatError(p errbase.Printer) (next error) {
107+
p.Print(e.smsg)
101108
if p.Detail() {
102109
p.Printf("-- cause hidden behind barrier\n%+v", e.maskedErr)
103110
}
@@ -108,19 +115,37 @@ func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
108115
func encodeBarrier(
109116
ctx context.Context, err error,
110117
) (msg string, details []string, payload proto.Message) {
111-
e := err.(*barrierError)
118+
e := err.(*barrierErr)
112119
enc := errbase.EncodeError(ctx, e.maskedErr)
113-
return e.msg, e.SafeDetails(), &enc
120+
return string(e.smsg), e.SafeDetails(), &enc
114121
}
115122

116123
// A barrier error is decoded exactly.
117124
func decodeBarrier(ctx context.Context, msg string, _ []string, payload proto.Message) error {
118125
enc := payload.(*errbase.EncodedError)
119-
return &barrierError{msg: msg, maskedErr: errbase.DecodeError(ctx, *enc)}
126+
return &barrierErr{smsg: redact.RedactableString(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
120127
}
121128

129+
// Previous versions of barrier errors.
130+
func decodeBarrierPrev(ctx context.Context, msg string, _ []string, payload proto.Message) error {
131+
enc := payload.(*errbase.EncodedError)
132+
return &barrierErr{smsg: redact.Sprint(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
133+
}
134+
135+
// barrierError is the "old" type name of barrierErr. We use a new
136+
// name now to ensure a different decode function is used when
137+
// importing barriers from the previous structure, where the
138+
// message is not redactable.
139+
type barrierError struct {
140+
msg string
141+
maskedErr error
142+
}
143+
144+
func (b *barrierError) Error() string { return "" }
145+
122146
func init() {
123-
tn := errbase.GetTypeKey((*barrierError)(nil))
147+
errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barrierError)(nil)), decodeBarrierPrev)
148+
tn := errbase.GetTypeKey((*barrierErr)(nil))
124149
errbase.RegisterLeafDecoder(tn, decodeBarrier)
125150
errbase.RegisterLeafEncoder(tn, encodeBarrier)
126151
}

barriers/barriers_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ woo
118118
| woo
119119
| (1) woo
120120
| Error types: (1) *errors.errorString
121-
Error types: (1) *barriers.barrierError`},
121+
Error types: (1) *barriers.barrierErr`},
122122

123123
{"handled + handled", barriers.Handled(barriers.Handled(goErr.New("woo"))), woo, `
124124
woo
@@ -130,8 +130,8 @@ woo
130130
| | woo
131131
| | (1) woo
132132
| | Error types: (1) *errors.errorString
133-
| Error types: (1) *barriers.barrierError
134-
Error types: (1) *barriers.barrierError`},
133+
| Error types: (1) *barriers.barrierErr
134+
Error types: (1) *barriers.barrierErr`},
135135

136136
{"handledmsg", barriers.HandledWithMessage(goErr.New("woo"), "waa"), "waa", `
137137
waa
@@ -140,7 +140,7 @@ waa
140140
| woo
141141
| (1) woo
142142
| Error types: (1) *errors.errorString
143-
Error types: (1) *barriers.barrierError`},
143+
Error types: (1) *barriers.barrierErr`},
144144

145145
{"handledmsg + handledmsg", barriers.HandledWithMessage(
146146
barriers.HandledWithMessage(
@@ -154,8 +154,8 @@ wuu
154154
| | woo
155155
| | (1) woo
156156
| | Error types: (1) *errors.errorString
157-
| Error types: (1) *barriers.barrierError
158-
Error types: (1) *barriers.barrierError`},
157+
| Error types: (1) *barriers.barrierErr
158+
Error types: (1) *barriers.barrierErr`},
159159

160160
{"handled + wrapper",
161161
barriers.Handled(
@@ -172,7 +172,7 @@ waa: woo
172172
| | multi-line wrapper payload
173173
| Wraps: (2) woo
174174
| Error types: (1) *barriers_test.werrFmt (2) *errors.errorString
175-
Error types: (1) *barriers.barrierError`},
175+
Error types: (1) *barriers.barrierErr`},
176176
}
177177

178178
for _, test := range testCases {

errutil/message_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Wraps: (4) wuu: woo
124124
| | multi-line payload
125125
| Wraps: (2) woo
126126
| Error types: (1) *errutil_test.werrFmt (2) *errors.errorString
127-
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *errutil.withPrefix (4) *barriers.barrierError`,
127+
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *errutil.withPrefix (4) *barriers.barrierErr`,
128128
},
129129

130130
{"assert + wrap empty",
@@ -148,7 +148,7 @@ Wraps: (3) wuu: woo
148148
| | multi-line payload
149149
| Wraps: (2) woo
150150
| Error types: (1) *errutil_test.werrFmt (2) *errors.errorString
151-
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *barriers.barrierError`,
151+
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *barriers.barrierErr`,
152152
},
153153

154154
{"assert + wrap empty+arg",
@@ -173,7 +173,7 @@ Wraps: (4) wuu: woo
173173
| | multi-line payload
174174
| Wraps: (2) woo
175175
| Error types: (1) *errutil_test.werrFmt (2) *errors.errorString
176-
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *errutil.withPrefix (4) *barriers.barrierError`,
176+
Error types: (1) *assert.withAssertionFailure (2) *withstack.withStack (3) *errutil.withPrefix (4) *barriers.barrierErr`,
177177
},
178178
}
179179

0 commit comments

Comments
 (0)