// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package validation import ( "net" "net/url" "regexp" "strings" "code.gitea.io/gitea/modules/setting" ) var loopbackIPBlocks []*net.IPNet var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`) func init() { for _, cidr := range []string{ "127.0.0.0/8", // IPv4 loopback "::1/128", // IPv6 loopback } { if _, block, err := net.ParseCIDR(cidr); err == nil { loopbackIPBlocks = append(loopbackIPBlocks, block) } } } func isLoopbackIP(ip string) bool { pip := net.ParseIP(ip) if pip == nil { return false } for _, block := range loopbackIPBlocks { if block.Contains(pip) { return true } } return false } // IsValidURL checks if URL is valid func IsValidURL(uri string) bool { if u, err := url.ParseRequestURI(uri); err != nil || (u.Scheme != "http" && u.Scheme != "https") || !validPort(portOnly(u.Host)) { return false } return true } // IsAPIURL checks if URL is current Gitea instance API URL func IsAPIURL(uri string) bool { return strings.HasPrefix(strings.ToLower(uri), strings.ToLower(setting.AppURL+"api")) } // IsValidExternalURL checks if URL is valid external URL func IsValidExternalURL(uri string) bool { if !IsValidURL(uri) || IsAPIURL(uri) { return false } u, err := url.ParseRequestURI(uri) if err != nil { return false } // Currently check only if not loopback IP is provided to keep compatibility if isLoopbackIP(u.Hostname()) || strings.ToLower(u.Hostname()) == "localhost" { return false } // TODO: Later it should be added to allow local network IP addreses // only if allowed by special setting return true } // IsValidExternalTrackerURLFormat checks if URL matches required syntax for external trackers func IsValidExternalTrackerURLFormat(uri string) bool { if !IsValidExternalURL(uri) { return false } // check for typoed variables like /{index/ or /[repo} for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) { if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") { return false } } return true }