Skip to content

Commit

Permalink
Mask the value of the Authorization header if debug is enabled (#501)
Browse files Browse the repository at this point in the history
* mask the value of the Authorization header if debug is enabled

Signed-off-by: Ross Kirkpatrick <rkirkpat@akamai.com>
Signed-off-by: rosskirkpat <rosskirkpat@outlook.com>

* fix linters

Signed-off-by: Ross Kirkpatrick <rosskirkpat@outlook.com>

* make the auth header sanitization function private

Signed-off-by: Ross Kirkpatrick <rosskirkpat@outlook.com>

---------

Signed-off-by: Ross Kirkpatrick <rkirkpat@akamai.com>
Signed-off-by: rosskirkpat <rosskirkpat@outlook.com>
Signed-off-by: Ross Kirkpatrick <rosskirkpat@outlook.com>
  • Loading branch information
rosskirkpat committed May 14, 2024
1 parent 2f9320e commit 25fe7d3
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
12 changes: 11 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ type (
)

func init() {
// Wether or not we will enable Resty debugging output
// Whether we will enable Resty debugging output
if apiDebug, ok := os.LookupEnv("LINODE_DEBUG"); ok {
if parsed, err := strconv.ParseBool(apiDebug); err == nil {
envDebug = parsed
Expand Down Expand Up @@ -122,6 +122,8 @@ func (c *Client) R(ctx context.Context) *resty.Request {
func (c *Client) SetDebug(debug bool) *Client {
c.debug = debug
c.resty.SetDebug(debug)
// this ensures that if there is an Authorization header present, the value is sanitized/masked
c.sanitizeAuthorizationHeader()

return c
}
Expand Down Expand Up @@ -412,6 +414,14 @@ func (c *Client) SetHeader(name, value string) {
c.resty.SetHeader(name, value)
}

func (c *Client) sanitizeAuthorizationHeader() {
c.resty.OnRequestLog(func(r *resty.RequestLog) error {
// masking authorization header
r.Header.Set("Authorization", "Bearer *******************************")
return nil
})
}

// NewClient factory to create new Client struct
func NewClient(hc *http.Client) (client Client) {
if hc != nil {
Expand Down
59 changes: 59 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package linodego

import (
"bytes"
"context"
"fmt"
"reflect"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/jarcoal/httpmock"
"github.com/linode/linodego/internal/testutil"
)

func TestClient_SetAPIVersion(t *testing.T) {
Expand Down Expand Up @@ -139,3 +145,56 @@ api_version = v4beta
[cool]
token = blah
`

func TestDebugLogSanitization(t *testing.T) {
type instanceResponse struct {
ID int `json:"id"`
Region string `json:"region"`
Label string `json:"label"`
}

var testResp = instanceResponse{
ID: 100,
Region: "test-central",
Label: "this-is-a-test-linode",
}
var lgr bytes.Buffer

plainTextToken := "NOTANAPIKEY"

mockClient := testutil.CreateMockClient(t, NewClient)
logger := testutil.CreateLogger()
mockClient.SetLogger(logger)
logger.L.SetOutput(&lgr)

mockClient.SetDebug(true)
if !mockClient.resty.Debug {
t.Fatal("debug should be enabled")
}
mockClient.SetHeader("Authorization", fmt.Sprintf("Bearer %s", plainTextToken))

if mockClient.resty.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", plainTextToken) {
t.Fatal("token not found in auth header")
}

httpmock.RegisterRegexpResponder("GET", testutil.MockRequestURL("/linode/instances"),
httpmock.NewJsonResponderOrPanic(200, &testResp))

result, err := doGETRequest[instanceResponse](
context.Background(),
mockClient,
"/linode/instances",
)
if err != nil {
t.Fatal(err)
}

logInfo := lgr.String()
if !strings.Contains(logInfo, "Bearer *******************************") {
t.Fatal("sanitized bearer token was expected")
}

if !reflect.DeepEqual(*result, testResp) {
t.Fatalf("actual response does not equal desired response: %s", cmp.Diff(result, testResponse))
}
}
39 changes: 39 additions & 0 deletions internal/testutil/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"reflect"
"regexp"
"strings"
Expand Down Expand Up @@ -87,3 +89,40 @@ func CreateMockClient[T any](t *testing.T, createFunc func(*http.Client) T) *T {
result := createFunc(client)
return &result
}

type Logger interface {
Errorf(format string, v ...interface{})
Warnf(format string, v ...interface{})
Debugf(format string, v ...interface{})
}

func CreateLogger() *TestLogger {
l := &TestLogger{L: log.New(os.Stderr, "", log.Ldate|log.Lmicroseconds)}
return l
}

var _ Logger = (*TestLogger)(nil)

type TestLogger struct {
L *log.Logger
}

func (l *TestLogger) Errorf(format string, v ...interface{}) {
l.outputf("ERROR RESTY "+format, v...)
}

func (l *TestLogger) Warnf(format string, v ...interface{}) {
l.outputf("WARN RESTY "+format, v...)
}

func (l *TestLogger) Debugf(format string, v ...interface{}) {
l.outputf("DEBUG RESTY "+format, v...)
}

func (l *TestLogger) outputf(format string, v ...interface{}) {
if len(v) == 0 {
l.L.Print(format)
return
}
l.L.Printf(format, v...)
}

0 comments on commit 25fe7d3

Please sign in to comment.