Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Custom/Improved Sanitize. Report* Functions. #136

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,82 @@ func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interfa
return DefaultClient.CapturePanicAndWait(f, tags, interfaces...)
}

// ReportPanic reports a panic to the Sentry server if it occurs and allows that panic to continue.
func (client *Client) ReportPanic(err interface{}, tags map[string]string, interfaces ...Interface) {
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
// be completely noop though if we cared.
var packet *Packet
switch rval := err.(type) {
case nil:
return
case error:
if client.shouldExcludeErr(rval.Error()) {
return
}
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
default:
rvalStr := fmt.Sprint(rval)
if client.shouldExcludeErr(rvalStr) {
return
}
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
}

client.Capture(packet, tags)
// send the panic up the stack
panic(err)
}

// ReportPanic reports a panic to the Sentry server if it occurs and allows that panic to continue.
func ReportPanic(tags map[string]string, interfaces ...Interface) {
err := recover()
if err == nil {
return
}
DefaultClient.ReportPanic(err, tags, interfaces...)
}

// ReportPanicAndWait is identical to ReportPanic, except it blocks and assures that the event was sent.
func (client *Client) ReportPanicAndWait(err interface{}, tags map[string]string, interfaces ...Interface) {
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
// be completely noop though if we cared.

var packet *Packet
switch rval := err.(type) {
case nil:
return
case error:
if client.shouldExcludeErr(rval.Error()) {
return
}
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
default:
rvalStr := fmt.Sprint(rval)
if client.shouldExcludeErr(rvalStr) {
return
}
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
}
_, ch := client.Capture(packet, tags)
// block to make sure the report is sent
<-ch
// send the panic up the stack
panic(err)
}

// ReportPanicAndWait is identical to ReportPanic, except it blocks and assures that the event was sent.
func ReportPanicAndWait(tags map[string]string, interfaces ...Interface) {
err := recover()
if err == nil {
return
}
DefaultClient.ReportPanicAndWait(err, tags, interfaces...)
}

func (client *Client) Close() {
close(client.queue)
}
Expand Down
47 changes: 41 additions & 6 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,40 @@ func NewHttp(req *http.Request) *Http {
h := &Http{
Method: req.Method,
Cookies: req.Header.Get("Cookie"),
Query: sanitizeQuery(req.URL.Query()).Encode(),
Query: url.Values(sanitizeValues(req.URL.Query())).Encode(),
URL: proto + "://" + req.Host + req.URL.Path,
Headers: make(map[string]string, len(req.Header)),
}
if addr, port, err := net.SplitHostPort(req.RemoteAddr); err == nil {
h.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
}
for k, v := range req.Header {

for k, v := range http.Header(sanitizeValues(req.Header)) {
h.Headers[k] = strings.Join(v, ",")
}
return h
}

var querySecretFields = []string{"password", "passphrase", "passwd", "secret"}

func sanitizeQuery(query url.Values) url.Values {
func sanitizeValues(query map[string][]string) map[string][]string {
for _, keyword := range querySecretFields {
for field := range query {
if strings.Contains(field, keyword) {
if strings.Contains(strings.ToLower(field), strings.ToLower(keyword)) {
query[field] = []string{"********"}
}
}
}
return query
}

// AddSanitizewField adds a custom sanitize field to the array of fields to
// search for and sanitize. This allows you to hide sensitive information in
// both the query string and headers.
func AddSanitizeField(field string) {
querySecretFields = append(querySecretFields, field)
}

// https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
type Http struct {
// Required
Expand All @@ -62,9 +70,12 @@ type Http struct {

func (h *Http) Class() string { return "request" }

// Recovery handler to wrap the stdlib net/http Mux.
// Recovery handler to wrap the stdlib net/http Mux. This function will detect a
// panic, report it, and recover from the panic, preventing it from continuing
// further.
//
// Example:
// http.HandleFunc("/", raven.RecoveryHandler(func(w http.ResponseWriter, r *http.Request) {
// http.HandleFunc("/", raven.ReportHandler(func(w http.ResponseWriter, r *http.Request) {
// ...
// }))
func RecoveryHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
Expand All @@ -82,3 +93,27 @@ func RecoveryHandler(handler func(http.ResponseWriter, *http.Request)) func(http
handler(w, r)
}
}

// Report handler to wrap the stdlib net/http Mux. This function will detect a
// panic, report it, and allow the panic to contune.
//
// Example:
// http.HandleFunc("/", raven.ReportHandler(func(w http.ResponseWriter, r *http.Request) {
// ...
// }))
func ReportHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rval := recover(); rval != nil {
debug.PrintStack()
rvalStr := fmt.Sprint(rval)
packet := NewPacket(rvalStr, NewException(errors.New(rvalStr), NewStacktrace(2, 3, nil)), NewHttp(r))
Capture(packet, nil)
w.WriteHeader(http.StatusInternalServerError)
panic(rval)
}
}()

handler(w, r)
}
}
30 changes: 29 additions & 1 deletion http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,38 @@ func parseQuery(q string) url.Values {

func TestSanitizeQuery(t *testing.T) {
for _, test := range sanitizeQueryTests {
actual := sanitizeQuery(parseQuery(test.input))
actual := url.Values(sanitizeValues(parseQuery(test.input)))
expected := parseQuery(test.output)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("incorrect sanitization: got %+v, want %+v", actual, expected)
}
}
}

var sanitizeHeadersTest = []struct {
input, output string
}{
{"foo=bar", "foo=bar"},
{"password=foo", "password=********"},
{"passphrase=foo", "passphrase=********"},
{"passwd=foo", "passwd=********"},
{"secret=foo", "secret=********"},
{"secretstuff=foo", "secretstuff=********"},
{"foo=bar&secret=foo", "foo=bar&secret=********"},
{"secret=foo&secret=bar", "secret=********"},
}

func parseHeaders(q string) http.Header {
r, _ := url.ParseQuery(q)
return http.Header(r)
}

func TestSanitizeHeaders(t *testing.T) {
for _, test := range sanitizeHeadersTest {
actual := http.Header(sanitizeValues(parseQuery(test.input)))
expected := parseHeaders(test.output)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("incorrect sanitization: got %+v, want %+v", actual, expected)
}
}
}