From a2d0720e518f1dff2da94262f86f7798670cf314 Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Fri, 21 Apr 2023 10:54:25 -0400 Subject: [PATCH] Indicate when req/res bodies have been redacted --- pkg/utils/http_dump_transport.go | 70 ++++++++++++++++++++++----- pkg/utils/http_dump_transport_test.go | 2 + 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/pkg/utils/http_dump_transport.go b/pkg/utils/http_dump_transport.go index 94b48c2..c724ea6 100644 --- a/pkg/utils/http_dump_transport.go +++ b/pkg/utils/http_dump_transport.go @@ -9,6 +9,8 @@ import ( "strings" ) +const redactedString = "[REDACTED]" + type dumpTransport struct { roundTripper http.RoundTripper logRequest func([]byte) @@ -22,7 +24,7 @@ func redactAuthz(req *http.Request) (restore func()) { origAuthz := req.Header.Get("Authorization") if origAuthz != "" { - req.Header.Set("Authorization", "[REDACTED]") + req.Header.Set("Authorization", redactedString) restore = func() { req.Header.Set("Authorization", origAuthz) } @@ -50,31 +52,77 @@ func redactBody(rc io.ReadCloser, rx *regexp.Regexp) (bool, io.ReadCloser, error return rx.Match(content.Bytes()), io.NopCloser(&content), nil } +func redactRequestBody(req *http.Request) (restore func()) { + restore = func() {} + + redactedReader := io.NopCloser(strings.NewReader(redactedString)) + + redact, origBody, _ := redactBody(req.Body, regexp.MustCompile(".*")) + + if redact { + origLength := req.ContentLength + + req.Body = redactedReader + req.ContentLength = int64(len(redactedString)) + + restore = func() { + req.Body = origBody + req.ContentLength = origLength + } + } else { + req.Body = origBody + } + + return +} + +func redactResponseBody(res *http.Response) (restore func()) { + restore = func() {} + + redactedReader := io.NopCloser(strings.NewReader(redactedString)) + + redact, origBody, _ := redactBody(res.Body, regexp.MustCompile("{\"protected\":\".*\",\"payload\":\".*\",\"signature\":\".*\"}")) + + if redact { + origLength := res.ContentLength + + res.Body = redactedReader + res.ContentLength = int64(len(redactedString)) + + restore = func() { + res.Body = origBody + res.ContentLength = origLength + } + } else { + res.Body = origBody + } + + return +} + // dumpRequest logs the contents of a given HTTP request, but first: // 1. sanitizes the Authorization header // 2. sanitizes the request body if the request is for authentication func (d *dumpTransport) dumpRequest(req *http.Request) []byte { - restore := redactAuthz(req) - defer restore() + restoreAuthz := redactAuthz(req) + defer restoreAuthz() - redact := false - var body io.ReadCloser if strings.Contains(req.URL.Path, "/authn") { - redact, body, _ = redactBody(req.Body, regexp.MustCompile(".*")) - req.Body = body + restoreBody := redactRequestBody(req) + defer restoreBody() } - dump, _ := httputil.DumpRequestOut(req, !redact) + dump, _ := httputil.DumpRequestOut(req, true) return dump } // dumpResponse logs the contents of a given HTTP response, but first // sanitizes the response body if it includes a Conjur token. func (d *dumpTransport) dumpResponse(res *http.Response) []byte { - redact, body, _ := redactBody(res.Body, regexp.MustCompile("{\"protected\":\".*\",\"payload\":\".*\",\"signature\":\".*\"}")) - res.Body = body + restoreBody := redactResponseBody(res) + defer restoreBody() - dump, _ := httputil.DumpResponse(res, !redact) + dump, _ := httputil.DumpResponse(res, true) return dump } diff --git a/pkg/utils/http_dump_transport_test.go b/pkg/utils/http_dump_transport_test.go index 9a6d832..9c760c3 100644 --- a/pkg/utils/http_dump_transport_test.go +++ b/pkg/utils/http_dump_transport_test.go @@ -37,6 +37,7 @@ func TestDumpTransport(t *testing.T) { path: "/authn-xyz/account/login", body: "some-body", assert: func(t *testing.T, req *http.Request, dump string) { + assert.Contains(t, dump, redactedString) assert.NotContains(t, dump, "some-body") reqBody, err := ioutil.ReadAll(req.Body) @@ -79,6 +80,7 @@ func TestDumpTransport(t *testing.T) { description: "Body is redacted if it contains a Conjur token", body: "{\"protected\":\"abcde\",\"payload\":\"fghijk\",\"signature\":\"lmnop\"}", assert: func(t *testing.T, res *http.Response, dump string) { + assert.Contains(t, dump, redactedString) assert.NotContains(t, dump, "{\"protected\":\"abcde\",\"payload\":\"fghijk\",\"signature\":\"lmnop\"}") reqBody, err := ioutil.ReadAll(res.Body)