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:
GitHub Actions
2026-03-18 03:05:10 +00:00
parent 8b4e0afd43
commit 3bc798bc9d
2 changed files with 16 additions and 2 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}