diff --git a/client/client.go b/client/client.go index b7b8f874a..44f5a9942 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,6 +95,26 @@ 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) { + return &TwilioRestError{ + Status: 400, + Code: 21222, + Message: "Invalid Username. Illegal chars", + MoreInfo: "https://www.twilio.com/docs/errors/21222"} + } + if !alphanumericRegex.MatchString(password) { + return &TwilioRestError{ + Status: 400, + Code: 21224, + Message: "Invalid Password. Illegal chars", + MoreInfo: "https://www.twilio.com/docs/errors/21224"} + } + 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) { @@ -101,8 +129,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 } @@ -112,6 +139,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..abf2999c2 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -116,6 +116,28 @@ func TestClient_SendRequestErrorWithDetails(t *testing.T) { assert.Equal(t, details, twilioError.Details) } +func TestClient_SendRequestUsernameError(t *testing.T) { + newTestClient := NewClient("user1\nuser2", "pass") + resp, err := newTestClient.SendRequest("GET", "http://example.org", nil, nil) //nolint:bodyclose + twilioError := err.(*twilio.TwilioRestError) + 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) { + newTestClient := NewClient("user1", "pass1\npass2") + resp, err := newTestClient.SendRequest("GET", "http://example.org", nil, nil) //nolint:bodyclose + twilioError := err.(*twilio.TwilioRestError) + 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) { redirectServer := httptest.NewServer(http.HandlerFunc( func(writer http.ResponseWriter, request *http.Request) {