@@ -20,8 +20,7 @@ pub mod http;
2020#[ cfg( feature = "related-origins-client" ) ]
2121pub use http:: { HttpPolicy , ReqwestRelatedOriginsClient } ;
2222
23- /// WebAuthn L3 §5.11 requires support for at least 5 registrable origin labels;
24- /// we cap at exactly 5 to bound abuse surface.
23+ /// WebAuthn L3 §5.11 minimum; capped at 5 to bound abuse surface.
2524const MAX_REGISTRABLE_LABELS : usize = 5 ;
2625
2726#[ derive( Debug , Clone ) ]
@@ -62,9 +61,7 @@ pub enum RelatedOriginsError {
6261}
6362
6463impl RelatedOriginsError {
65- /// Static, log-safe variant discriminant. Use in place of the `Debug` /
66- /// `Display` impls when the error may carry reqwest- or serde-supplied
67- /// text (IPs, body snippets) that should not reach operator logs.
64+ /// Log-safe variant discriminant; `Debug`/`Display` may carry reqwest/serde text with IPs or body snippets.
6865 pub fn kind ( & self ) -> & ' static str {
6966 match self {
7067 RelatedOriginsError :: FetchFailed ( _) => "fetch_failed" ,
@@ -84,8 +81,6 @@ struct WellKnownDocument {
8481}
8582
8683/// Runs the WebAuthn L3 §5.11.1 related-origins validation procedure.
87- /// Returns `Ok(())` when a listed origin matches `caller_origin`, otherwise
88- /// returns the first fetch/parse error or [`RelatedOriginsError::NoMatchingOrigin`].
8984pub async fn validate_related_origins (
9085 caller_origin : & Origin ,
9186 rp_id : & RelyingPartyId ,
@@ -140,8 +135,7 @@ pub async fn validate_related_origins(
140135 Err ( RelatedOriginsError :: NoMatchingOrigin )
141136}
142137
143- /// First label of `host`'s registrable domain (eTLD+1), or `None` when the host
144- /// has no registrable domain (e.g. bare eTLD, IP literal, unknown TLD).
138+ /// First label of `host`'s registrable domain (eTLD+1), or `None` if `host` has no registrable domain.
145139pub ( crate ) fn registrable_origin_label ( host : & str , psl : & dyn PublicSuffixList ) -> Option < String > {
146140 let registrable = psl. registrable_domain ( host) ?;
147141 let label = registrable. split ( '.' ) . next ( ) ?;
@@ -151,8 +145,7 @@ pub(crate) fn registrable_origin_label(host: &str, psl: &dyn PublicSuffixList) -
151145 Some ( label. to_string ( ) )
152146}
153147
154- /// Effective domain of a URL per HTML §6.2: domain hosts and IP literals; opaque
155- /// hosts and host-less URLs return `None`.
148+ /// Effective domain of `url` per HTML §6.2; `None` for opaque or host-less URLs.
156149fn effective_domain_of ( url : & Url ) -> Option < String > {
157150 match url. host ( ) ? {
158151 Host :: Domain ( d) => Some ( d. to_string ( ) ) ,
@@ -161,10 +154,7 @@ fn effective_domain_of(url: &Url) -> Option<String> {
161154 }
162155}
163156
164- /// WebAuthn L3 §5.11.1 step 4.f: tuple-origin equality (scheme, host, port)
165- /// between the caller's origin and the listed entry. The spec defers to HTML
166- /// §7.5 "same origin", which compares only those three components, so
167- /// userinfo, paths, queries and fragments on the listed URL are ignored.
157+ /// Tuple-origin equality (scheme, host, port) per WebAuthn L3 §5.11.1 step 4.f and HTML §7.5; userinfo, path, query and fragment on `listed` are ignored.
168158fn same_origin ( caller : & Origin , listed : & Url ) -> bool {
169159 if caller. scheme . as_str ( ) != listed. scheme ( ) {
170160 return false ;
@@ -179,25 +169,21 @@ fn same_origin(caller: &Origin, listed: &Url) -> bool {
179169 caller_port == listed. port_or_known_default ( )
180170}
181171
182- /// Default port for a WebAuthn scheme, per the WHATWG URL Standard's
183- /// special-scheme port table.
172+ /// Default port per the WHATWG URL Standard special-scheme port table.
184173fn default_port ( scheme : Scheme ) -> Option < u16 > {
185174 match scheme {
186175 Scheme :: Https => Some ( 443 ) ,
187176 Scheme :: Http => Some ( 80 ) ,
188177 }
189178}
190179
191- /// Fetch §2.5 `application/json` essence check: case-insensitive, parameters
192- /// ignored. Used for WebAuthn L3 §5.11.1 step 2.a.
180+ /// Fetch §2.5 `application/json` essence check; used for WebAuthn L3 §5.11.1 step 2.a.
193181fn is_application_json ( value : & str ) -> bool {
194182 let essence = value. split ( ';' ) . next ( ) . unwrap_or ( "" ) . trim ( ) ;
195183 essence. eq_ignore_ascii_case ( "application/json" )
196184}
197185
198- /// `RelatedOriginsHttpClient` that always refuses; preserves today's
199- /// "mismatching rp.id is a hard error" semantics for callers that do not opt
200- /// into related-origin fetches.
186+ /// `RelatedOriginsHttpClient` that always refuses; preserves strict rp.id matching when callers do not opt into related-origin fetches.
201187#[ derive( Debug , Clone , Copy , Default ) ]
202188pub struct NoRelatedOriginsClient ;
203189
@@ -351,9 +337,7 @@ mod tests {
351337
352338 #[ tokio:: test]
353339 async fn label_cap_allows_fifth_distinct_label_match ( ) {
354- // §5.11.1 step 4.e: the 5th distinct label still satisfies size < max
355- // at step 4.g, so it is recorded and the same-origin check at 4.f
356- // succeeds. Pins the cap boundary at 5.
340+ // §5.11.1 step 4.e: the 5th distinct label is still within the cap.
357341 let body = r#"{"origins":[
358342 "https://a.com",
359343 "https://b.com",
@@ -735,9 +719,7 @@ mod tests {
735719
736720 #[ tokio:: test]
737721 async fn ipv6_listed_origin_skipped_no_registrable_label ( ) {
738- // IPv6 host has no registrable label, so the loop skips at step 4.c/4.d
739- // before reaching same-origin. Bare IP-literal origins therefore
740- // cannot match via related-origins, matching browser behaviour.
722+ // IPv6 host has no registrable label; loop skips at step 4.c/4.d.
741723 let http = MockClient {
742724 response : Ok ( json_ct ( r#"{"origins":["https://[::1]"]}"# ) ) ,
743725 } ;
0 commit comments