Skip to content

Commit d7883ae

Browse files
authored
Reject private and loopback IPs in checkMetadataURL (#21)
The URL check accepted any HTTPS host including loopback, link-local, and private IPs. A compromised registry could point download URLs at cloud metadata endpoints or internal services. Now resolves the hostname and rejects private, loopback, and link-local addresses.
1 parent 938c01e commit d7883ae

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

fetch/resolver.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"net"
78
"net/url"
89
"strings"
910
"unicode"
@@ -183,12 +184,28 @@ func checkMetadataURL(raw string) error {
183184
if u.Scheme != "https" {
184185
return fmt.Errorf("%w: scheme %q", ErrUnsafeURL, u.Scheme)
185186
}
186-
if u.Hostname() == "" {
187+
hostname := u.Hostname()
188+
if hostname == "" {
187189
return fmt.Errorf("%w: empty host", ErrUnsafeURL)
188190
}
191+
if isPrivateHost(hostname) {
192+
return fmt.Errorf("%w: private/loopback host %q", ErrUnsafeURL, hostname)
193+
}
189194
return nil
190195
}
191196

197+
func isPrivateHost(hostname string) bool {
198+
ip := net.ParseIP(hostname)
199+
if ip == nil {
200+
ips, err := net.LookupIP(hostname)
201+
if err != nil || len(ips) == 0 {
202+
return false
203+
}
204+
ip = ips[0]
205+
}
206+
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast()
207+
}
208+
192209
func filenameFromURL(url string) string {
193210
if idx := strings.LastIndex(url, "/"); idx >= 0 {
194211
return url[idx+1:]

fetch/resolver_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ func TestCheckMetadataURL(t *testing.T) {
141141
{"https:///path/only", false},
142142
{"//169.254.169.254/latest/meta-data/", false},
143143
{"169.254.169.254/latest/meta-data/", false},
144+
{"https://169.254.169.254/latest/meta-data/", false},
145+
{"https://127.0.0.1/something", false},
146+
{"https://[::1]/something", false},
144147
{"", false},
145148
{"\x00https://evil", false},
146149
}

0 commit comments

Comments
 (0)