Trim trailing dot from trusted IMDS hostnames#428
Conversation
DNS resolvers may return hostnames with a trailing dot (FQDN form), e.g. `metadata.google.internal.`. The direct map lookup failed to match these, risking false-positive stored-SSRF blocks for GCP IMDS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of inlining strings.TrimRight in isTrustedHostname, route through the existing NormalizeHostname helper (which already handles invisible chars, URL encoding, case, and IDN). Add trailing-dot stripping to NormalizeHostname itself so all callers get the fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| func isTrustedHostname(hostname string) bool { | ||
| // Check if the hostname is in the trusted hosts map | ||
| _, exists := trustedHosts[hostname] | ||
| _, exists := trustedHosts[helpers.NormalizeHostname(hostname)] |
There was a problem hiding this comment.
Replacing the direct trustedHosts map lookup with helpers.NormalizeHostname(hostname) adds expensive work (URL unescape + IDN conversion) on a likely hot path; consider pre-normalizing or caching hostnames.
| _, exists := trustedHosts[helpers.NormalizeHostname(hostname)] | |
| _, exists := trustedHosts[hostname] |
Details
✨ AI Reasoning
The code previously did a direct map lookup of the hostname (cheap O(1)). The diff replaces that with helpers.NormalizeHostname(hostname), which runs TrimInvisible, url.QueryUnescape, strings.ToLower, and idna.ToUnicode. These operations allocate and perform non-trivial work (especially IDN conversion). Because isTrustedHostname is likely called per-request for each resolved host, this introduces a performance regression by doing expensive normalization on every check. The added TrimSuffix itself is cheap, but the overall NormalizeHostname call makes the previously trivial lookup much heavier. A lightweight alternative would be to normalize trustedHosts keys up-front or cache normalization results.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
Summary
DNS resolvers sometimes return fully-qualified domain names with a trailing dot (e.g.
metadata.google.internal.). The previous direct map lookup inisTrustedHostnamedid not match this form, so GCP IMDS requests could be incorrectly flagged as stored SSRF.strings.ToLower+strings.TrimRight(hostname, ".")before the map lookup"strings"importThis is the same fix already applied in firewall-go (PR #459). Ported to Python, Ruby, .NET, PHP, and Java.
Test plan
lib/request-processor/passSummary by Aikido
🐛 Bugfixes
🔧 Refactors
More info