From ba008f2902528970a4923c5365d46ca6f41ca65b Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 7 Feb 2024 18:31:46 +0530 Subject: [PATCH 1/3] fix: added validation check for user credentials --- client/client.go | 23 ++++++++++++++++++++++ client/client_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/client/client.go b/client/client.go index b7b8f874a..5fd65876a 100644 --- a/client/client.go +++ b/client/client.go @@ -87,6 +87,24 @@ func (c *Client) doWithErr(req *http.Request) (*http.Response, error) { return res, nil } +func isAlphanumeric(word string) bool { + return regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(word) +} + +func (c *Client) validateCredentials() error { + username, password := c.basicAuth() + err := &TwilioRestError{Status: 400} + if !isAlphanumeric(username) { + err.Message = "Invalid Username" + return err + } + if !isAlphanumeric(password) { + err.Message = "Invalid Password" + return err + } + return nil +} + // SendRequest verifies, constructs, and authorizes an HTTP request. func (c *Client) SendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}) (*http.Response, error) { @@ -112,6 +130,11 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, valueReader = strings.NewReader(data.Encode()) } + credErr := c.validateCredentials() + if credErr != nil { + return nil, credErr + } + req, err := http.NewRequest(method, u.String(), valueReader) if err != nil { return nil, err diff --git a/client/client_test.go b/client/client_test.go index d1e7d88aa..140540231 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -116,6 +116,52 @@ func TestClient_SendRequestErrorWithDetails(t *testing.T) { assert.Equal(t, details, twilioError.Details) } +func TestClient_SendRequestUsernameError(t *testing.T) { + errorResponse := `{ + "status": 400, + "code":20001, + "message":"Bad request", + "more_info":"https://www.twilio.com/docs/errors/20001", +}` + errorServer := httptest.NewServer(http.HandlerFunc( + func(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(400) + _, _ = resp.Write([]byte(errorResponse)) + })) + defer errorServer.Close() + + newTestClient := NewClient("user1\nuser2", "pass") + resp, err := newTestClient.SendRequest("GET", errorServer.URL, nil, nil) //nolint:bodyclose + twilioError := err.(*twilio.TwilioRestError) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Invalid Username") + assert.Equal(t, 400, twilioError.Status) + assert.Nil(t, resp) +} + +func TestClient_SendRequestPasswordError(t *testing.T) { + errorResponse := `{ + "status": 400, + "code":20001, + "message":"Bad request", + "more_info":"https://www.twilio.com/docs/errors/20001", +}` + errorServer := httptest.NewServer(http.HandlerFunc( + func(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(400) + _, _ = resp.Write([]byte(errorResponse)) + })) + defer errorServer.Close() + + newTestClient := NewClient("user1", "pass1\npass2") + resp, err := newTestClient.SendRequest("GET", errorServer.URL, nil, nil) //nolint:bodyclose + twilioError := err.(*twilio.TwilioRestError) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Invalid Password") + assert.Equal(t, 400, twilioError.Status) + assert.Nil(t, resp) +} + func TestClient_SendRequestWithRedirect(t *testing.T) { redirectServer := httptest.NewServer(http.HandlerFunc( func(writer http.ResponseWriter, request *http.Request) { From 645c526940106bdd64cf0bbb0b45765a226a92fe Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 8 Feb 2024 13:38:31 +0530 Subject: [PATCH 2/3] fix: added error code and more info in validate credentials --- client/client.go | 34 ++++++++++++++++++++------------- client/client_test.go | 44 ++++++++++--------------------------------- 2 files changed, 31 insertions(+), 47 deletions(-) diff --git a/client/client.go b/client/client.go index 5fd65876a..0e07a06d0 100644 --- a/client/client.go +++ b/client/client.go @@ -16,6 +16,14 @@ import ( "github.com/twilio/twilio-go/client/form" ) +var alphanumericRegex *regexp.Regexp +var delimitingRegex *regexp.Regexp + +func init() { + alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`) + delimitingRegex = regexp.MustCompile(`\.\d+`) +} + // Credentials store user authentication credentials. type Credentials struct { Username string @@ -87,20 +95,21 @@ func (c *Client) doWithErr(req *http.Request) (*http.Response, error) { return res, nil } -func isAlphanumeric(word string) bool { - return regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(word) -} - func (c *Client) validateCredentials() error { username, password := c.basicAuth() - err := &TwilioRestError{Status: 400} - if !isAlphanumeric(username) { - err.Message = "Invalid Username" - return err + if alphanumericRegex.MatchString(username) == false { + return &TwilioRestError{ + Status: 400, + Code: 21222, + Message: "Invalid Username. Illegal chars", + MoreInfo: "https://www.twilio.com/docs/errors/21222"} } - if !isAlphanumeric(password) { - err.Message = "Invalid Password" - return err + if alphanumericRegex.MatchString(password) == false { + return &TwilioRestError{ + Status: 400, + Code: 21224, + Message: "Invalid Password. Illegal chars", + MoreInfo: "https://www.twilio.com/docs/errors/21224"} } return nil } @@ -119,8 +128,7 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, if method == http.MethodGet { if data != nil { v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros) - regex := regexp.MustCompile(`\.\d+`) - s := regex.ReplaceAllString(v, "") + s := delimitingRegex.ReplaceAllString(v, "") u.RawQuery = s } diff --git a/client/client_test.go b/client/client_test.go index 140540231..abf2999c2 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -117,49 +117,25 @@ func TestClient_SendRequestErrorWithDetails(t *testing.T) { } func TestClient_SendRequestUsernameError(t *testing.T) { - errorResponse := `{ - "status": 400, - "code":20001, - "message":"Bad request", - "more_info":"https://www.twilio.com/docs/errors/20001", -}` - errorServer := httptest.NewServer(http.HandlerFunc( - func(resp http.ResponseWriter, req *http.Request) { - resp.WriteHeader(400) - _, _ = resp.Write([]byte(errorResponse)) - })) - defer errorServer.Close() - newTestClient := NewClient("user1\nuser2", "pass") - resp, err := newTestClient.SendRequest("GET", errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := newTestClient.SendRequest("GET", "http://example.org", nil, nil) //nolint:bodyclose twilioError := err.(*twilio.TwilioRestError) - assert.Error(t, err) - assert.Contains(t, err.Error(), "Invalid Username") - assert.Equal(t, 400, twilioError.Status) assert.Nil(t, resp) + assert.Equal(t, 400, twilioError.Status) + assert.Equal(t, 21222, twilioError.Code) + assert.Equal(t, "https://www.twilio.com/docs/errors/21222", twilioError.MoreInfo) + assert.Equal(t, "Invalid Username. Illegal chars", twilioError.Message) } func TestClient_SendRequestPasswordError(t *testing.T) { - errorResponse := `{ - "status": 400, - "code":20001, - "message":"Bad request", - "more_info":"https://www.twilio.com/docs/errors/20001", -}` - errorServer := httptest.NewServer(http.HandlerFunc( - func(resp http.ResponseWriter, req *http.Request) { - resp.WriteHeader(400) - _, _ = resp.Write([]byte(errorResponse)) - })) - defer errorServer.Close() - newTestClient := NewClient("user1", "pass1\npass2") - resp, err := newTestClient.SendRequest("GET", errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := newTestClient.SendRequest("GET", "http://example.org", nil, nil) //nolint:bodyclose twilioError := err.(*twilio.TwilioRestError) - assert.Error(t, err) - assert.Contains(t, err.Error(), "Invalid Password") - assert.Equal(t, 400, twilioError.Status) assert.Nil(t, resp) + assert.Equal(t, 400, twilioError.Status) + assert.Equal(t, 21224, twilioError.Code) + assert.Equal(t, "https://www.twilio.com/docs/errors/21224", twilioError.MoreInfo) + assert.Equal(t, "Invalid Password. Illegal chars", twilioError.Message) } func TestClient_SendRequestWithRedirect(t *testing.T) { From 4836dc79cfc8aeb1af253ecc1723f665260d0302 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 8 Feb 2024 13:42:28 +0530 Subject: [PATCH 3/3] fix: added inline comments --- client/client.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index 0e07a06d0..44f5a9942 100644 --- a/client/client.go +++ b/client/client.go @@ -95,16 +95,17 @@ func (c *Client) doWithErr(req *http.Request) (*http.Response, error) { return res, nil } +// throws error if username and password contains special characters func (c *Client) validateCredentials() error { username, password := c.basicAuth() - if alphanumericRegex.MatchString(username) == false { + if !alphanumericRegex.MatchString(username) { return &TwilioRestError{ Status: 400, Code: 21222, Message: "Invalid Username. Illegal chars", MoreInfo: "https://www.twilio.com/docs/errors/21222"} } - if alphanumericRegex.MatchString(password) == false { + if !alphanumericRegex.MatchString(password) { return &TwilioRestError{ Status: 400, Code: 21224,