From dcaed6c84346d0fddbfa5ac221be2a83ff99d189 Mon Sep 17 00:00:00 2001 From: jgheewala Date: Wed, 10 Nov 2021 09:39:58 -0500 Subject: [PATCH] sfxclient: http sink upon error should sha request headers that contain secure credentials some headers in the request can potentially contain secure credentials. Added api loggableHeaders() which will extract out such headers that should not be logged sha them and write it back. --- sfxclient/httpsink.go | 25 ++++++++++++++++++++++++- sfxclient/httpsink_test.go | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/sfxclient/httpsink.go b/sfxclient/httpsink.go index dc51a5c..e05b840 100644 --- a/sfxclient/httpsink.go +++ b/sfxclient/httpsink.go @@ -4,6 +4,7 @@ import ( "bytes" "compress/gzip" "context" + "crypto/sha1" "encoding/json" "fmt" "io" @@ -154,6 +155,28 @@ var _ Sink = &HTTPSink{} // TokenHeaderName is the header key for the auth token in the HTTP request const TokenHeaderName = "X-Sf-Token" +func getShaValue(values []string) string { + h := sha1.New() + for _, v := range values { + h.Write([]byte(v)) + } + return string(h.Sum(nil)) +} + +// loggableHeaders returns headers that are only allowed to be logged. For instance "X-Sf-Token" should not be logged so +// it will generate a sha value and then log it +func loggableHeaders(headers map[string][]string) (rv map[string][]string) { + rv = make(map[string][]string, len(headers)) + for header, value := range headers { + if strings.EqualFold(header, TokenHeaderName) { + rv[header] = []string{getShaValue(value)} + continue + } + rv[header] = value + } + return rv +} + func (h *HTTPSink) doBottom(ctx context.Context, f func() (io.Reader, bool, error), contentType, endpoint string, respValidator responseValidator) error { if ctx.Err() != nil { @@ -176,7 +199,7 @@ func (h *HTTPSink) doBottom(ctx context.Context, f func() (io.Reader, bool, erro if err != nil { // According to docs, resp can be ignored since err is non-nil, so we // don't have to close body. - return fmt.Errorf("failed to send/receive http request: %w: %v", err, req.Header) + return fmt.Errorf("failed to send/receive http request: %w: %v", err, loggableHeaders(req.Header)) } return h.handleResponse(resp, respValidator) diff --git a/sfxclient/httpsink_test.go b/sfxclient/httpsink_test.go index 27f3028..1fe7bbe 100644 --- a/sfxclient/httpsink_test.go +++ b/sfxclient/httpsink_test.go @@ -727,3 +727,26 @@ func TestSetHeaders(t *testing.T) { So(req.Header.Get(TokenHeaderName), ShouldEqual, "bar") }) } + +func TestLoggableHeaders(t *testing.T) { + Convey("given a list of request headers we should log all except some", t, func() { + lowerCaseTokenHeader := "x-sf-token" + inputHeaders := map[string][]string{ + lowerCaseTokenHeader: {"thisIsValidToken"}, + "version": {"test"}, + "gzip": {"yes"}, + } + expectedHeader := map[string][]string{ + lowerCaseTokenHeader: {getShaValue(inputHeaders[lowerCaseTokenHeader])}, + "version": {"test"}, + "gzip": {"yes"}, + } + actualHeaders := loggableHeaders(inputHeaders) + So(len(actualHeaders), ShouldEqual, len(expectedHeader)) + So(actualHeaders, ShouldResemble, expectedHeader) + So(actualHeaders[lowerCaseTokenHeader], ShouldNotEqual, inputHeaders[lowerCaseTokenHeader]) + for header := range expectedHeader { + So(actualHeaders[header], ShouldNotBeNil) + } + }) +}