[utils/handlers] Verify alias contains only unreserved URI chars
This commit is contained in:
parent
58c65bc261
commit
c25d50c35f
4 changed files with 40 additions and 23 deletions
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?")
|
||||||
|
|
Loading…
Reference in a new issue