Skip to content

Commit 813f3fa

Browse files
authored
FxA: Track location when parsing URLs (#6981)
I'm hoping this will help us track down https://bugzilla.mozilla.org/show_bug.cgi?id=1991756
1 parent 0981e2d commit 813f3fa

File tree

5 files changed

+90
-19
lines changed

5 files changed

+90
-19
lines changed

components/fxa-client/src/error.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,13 @@ pub enum Error {
167167
#[error("Network error: {0}")]
168168
RequestError(#[from] viaduct::ViaductError),
169169

170-
#[error("Malformed URL error: {0}")]
171-
MalformedUrl(#[from] url::ParseError),
170+
#[error("Malformed URL: {sanitized_url} ({when})")]
171+
MalformedUrl {
172+
/// URL without any query or fragment parts.
173+
/// This is safe to send in an error report because there's no auth information.
174+
sanitized_url: String,
175+
when: String,
176+
},
172177

173178
#[error("Unexpected HTTP status: {0}")]
174179
UnexpectedStatus(#[from] viaduct::UnexpectedStatus),

components/fxa-client/src/internal/config.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
use super::http_client;
6-
use crate::{FxaConfig, Result};
6+
use crate::{internal::util, FxaConfig, Result};
77
use serde_derive::{Deserialize, Serialize};
88
use std::{cell::RefCell, sync::Arc};
99
use url::Url;
@@ -77,11 +77,11 @@ impl Config {
7777
}
7878

7979
pub fn content_url(&self) -> Result<Url> {
80-
Url::parse(&self.content_url).map_err(Into::into)
80+
util::parse_url(&self.content_url, "content_url")
8181
}
8282

8383
pub fn content_url_path(&self, path: &str) -> Result<Url> {
84-
self.content_url()?.join(path).map_err(Into::into)
84+
util::join_url(&self.content_url()?, path, "content_url_path")
8585
}
8686

8787
pub fn client_config_url(&self) -> Result<Url> {
@@ -117,44 +117,57 @@ impl Config {
117117
}
118118

119119
pub fn auth_url(&self) -> Result<Url> {
120-
Url::parse(&self.remote_config()?.auth_url).map_err(Into::into)
120+
util::parse_url(&self.remote_config()?.auth_url, "auth_url")
121121
}
122122

123123
pub fn auth_url_path(&self, path: &str) -> Result<Url> {
124-
self.auth_url()?.join(path).map_err(Into::into)
124+
util::join_url(&self.auth_url()?, path, "auth_url_path")
125125
}
126126

127127
pub fn oauth_url(&self) -> Result<Url> {
128-
Url::parse(&self.remote_config()?.oauth_url).map_err(Into::into)
128+
util::parse_url(&self.remote_config()?.oauth_url, "oauth_url")
129129
}
130130

131131
pub fn oauth_url_path(&self, path: &str) -> Result<Url> {
132-
self.oauth_url()?.join(path).map_err(Into::into)
132+
util::join_url(&self.oauth_url()?, path, "oauth_url_path")
133133
}
134134

135135
pub fn token_server_endpoint_url(&self) -> Result<Url> {
136136
if let Some(token_server_url_override) = &self.token_server_url_override {
137-
return Ok(Url::parse(token_server_url_override)?);
137+
return util::parse_url(
138+
token_server_url_override,
139+
"token_server_endpoint_url (override)",
140+
);
138141
}
139-
Ok(Url::parse(
142+
util::parse_url(
140143
&self.remote_config()?.token_server_endpoint_url,
141-
)?)
144+
"token_server_endpoint_url",
145+
)
142146
}
143147

144148
pub fn authorization_endpoint(&self) -> Result<Url> {
145-
Url::parse(&self.remote_config()?.authorization_endpoint).map_err(Into::into)
149+
util::parse_url(
150+
&self.remote_config()?.authorization_endpoint,
151+
"authorization_endpoint",
152+
)
146153
}
147154

148155
pub fn token_endpoint(&self) -> Result<Url> {
149-
Url::parse(&self.remote_config()?.token_endpoint).map_err(Into::into)
156+
util::parse_url(&self.remote_config()?.token_endpoint, "token_endpoint")
150157
}
151158

152159
pub fn introspection_endpoint(&self) -> Result<Url> {
153-
Url::parse(&self.remote_config()?.introspection_endpoint).map_err(Into::into)
160+
util::parse_url(
161+
&self.remote_config()?.introspection_endpoint,
162+
"introspection_endpoint",
163+
)
154164
}
155165

156166
pub fn userinfo_endpoint(&self) -> Result<Url> {
157-
Url::parse(&self.remote_config()?.userinfo_endpoint).map_err(Into::into)
167+
util::parse_url(
168+
&self.remote_config()?.userinfo_endpoint,
169+
"userinfo_endpoint",
170+
)
158171
}
159172

160173
fn normalize_token_server_url(token_server_url_override: &str) -> String {

components/fxa-client/src/internal/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,15 @@ impl FirefoxAccount {
133133
/// a computer).
134134
pub fn get_pairing_authority_url(&self) -> Result<String> {
135135
// Special case for the production server, we use the shorter firefox.com/pair URL.
136-
if self.state.config().content_url()? == Url::parse(config::CONTENT_URL_RELEASE)? {
136+
let content_url_release = Url::parse(config::CONTENT_URL_RELEASE)
137+
.expect("hardcoded CONTENT_URL_RELEASE failed to parse");
138+
if self.state.config().content_url()? == content_url_release {
137139
return Ok("https://firefox.com/pair".to_owned());
138140
}
139141
// Similarly special case for the China server.
140-
if self.state.config().content_url()? == Url::parse(config::CONTENT_URL_CHINA)? {
142+
let content_url_china = Url::parse(config::CONTENT_URL_CHINA)
143+
.expect("hardcoded CONTENT_URL_CHINA failed to parse");
144+
if self.state.config().content_url()? == content_url_china {
141145
return Ok("https://firefox.com.cn/pair".to_owned());
142146
}
143147
Ok(self.state.config().pair_url()?.into())

components/fxa-client/src/internal/oauth.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl FirefoxAccount {
137137
) -> Result<String> {
138138
let mut url = self.state.config().pair_supp_url()?;
139139
url.query_pairs_mut().append_pair("entrypoint", entrypoint);
140-
let pairing_url = Url::parse(pairing_url)?;
140+
let pairing_url = util::parse_url(pairing_url, "begin_pairing_flow")?;
141141
if url.host_str() != pairing_url.host_str() {
142142
let fxa_server = FxaServer::from(&url);
143143
let pairing_fxa_server = FxaServer::from(&pairing_url);

components/fxa-client/src/internal/util.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,52 @@ impl Xorable for [u8] {
5252
}
5353
}
5454
}
55+
56+
pub fn parse_url(url: &str, when: impl Into<String>) -> Result<url::Url> {
57+
url::Url::parse(url).map_err(|_| Error::MalformedUrl {
58+
sanitized_url: sanitized_url(url),
59+
when: when.into(),
60+
})
61+
}
62+
63+
pub fn join_url(url: &url::Url, path: &str, when: impl Into<String>) -> Result<url::Url> {
64+
url.join(path).map_err(|_| Error::MalformedUrl {
65+
sanitized_url: sanitized_url(url.as_str()),
66+
when: when.into(),
67+
})
68+
}
69+
70+
fn sanitized_url(url: &str) -> String {
71+
// Remove everything after the `?` char, this is the URL params where all the auth data
72+
// goes.
73+
match url.split_once(['?', '#']) {
74+
Some((before_qmark, _)) => before_qmark,
75+
None => url,
76+
}
77+
.to_string()
78+
}
79+
80+
#[cfg(test)]
81+
mod test {
82+
use super::*;
83+
84+
#[test]
85+
fn test_sanitized_url() {
86+
assert_eq!(
87+
sanitized_url("https://mozilla.com/foo/bar"),
88+
"https://mozilla.com/foo/bar"
89+
);
90+
assert_eq!(
91+
sanitized_url("https://mozilla.com/foo/bar?password=1234"),
92+
"https://mozilla.com/foo/bar"
93+
);
94+
assert_eq!(
95+
sanitized_url("https://mozilla.com/foo/bar?password=1234#key=4321"),
96+
"https://mozilla.com/foo/bar"
97+
);
98+
assert_eq!(
99+
sanitized_url("https://mozilla.com/foo/bar#key=4321"),
100+
"https://mozilla.com/foo/bar"
101+
);
102+
}
103+
}

0 commit comments

Comments
 (0)