}
        }
 
-       if !strings.Contains(mmi.RepoRoot, "://") {
-               return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, mmi.RepoRoot)
+       if err := validateRepoRootScheme(mmi.RepoRoot); err != nil {
+               return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err)
        }
        rr := &repoRoot{
                vcs:      vcsByCmd(mmi.VCS),
        return rr, nil
 }
 
+// validateRepoRootScheme returns an error if repoRoot does not seem
+// to have a valid URL scheme. At this point we permit things that
+// aren't valid URLs, although later, if not using -insecure, we will
+// restrict repoRoots to be valid URLs. This is only because we've
+// historically permitted them, and people may depend on that.
+func validateRepoRootScheme(repoRoot string) error {
+       end := strings.Index(repoRoot, "://")
+       if end <= 0 {
+               return errors.New("no scheme")
+       }
+
+       // RFC 3986 section 3.1.
+       for i := 0; i < end; i++ {
+               c := repoRoot[i]
+               switch {
+               case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
+                       // OK.
+               case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
+                       // OK except at start.
+                       if i == 0 {
+                               return errors.New("invalid scheme")
+                       }
+               default:
+                       return errors.New("invalid scheme")
+               }
+       }
+
+       return nil
+}
+
 var fetchGroup singleflight.Group
 var (
        fetchCacheMu sync.Mutex
 
                }
        }
 }
+
+func TestValidateRepoRootScheme(t *testing.T) {
+       tests := []struct {
+               root string
+               err  string
+       }{
+               {
+                       root: "",
+                       err:  "no scheme",
+               },
+               {
+                       root: "http://",
+                       err:  "",
+               },
+               {
+                       root: "a://",
+                       err:  "",
+               },
+               {
+                       root: "a#://",
+                       err:  "invalid scheme",
+               },
+               {
+                       root: "-config://",
+                       err:  "invalid scheme",
+               },
+       }
+
+       for _, test := range tests {
+               err := validateRepoRootScheme(test.root)
+               if err == nil {
+                       if test.err != "" {
+                               t.Errorf("validateRepoRootScheme(%q) = nil, want %q", test.root, test.err)
+                       }
+               } else if test.err == "" {
+                       if err != nil {
+                               t.Errorf("validateRepoRootScheme(%q) = %q, want nil", test.root, test.err)
+                       }
+               } else if err.Error() != test.err {
+                       t.Errorf("validateRepoRootScheme(%q) = %q, want %q", test.root, err, test.err)
+               }
+       }
+}