feat: add url shortner
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package shorturl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MaxPathLength caps the long URL length we'll accept. 2KB is well beyond any
|
||||
// real `?hl=` URL we generate while preventing pathological storage abuse.
|
||||
const MaxPathLength = 2048
|
||||
|
||||
// ValidatePath verifies that s is a same-site relative URL — starts with "/",
|
||||
// no scheme/host, no NUL bytes, within length. Prevents using the shortener
|
||||
// as an open redirect to arbitrary external domains.
|
||||
func ValidatePath(s string) error {
|
||||
if s == "" {
|
||||
return fmt.Errorf("url is empty")
|
||||
}
|
||||
if len(s) > MaxPathLength {
|
||||
return fmt.Errorf("url exceeds %d bytes", MaxPathLength)
|
||||
}
|
||||
if !strings.HasPrefix(s, "/") {
|
||||
return fmt.Errorf("url must start with '/'")
|
||||
}
|
||||
// "//host/…" is protocol-relative — disallow.
|
||||
if strings.HasPrefix(s, "//") {
|
||||
return fmt.Errorf("url must not be protocol-relative")
|
||||
}
|
||||
if strings.ContainsRune(s, 0) {
|
||||
return fmt.Errorf("url contains NUL byte")
|
||||
}
|
||||
// No scheme allowed: scan up to the first '/' or '?' for ':'.
|
||||
for i, c := range s {
|
||||
if c == '?' || c == '#' {
|
||||
break
|
||||
}
|
||||
if c == ':' && i > 0 {
|
||||
return fmt.Errorf("url must not contain a scheme")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user