@@ -2640,6 +2640,68 @@ TEST(X509Test, NameConstraints) {
26402640 // An incomplete IPv6 literal is also rejected.
26412641 {GEN_URI, " foo://[2001:db8::1" , " [2001:db8::1]" ,
26422642 X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2643+
2644+ // RFC 3986 §3.2 defines authority = [userinfo "@"] host [":" port].
2645+ // URIs with userinfo are rejected to prevent host confusion: without
2646+ // this, "spiffe://x.team-a.corp:x@team-b.corp/admin" would be matched
2647+ // against "x.team-a.corp" instead of the actual host "team-b.corp",
2648+ // bypassing permittedSubtrees or evading excludedSubtrees.
2649+ //
2650+ // Basic userinfo before host.
2651+ {GEN_URI, " foo://user@example.com" , " example.com" ,
2652+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2653+ // Userinfo with colon (user:password style) — the colon in userinfo
2654+ // would previously be mistaken for a port delimiter, extracting the
2655+ // userinfo prefix as the host.
2656+ {GEN_URI, " spiffe://x.team-a.corp:x@team-b.corp/admin" , " team-a.corp" ,
2657+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2658+ {GEN_URI, " spiffe://x.team-a.corp:x@team-b.corp/admin" , " team-b.corp" ,
2659+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2660+ // Userinfo with path, query, and fragment components.
2661+ {GEN_URI, " foo://user@example.com/path" , " example.com" ,
2662+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2663+ {GEN_URI, " foo://user@example.com?query" , " example.com" ,
2664+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2665+ {GEN_URI, " foo://user@example.com#fragment" , " example.com" ,
2666+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2667+ // '@' in path (after '/') is not userinfo and should not be rejected.
2668+ {GEN_URI, " foo://example.com/@user" , " example.com" , X509_V_OK,
2669+ X509_V_ERR_EXCLUDED_VIOLATION},
2670+ {GEN_URI, " foo://example.com/path@thing" , " .example.com" ,
2671+ X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK},
2672+ // '@' after '?' or '#' is not in the authority and is not rejected.
2673+ // However, since the host parser doesn't treat '?' or '#' as authority
2674+ // terminators, the extracted host contains invalid FQDN characters and
2675+ // is rejected by FQDN validation.
2676+ {GEN_URI, " foo://example.com?x@y" , " example.com" ,
2677+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2678+ {GEN_URI, " foo://example.com#x@y" , " example.com" ,
2679+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2680+ // Empty userinfo and user:pass userinfo are both rejected.
2681+ {GEN_URI, " foo://@example.com" , " example.com" ,
2682+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2683+ {GEN_URI, " foo://user:pass@example.com" , " example.com" ,
2684+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2685+
2686+ // Trailing dot normalization: "host." and "host" are the same FQDN.
2687+ // This matches the normalization in nc_dns().
2688+ {GEN_URI, " spiffe://admin.team-a.corp./admin" , " .team-a.corp" ,
2689+ X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION},
2690+ {GEN_URI, " spiffe://admin.team-a.corp/admin" , " .team-a.corp." ,
2691+ X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION},
2692+ {GEN_URI, " spiffe://team-a.corp./admin" , " team-a.corp" ,
2693+ X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION},
2694+
2695+ // FQDN validation: hosts must contain only a-z, A-Z, 0-9, '-', '.'.
2696+ // Percent-encoded hosts are rejected, preventing equivalence bypasses
2697+ // (e.g., "b%61d.com" should not evade an exclusion for ".bad.com").
2698+ {GEN_URI, " spiffe://evil.b%61d.com/resource" , " .bad.com" ,
2699+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2700+ {GEN_URI, " foo://ex%61mple.com/path" , " example.com" ,
2701+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
2702+ // Underscores are not valid in FQDNs.
2703+ {GEN_URI, " foo://my_host.example.com/path" , " .example.com" ,
2704+ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
26432705 };
26442706 for (const auto &t : kTests ) {
26452707 SCOPED_TRACE (t.type );
0 commit comments