Skip to content

Commit

Permalink
Revert "feat: Add support for x-www-form-urlencoded requests to rv.Va…
Browse files Browse the repository at this point in the history
…lidateBody (#165)"

This reverts commit c21e6c4.
  • Loading branch information
claudiachua committed Jul 8, 2022
1 parent c21e6c4 commit dcfcf11
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 39 deletions.
37 changes: 9 additions & 28 deletions client/request_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,45 +40,26 @@ func (rv *RequestValidator) Validate(url string, params map[string]string, expec
// check signature of testURL with and without port, since sig generation on back-end is inconsistent
signatureWithPort := rv.getValidationSignature(addPort(url), paramSlc)
signatureWithoutPort := rv.getValidationSignature(removePort(url), paramSlc)

return compare(signatureWithPort, expectedSignature) ||
compare(signatureWithoutPort, expectedSignature)
}

// ValidateBody can be used to verify request signatures included with Twilio webhook requests configured to be sent as POST requests.
// url is the full URL you are receiving webhook requests at
// body is a byte slice of the body of the incoming webhook request
// expectedSignature is the value of the X-Twilio-Signature header
// ValidateBody can be used for Twilio Signatures sent with webhooks configured for POST calls. It returns true
// if the computed signature matches the expectedSignature. Body is the HTTP request body from the webhook call
// as a slice of bytes.
func (rv *RequestValidator) ValidateBody(url string, body []byte, expectedSignature string) bool {
parsed, err := urllib.Parse(url)
if err != nil {
return false
}

// we can expect this query paramter on requests made with json bodies
if parsed.Query().Has("bodySHA256") {
bodySHA256 := parsed.Query().Get("bodySHA256")
if len(bodySHA256) == 0 {
return false
}
return rv.Validate(url, map[string]string{}, expectedSignature) &&
rv.validateBody(body, bodySHA256)
} else {
// however if that parameter is not present, we assume the request body is x-www-form-urlencoded, e.g "property=value&boolean=true" (quotes added for clarity)
parsedBody, err := urllib.ParseQuery(string(body))
if err != nil {
return false
}

// url.Values is a map[string][]string, therefore we need to create a new map to store the values we will pass to rv.Validate below
params := make(map[string]string)
for k, v := range parsedBody {
// we only care about the first value held by each key. all other values under a key will be ignored.
params[k] = v[0]
}

return rv.Validate(url, params, expectedSignature)
bodySHA256 := parsed.Query().Get("bodySHA256")
if len(bodySHA256) == 0 {
return false
}

return rv.Validate(url, map[string]string{}, expectedSignature) &&
rv.validateBody(body, bodySHA256)
}

func compare(x, y string) bool {
Expand Down
16 changes: 5 additions & 11 deletions client/request_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ var (
"Caller": "+14158675309",
"From": "+14158675309",
}
jsonBody = []byte(`{"property": "value", "boolean": true}`)
formBody = []byte(`property=value&boolean=true`)
body = []byte(`{"property": "value", "boolean": true}`)
)

func TestRequestValidator_Validate(t *testing.T) {
Expand Down Expand Up @@ -65,24 +64,19 @@ func TestRequestValidator_Validate(t *testing.T) {
func TestRequestValidator_ValidateBody(t *testing.T) {
t.Parallel()

t.Run("returns true when validation succeeds with json body", func(t *testing.T) {
t.Run("returns true when validation succeeds", func(t *testing.T) {
theURL := testURL + "&bodySHA256=" + bodyHash
signatureWithBodyHash := "a9nBmqA0ju/hNViExpshrM61xv4="
assert.True(t, validator.ValidateBody(theURL, jsonBody, signatureWithBodyHash))
})

t.Run("returns true when validation succeeds with form body", func(t *testing.T) {
expectedSignature := "NBdBDr/T/lgjI+tlgpXjKZQZs/k="
assert.True(t, validator.ValidateBody(testURL, formBody, expectedSignature))
assert.True(t, validator.ValidateBody(theURL, body, signatureWithBodyHash))
})

t.Run("returns false when validation fails", func(t *testing.T) {
assert.False(t, validator.ValidateBody(testURL, jsonBody, signature))
assert.False(t, validator.ValidateBody(testURL, body, signature))
})

t.Run("returns true when there's no other parameters and the signature is right", func(t *testing.T) {
theURL := "https://mycompany.com/myapp.php?bodySHA256=" + bodyHash
signatureForURL := "y77kIzt2vzLz71DgmJGsen2scGs="
assert.True(t, validator.ValidateBody(theURL, jsonBody, signatureForURL))
assert.True(t, validator.ValidateBody(theURL, body, signatureForURL))
})
}

0 comments on commit dcfcf11

Please sign in to comment.