Skip to content

Commit afcd467

Browse files
authored
Merge pull request #32 from knz/20200518-errors-redact
2 parents 0daaf51 + 59d01f0 commit afcd467

File tree

2 files changed

+148
-4
lines changed

2 files changed

+148
-4
lines changed

safedetails/redact.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ func Redact(r interface{}) string {
3737
case SafeMessager:
3838
buf.WriteString(t.SafeMessage())
3939
case error:
40+
if file, line, _, ok := withstack.GetOneLineSource(t); ok {
41+
fmt.Fprintf(&buf, "%s:%d: ", file, line)
42+
}
4043
redactErr(&buf, t)
4144
default:
4245
typAnd(&buf, r, "")
@@ -48,9 +51,6 @@ func Redact(r interface{}) string {
4851
func redactErr(buf *strings.Builder, err error) {
4952
if c := errbase.UnwrapOnce(err); c == nil {
5053
// This is a leaf error. Decode the leaf and return.
51-
if file, line, _, ok := withstack.GetOneLineSource(err); ok {
52-
fmt.Fprintf(buf, "%s:%d: ", file, line)
53-
}
5454
redactLeafErr(buf, err)
5555
} else /* c != nil */ {
5656
// Print the inner error before the outer error.
@@ -76,7 +76,7 @@ func redactWrapper(buf *strings.Builder, err error) {
7676
case *os.PathError:
7777
typAnd(buf, t, t.Op)
7878
case *os.LinkError:
79-
fmt.Fprintf(buf, "%T: %s %s %s", t, t.Op, t.Old, t.New)
79+
fmt.Fprintf(buf, "%T: %s <redacted> <redacted>", t, t.Op)
8080
case *net.OpError:
8181
typAnd(buf, t, t.Op)
8282
if t.Net != "" {

safedetails/redact_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2020 The Cockroach Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
// implied. See the License for the specific language governing
13+
// permissions and limitations under the License.
14+
15+
package safedetails_test
16+
17+
import (
18+
"context"
19+
"errors"
20+
"net"
21+
"os"
22+
"regexp"
23+
"runtime"
24+
"testing"
25+
26+
"github.com/cockroachdb/errors/safedetails"
27+
"github.com/cockroachdb/errors/testutils"
28+
"github.com/cockroachdb/errors/withstack"
29+
)
30+
31+
func TestRedact(t *testing.T) {
32+
errSentinel := (error)(struct{ error }{})
33+
34+
testData := []struct {
35+
obj interface{}
36+
expected string
37+
}{
38+
// Redacting non-error values.
39+
40+
{123, `<int>`},
41+
{"secret", `<string>`},
42+
43+
// Redacting SafeMessagers.
44+
45+
{mySafer{}, `hello`},
46+
{safedetails.Safe(123), `123`},
47+
48+
// Redacting errors.
49+
50+
// Unspecial cases, get redacted.
51+
{errors.New("secret"), `<*errors.errorString>`},
52+
53+
// Stack trace in error retrieves some info about the context.
54+
{withstack.WithStack(errors.New("secret")),
55+
`<path>: <*errors.errorString>
56+
wrapper: <*withstack.withStack>
57+
(more details:)
58+
github.com/cockroachdb/errors/safedetails_test.TestRedact
59+
<path>
60+
testing.tRunner
61+
<path>
62+
runtime.goexit
63+
<path>`},
64+
65+
// Special cases, unredacted.
66+
{os.ErrInvalid, `*errors.errorString: invalid argument`},
67+
{os.ErrPermission, `*errors.errorString: permission denied`},
68+
{os.ErrExist, `*errors.errorString: file already exists`},
69+
{os.ErrNotExist, `*errors.errorString: file does not exist`},
70+
{os.ErrClosed, `*errors.errorString: file already closed`},
71+
{os.ErrNoDeadline, `*errors.errorString: file type does not support deadline`},
72+
73+
{context.Canceled,
74+
`*errors.errorString: context canceled`},
75+
{context.DeadlineExceeded,
76+
`context.deadlineExceededError: context deadline exceeded`},
77+
78+
{makeTypeAssertionErr(),
79+
`*runtime.TypeAssertionError: interface conversion: interface {} is nil, not int`},
80+
81+
{errSentinel, // explodes if Error() called
82+
`<struct { error }>`},
83+
84+
{&werrFmt{&werrFmt{os.ErrClosed, "unseen"}, "unsung"},
85+
`*errors.errorString: file already closed
86+
wrapper: <*safedetails_test.werrFmt>
87+
wrapper: <*safedetails_test.werrFmt>`},
88+
89+
// Special cases, get partly redacted.
90+
91+
{os.NewSyscallError("rename", os.ErrNotExist),
92+
`*errors.errorString: file does not exist
93+
wrapper: *os.SyscallError: rename`},
94+
95+
{&os.PathError{Op: "rename", Path: "secret", Err: os.ErrNotExist},
96+
`*errors.errorString: file does not exist
97+
wrapper: *os.PathError: rename`},
98+
99+
{&os.LinkError{
100+
Op: "moo",
101+
Old: "sec",
102+
New: "cret",
103+
Err: os.ErrNotExist,
104+
},
105+
`*errors.errorString: file does not exist
106+
wrapper: *os.LinkError: moo <redacted> <redacted>`},
107+
108+
{&net.OpError{
109+
Op: "write",
110+
Net: "tcp",
111+
Source: &net.IPAddr{IP: net.IP("sensitive-source")},
112+
Addr: &net.IPAddr{IP: net.IP("sensitive-addr")},
113+
Err: errors.New("not safe"),
114+
}, `<*errors.errorString>
115+
wrapper: *net.OpError: write tcp<redacted>-><redacted>`},
116+
}
117+
118+
tt := testutils.T{T: t}
119+
120+
for _, tc := range testData {
121+
s := safedetails.Redact(tc.obj)
122+
s = fileref.ReplaceAllString(s, "<path>")
123+
124+
tt.CheckStringEqual(s, tc.expected)
125+
}
126+
}
127+
128+
var fileref = regexp.MustCompile(`([a-zA-Z0-9\._/@-]*\.(?:go|s):\d+)`)
129+
130+
// makeTypeAssertionErr returns a runtime.Error with the message:
131+
// interface conversion: interface {} is nil, not int
132+
func makeTypeAssertionErr() (result runtime.Error) {
133+
defer func() {
134+
e := recover()
135+
result = e.(runtime.Error)
136+
}()
137+
var x interface{}
138+
_ = x.(int)
139+
return nil
140+
}
141+
142+
type mySafer struct{}
143+
144+
func (mySafer) SafeMessage() string { return "hello" }

0 commit comments

Comments
 (0)