Refuse brackets around anything but an IPv6 address in an image URL host
check / check (push) Waiting to run
check / check (push) Waiting to run
The host check accepted any hex digits, : and . in brackets, so a host such as [203.0.113.5] or [abc] got through it, and Go's URL parser then refused the source URL, which the route answered with 401. The text in brackets must now parse as an IPv6 address, as Go's URL parser asks, or the host is refused with 400. The comment above the check now says only what is true of it. Model: opus-5-5
This commit is contained in:
@@ -3,6 +3,7 @@ package imgcache
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -50,11 +51,13 @@ var sizeFormatRegex = regexp.MustCompile(
|
|||||||
// percentEscapeRegex matches one percent-escape, such as "%2e"
|
// percentEscapeRegex matches one percent-escape, such as "%2e"
|
||||||
var percentEscapeRegex = regexp.MustCompile(`%[0-9A-Fa-f]{2}`)
|
var percentEscapeRegex = regexp.MustCompile(`%[0-9A-Fa-f]{2}`)
|
||||||
|
|
||||||
// hostRegex matches the host of an image URL: a host name or an IPv4 address
|
// hostRegex matches the forms the host of an image URL may take: a host name
|
||||||
// (letters, digits, "-", "_" and "."), or an IPv6 address in brackets, either
|
// or an IPv4 address (letters, digits, "-", "_" and "."), or an IPv6 address
|
||||||
// one optionally followed by ":" and a port, which may be empty
|
// in brackets (hex digits, ":" and "."), either one optionally followed by ":"
|
||||||
|
// and a port, which may be empty. Its one group holds the text in brackets,
|
||||||
|
// which parseImageComponents also checks is an IPv6 address.
|
||||||
var hostRegex = regexp.MustCompile(
|
var hostRegex = regexp.MustCompile(
|
||||||
`^([A-Za-z0-9_.-]+|\[[0-9A-Fa-f:.]+\])(:[0-9]*)?$`)
|
`^(?:[A-Za-z0-9_.-]+|\[([0-9A-Fa-f:.]+)\])(?::[0-9]*)?$`)
|
||||||
|
|
||||||
// ParsedURL contains the parsed components of an image proxy URL.
|
// ParsedURL contains the parsed components of an image proxy URL.
|
||||||
type ParsedURL struct {
|
type ParsedURL struct {
|
||||||
@@ -147,14 +150,22 @@ func parseImageComponents(remainder string) (*ParsedURL, error) {
|
|||||||
return nil, ErrMissingHost
|
return nil, ErrMissingHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go's URL parser, which the allowlist check and the fetcher use, reads a
|
// Only the forms hostRegex describes are accepted. A "?", "#" or "@" in
|
||||||
// host that hostRegex does not match as a different host, or refuses it:
|
// the host would make Go's URL parser, which the allowlist check and the
|
||||||
// what comes before an "@" is a user name and password, and what comes
|
// fetcher use, read a different host from the one allowlisted and cached
|
||||||
// after a "?" or "#" is a query or a fragment
|
hostMatch := hostRegex.FindStringSubmatch(host)
|
||||||
if !hostRegex.MatchString(host) {
|
if hostMatch == nil {
|
||||||
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
|
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go's URL parser refuses brackets around anything but an IPv6 address
|
||||||
|
if inBrackets := hostMatch[1]; inBrackets != "" {
|
||||||
|
addr, err := netip.ParseAddr(inBrackets)
|
||||||
|
if err != nil || !addr.Is6() {
|
||||||
|
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Host names do not depend on letter case, so the allowlist, the upstream
|
// Host names do not depend on letter case, so the allowlist, the upstream
|
||||||
// connection limits, the cache and the signature see one spelling of each
|
// connection limits, the cache and the signature see one spelling of each
|
||||||
// host
|
// host
|
||||||
|
|||||||
Reference in New Issue
Block a user