[utils/handlers] Verify alias contains only unreserved URI chars

This commit is contained in:
Richard Huang 2020-05-15 02:16:23 -07:00
parent 58c65bc261
commit c25d50c35f
No known key found for this signature in database
GPG key ID: FFDEF81D05C2EC94
4 changed files with 40 additions and 23 deletions

View file

@ -66,6 +66,11 @@ func (a *App) CreateShortURL(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Invalid URL", http.StatusBadRequest) http.Error(w, "Invalid URL", http.StatusBadRequest)
return return
} }
// Verify alias is valid
if u.Alias != "" && !utils.IsValidAlias(u.Alias) {
http.Error(w, "Invalid Alias", http.StatusBadRequest)
return
}
// Check if URL entry already exists // Check if URL entry already exists
existingURL := &URLEntry{} existingURL := &URLEntry{}
a.DB.Where("url = ?", u.URL).Find(existingURL) a.DB.Where("url = ?", u.URL).Find(existingURL)

View file

@ -80,6 +80,9 @@ func TestInvalidCreate(t *testing.T) {
"no colon": {url: "http//google.com", alias: "ggl", status: http.StatusBadRequest}, "no colon": {url: "http//google.com", alias: "ggl", status: http.StatusBadRequest},
"empty": {url: "", alias: "", status: http.StatusBadRequest}, "empty": {url: "", alias: "", status: http.StatusBadRequest},
"asdf": {url: "asdf", alias: "", status: http.StatusBadRequest}, "asdf": {url: "asdf", alias: "", status: http.StatusBadRequest},
"spaces": {url: "https://google.com", alias: "oh no spaces", status: http.StatusBadRequest},
"question mark": {url: "https://google.com", alias: "huh?", status: http.StatusBadRequest},
"percent": {url: "https://google.com", alias: "test%20stuff", status: http.StatusBadRequest},
} }
app := setup() app := setup()

View file

@ -2,22 +2,23 @@ package utils
import ( import (
"math/rand" "math/rand"
"strings" "regexp"
"time" "time"
) )
// Alphanumeric charset const (
const CHARSET = "abcdefghijklmnopqrstuvwxyz" + // Alphanumeric charset
CHARSET = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// RFC 3986 Section 2.3 URI Unreserved Characters
URIUnreservedChars = `^([A-Za-z0-9_.~-])+$`
)
// Tests whether a string is in the alphanumeric charset // Tests whether a string is in the alphanumeric charset
func IsAlphaNum(str string) bool { func IsValidAlias(str string) bool {
for _, r := range []rune(str) { valid, err := regexp.MatchString(URIUnreservedChars, str)
if !strings.ContainsRune(CHARSET, r) { return valid && err == nil
return false
}
}
return true
} }
// Generate a random alphanumeric string of given length // Generate a random alphanumeric string of given length

View file

@ -4,23 +4,31 @@ import (
"testing" "testing"
) )
func TestIsAlphaNum(t *testing.T) { func TestValidAlias(t *testing.T) {
tests := map[string]struct { tests := map[string]struct {
name string name string
str string str string
val bool val bool
}{ }{
"basic": {str: "hello", val: true}, "LOWERCASE": {str: "lowercase", val: true},
"dash": {str: "-", val: false}, "UPPERCASE": {str: "UPPERCASE", val: true},
"period": {str: ".", val: false}, "underscore": {str: "_", val: true},
"period": {str: ".", val: true},
"dash": {str: "-", val: true},
"tilde": {str: "~", val: true},
"adsf": {str: "a_S-d.f~", val: true},
"question mark": {str: "?", val: false}, "question mark": {str: "?", val: false},
"backslash": {str: "?", val: false}, "backslash": {str: "\\", val: false},
"asterix": {str: "*", val: false},
"ampersand": {str: "&", val: false},
"space": {str: " ", val: false},
"percent": {str: "%", val: false},
} }
for name, tc := range tests { for name, tc := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
if IsAlphaNum(tc.str) != tc.val { if IsValidAlias(tc.str) != tc.val {
t.Errorf("For '%s', expected %t. Got %t instead.", tc.str, tc.val, IsAlphaNum(tc.str)) t.Errorf("For '%s', expected %t. Got %t instead.", tc.str, tc.val, IsValidAlias(tc.str))
} }
}) })
} }
@ -29,7 +37,7 @@ func TestIsAlphaNum(t *testing.T) {
// For the code coverage why not // For the code coverage why not
func TestRandString(t *testing.T) { func TestRandString(t *testing.T) {
str := RandString(6) str := RandString(6)
if len(str) == 6 && IsAlphaNum(str) { if len(str) == 6 && IsValidAlias(str) {
return return
} }
t.Errorf("Seriously? How?") t.Errorf("Seriously? How?")