diff --git a/default.go b/default.go index 61979d0..4b20d3a 100644 --- a/default.go +++ b/default.go @@ -6,6 +6,7 @@ import ( "github.com/PuerkitoBio/goquery" "io" "io/ioutil" + "net" "net/http" "net/url" "path" @@ -19,6 +20,35 @@ const ( maxBodySizeBytes = 20971520 ) +var privateIPBlocks []*net.IPNet + +func init() { + for _, cidr := range []string{ + "127.0.0.0/8", // IPv4 loopback + "10.0.0.0/8", // RFC1918 + "172.16.0.0/12", // RFC1918 + "192.168.0.0/16", // RFC1918 + "::1/128", // IPv6 loopback + "fe80::/10", // IPv6 link-local + "fc00::/7", // IPv6 unique local addr + } { + _, block, err := net.ParseCIDR(cidr) + if err != nil { + panic(fmt.Errorf("parse error on %q: %v", cidr, err)) + } + privateIPBlocks = append(privateIPBlocks, block) + } +} + +func isPrivateIP(ip net.IP) bool { + for _, block := range privateIPBlocks { + if block.Contains(ip) { + return true + } + } + return false +} + func (api *LinkInfoApi) DefaultLinkHandler(link string) (*LinkInfo, error) { redirects := make([]string, 0) @@ -28,6 +58,12 @@ func (api *LinkInfoApi) DefaultLinkHandler(link string) (*LinkInfo, error) { return nil, err } + if u.Host == "localhost" { + return nil, errors.New("url is localhost") + } else if ip := net.ParseIP(u.Host); ip != nil && isPrivateIP(ip) { + return nil, errors.New("url is a local ip address") + } + var res *http.Response for i := 0; i < 10; i++ { @@ -39,6 +75,19 @@ func (api *LinkInfoApi) DefaultLinkHandler(link string) (*LinkInfo, error) { if (res.StatusCode == 301 || res.StatusCode == 302) && res.Header.Get("Location") != "" { link = res.Header.Get("Location") + + redirectUrl, err := url.Parse(link) + + if err != nil { + return nil, err + } + + if redirectUrl.Host == "localhost" { + return nil, errors.New("url attempted to redirect to localhost") + } else if ip := net.ParseIP(redirectUrl.Host); ip != nil && isPrivateIP(ip) { + return nil, errors.New("host is a local ip address") + } + redirects = append(redirects, link) } else { break @@ -140,7 +189,7 @@ func (api *LinkInfoApi) retrieveHtmlLinkTitle(i *LinkInfo, link string) error { metaTags := make(map[string]string) meta.Each(func(_ int, s *goquery.Selection) { - var key string + var key, value string var exists bool for _, k := range attrKeys { @@ -154,6 +203,14 @@ func (api *LinkInfoApi) retrieveHtmlLinkTitle(i *LinkInfo, link string) error { if key == "" { return } + + value, exists = s.Attr("content") + + if !exists { + return + } + + metaTags[key] = value }) var attr string @@ -161,7 +218,7 @@ func (api *LinkInfoApi) retrieveHtmlLinkTitle(i *LinkInfo, link string) error { if attr, exists = metaTags["og:title"]; exists { i.Title = attr - } else if tag := q.Find("title"); tag.Length() > 0 { + } else if tag := q.Find("head > title"); tag.Length() > 0 { i.Title = strings.TrimSpace(tag.Text()) }