2022-06-11 21:07:42 +00:00
|
|
|
package valid
|
|
|
|
|
2022-08-13 14:14:26 +00:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
)
|
|
|
|
|
2022-06-11 21:07:42 +00:00
|
|
|
var URIs = [][]byte{
|
|
|
|
[]byte("http://"),
|
|
|
|
[]byte("https://"),
|
|
|
|
[]byte("ftp://"),
|
|
|
|
[]byte("mailto:"),
|
|
|
|
}
|
|
|
|
|
|
|
|
var Paths = [][]byte{
|
|
|
|
[]byte("/"),
|
|
|
|
[]byte("./"),
|
|
|
|
[]byte("../"),
|
|
|
|
}
|
2022-08-13 14:14:26 +00:00
|
|
|
|
|
|
|
// TODO: documentation
|
|
|
|
func IsSafeURL(url []byte) bool {
|
|
|
|
nLink := len(url)
|
|
|
|
for _, path := range Paths {
|
|
|
|
nPath := len(path)
|
|
|
|
linkPrefix := url[:nPath]
|
|
|
|
if nLink >= nPath && bytes.Equal(linkPrefix, path) {
|
|
|
|
if nLink == nPath {
|
|
|
|
return true
|
|
|
|
} else if isAlnum(url[nPath]) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prefix := range URIs {
|
|
|
|
// TODO: handle unicode here
|
|
|
|
// case-insensitive prefix test
|
|
|
|
nPrefix := len(prefix)
|
|
|
|
if nLink > nPrefix {
|
|
|
|
linkPrefix := bytes.ToLower(url[:nPrefix])
|
|
|
|
if bytes.Equal(linkPrefix, prefix) && isAlnum(url[nPrefix]) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// isAlnum returns true if c is a digit or letter
|
|
|
|
// TODO: check when this is looking for ASCII alnum and when it should use unicode
|
|
|
|
func isAlnum(c byte) bool {
|
|
|
|
return (c >= '0' && c <= '9') || isLetter(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// isLetter returns true if c is ascii letter
|
|
|
|
func isLetter(c byte) bool {
|
|
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
|
|
}
|