diff --git a/uri.go b/uri.go index 70004f1..528e884 100644 --- a/uri.go +++ b/uri.go @@ -289,6 +289,9 @@ func (u *URI) parse(host, uri []byte, isTLS bool) error { if len(host) == 0 || bytes.Contains(uri, strColonSlashSlash) { scheme, newHost, newURI := splitHostURI(host, uri) + if len(scheme) > 0 && !isValidScheme(scheme) { + return fmt.Errorf("invalid scheme %q", scheme) + } u.SetSchemeBytes(scheme) host = newHost uri = newURI @@ -379,6 +382,28 @@ func validUserinfo(userinfo []byte) bool { return true } +func isValidScheme(scheme []byte) bool { + if len(scheme) == 0 { + return false + } + first := scheme[0] + if (first < 'a' || first > 'z') && (first < 'A' || first > 'Z') { + return false + } + for i := 1; i < len(scheme); i++ { + c := scheme[i] + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') { + continue + } + switch c { + case '+', '-', '.': + continue + } + return false + } + return true +} + // parseHost parses host as an authority without user // information. That is, as host[:port]. // diff --git a/uri_test.go b/uri_test.go index 68e5551..cb2bb3c 100644 --- a/uri_test.go +++ b/uri_test.go @@ -131,6 +131,20 @@ func TestURIRejectInvalidIPv6(t *testing.T) { } } +func TestURIRejectInvalidScheme(t *testing.T) { + t.Parallel() + + var u URI + if err := u.Parse(nil, []byte("https>://vulndetector.com/path")); err == nil { + t.Fatalf("expected invalid scheme error, got nil") + } + + var relative URI + if err := relative.Parse(nil, []byte("/relative")); err != nil { + t.Fatalf("unexpected error for relative path: %v", err) + } +} + func TestURIUpdate(t *testing.T) { t.Parallel()