fix: normalize IPv4-mapped cloud-metadata address to its IPv4 form before error reporting
- IPv4-mapped cloud metadata (::ffff:169.254.169.254) previously fell through the IPv4-mapped IPv6 detection block and returned the generic private-IP error instead of the cloud-metadata error, making the two cases inconsistent - The IPv4-mapped error path used ip.String() (the raw ::ffff:… form) directly rather than sanitizeIPForError, potentially leaking the unsanitized IPv6 address in error messages visible to callers - Now extracts the IPv4 from the mapped address before both the cloud-metadata comparison and the sanitization call, so ::ffff:169.254.169.254 produces the same "access to cloud metadata endpoints is blocked" error as 169.254.169.254 and the error message is always sanitized through the shared helper - Updated the corresponding test to assert the cloud-metadata message and the absence of the raw IPv6 representation in the error text
This commit is contained in:
@@ -294,7 +294,14 @@ func ValidateExternalURL(rawURL string, options ...ValidationOption) (string, er
|
||||
continue
|
||||
}
|
||||
if network.IsPrivateIP(ipv4) {
|
||||
return "", fmt.Errorf("connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: %s)", ip.String())
|
||||
// Normalize to the extracted IPv4 for both the cloud-metadata special-case
|
||||
// and sanitization, so ::ffff:169.254.169.254 produces the same error as
|
||||
// 169.254.169.254 and doesn't leak the raw IPv6 form in messages.
|
||||
sanitizedIPv4 := sanitizeIPForError(ipv4.String())
|
||||
if ipv4.String() == "169.254.169.254" {
|
||||
return "", fmt.Errorf("access to cloud metadata endpoints is blocked for security (detected: %s)", sanitizedIPv4)
|
||||
}
|
||||
return "", fmt.Errorf("connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: %s)", sanitizedIPv4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1175,7 +1175,8 @@ func TestValidateExternalURL_WithAllowRFC1918_IPv4MappedIPv6Allowed(t *testing.T
|
||||
|
||||
func TestValidateExternalURL_WithAllowRFC1918_IPv4MappedMetadataBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
// ::ffff:169.254.169.254 maps to the cloud metadata IP; must stay blocked.
|
||||
// ::ffff:169.254.169.254 maps to the cloud metadata IP; must stay blocked and
|
||||
// produce the same cloud-metadata error as the non-mapped address.
|
||||
_, err := ValidateExternalURL(
|
||||
"http://[::ffff:169.254.169.254]",
|
||||
WithAllowHTTP(),
|
||||
@@ -1185,4 +1186,10 @@ func TestValidateExternalURL_WithAllowRFC1918_IPv4MappedMetadataBlocked(t *testi
|
||||
if err == nil {
|
||||
t.Fatal("expected IPv4-mapped metadata address to be blocked, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "cloud metadata") {
|
||||
t.Errorf("expected cloud-metadata error for ::ffff:169.254.169.254, got: %v", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "ffff") {
|
||||
t.Errorf("error message must not leak the raw IPv6 form, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user