Skip to content

Commit b83591a

Browse files
committed
fix path URL-encoding for redirects on crate-details pages
1 parent a1928f7 commit b83591a

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

src/web/crate_details.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ mod tests {
681681
};
682682
use anyhow::Error;
683683
use kuchikiki::traits::TendrilSink;
684+
use reqwest::StatusCode;
684685
use semver::Version;
685686
use std::collections::HashMap;
686687

@@ -1667,4 +1668,21 @@ mod tests {
16671668
Ok(())
16681669
});
16691670
}
1671+
1672+
#[test]
1673+
fn test_crate_name_with_other_uri_chars() {
1674+
wrapper(|env| {
1675+
env.fake_release().name("dummy").version("1.0.0").create()?;
1676+
1677+
assert_eq!(
1678+
env.frontend()
1679+
.get_no_redirect("/crate/dummy%3E")
1680+
.send()?
1681+
.status(),
1682+
StatusCode::FOUND
1683+
);
1684+
1685+
Ok(())
1686+
})
1687+
}
16701688
}

src/web/error.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
db::PoolError,
33
storage::PathNotFoundError,
4-
web::{cache::CachePolicy, releases::Search, AxumErrorPage},
4+
web::{cache::CachePolicy, encode_url_path, releases::Search, AxumErrorPage},
55
};
66
use anyhow::anyhow;
77
use axum::{
@@ -107,7 +107,7 @@ impl IntoResponse for AxumNope {
107107
web_error.into_response()
108108
}
109109
AxumNope::Redirect(target, cache_policy) => {
110-
match super::axum_cached_redirect(&target, cache_policy) {
110+
match super::axum_cached_redirect(&encode_url_path(&target), cache_policy) {
111111
Ok(response) => response.into_response(),
112112
Err(err) => AxumNope::InternalError(err).into_response(),
113113
}
@@ -144,9 +144,20 @@ pub(crate) type AxumResult<T> = Result<T, AxumNope>;
144144

145145
#[cfg(test)]
146146
mod tests {
147-
use crate::test::wrapper;
147+
use super::{AxumNope, IntoResponse};
148+
use crate::{test::wrapper, web::cache::CachePolicy};
148149
use kuchikiki::traits::TendrilSink;
149150

151+
#[test]
152+
fn test_redirect_error_encodes_url_path() {
153+
let response =
154+
AxumNope::Redirect("/something>".into(), CachePolicy::ForeverInCdnAndBrowser)
155+
.into_response();
156+
157+
assert_eq!(response.status(), 302);
158+
assert_eq!(response.headers().get("Location").unwrap(), "/something%3E");
159+
}
160+
150161
#[test]
151162
fn check_404_page_content_crate() {
152163
wrapper(|env| {

src/web/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,4 +1248,11 @@ mod test {
12481248
assert_eq!(req_version, ReqVersion::Semver(VersionReq::STAR));
12491249
assert_eq!(req_version.to_string(), "*")
12501250
}
1251+
1252+
#[test_case("/something/", "/something/")] // already valid path
1253+
#[test_case("/something>", "/something%3E")] // something to encode
1254+
#[test_case("/something%3E", "/something%3E")] // re-running doesn't change anything
1255+
fn test_encode_url_path(input: &str, expected: &str) {
1256+
assert_eq!(encode_url_path(input), expected);
1257+
}
12511258
}

src/web/rustdoc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ async fn try_serve_legacy_toolchain_asset(
8686

8787
/// Handler called for `/:crate` and `/:crate/:version` URLs. Automatically redirects to the docs
8888
/// or crate details page based on whether the given crate version was successfully built.
89-
#[instrument(skip_all)]
89+
#[instrument(skip(storage, config, conn))]
9090
pub(crate) async fn rustdoc_redirector_handler(
9191
Path(params): Path<RustdocRedirectorParams>,
9292
Extension(storage): Extension<Arc<AsyncStorage>>,

0 commit comments

Comments
 (0)