Skip to content

Commit c86fbce

Browse files
committed
crate-search: fix handling crates.io error response
* in case of errors from the crates.io API we tried to parse the body, which we don't need to do. Now we see the actual error. * Given any based64 encoded abiraty data to the `paginate` parameter we were passing it directly to crates.io, which we don't need to. Only GET arguments are allowed.
1 parent 824daa4 commit c86fbce

File tree

1 file changed

+66
-13
lines changed

1 file changed

+66
-13
lines changed

src/web/releases.rs

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ fn get_search_results(
220220
}
221221
});
222222

223-
let releases: CratesIoSearchResult = HTTP_CLIENT.get(url).send()?.json()?;
223+
let releases: CratesIoSearchResult = HTTP_CLIENT.get(url).send()?.error_for_status()?.json()?;
224224

225225
let names: Vec<_> = releases
226226
.crates
@@ -594,18 +594,30 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
594594
let search_result = ctry!(
595595
req,
596596
if let Some(paginate) = params.get("paginate") {
597-
get_search_results(
598-
&mut conn,
599-
&String::from_utf8_lossy(&base64::decode(&paginate.as_bytes()).map_err(
600-
|e| -> IronError {
601-
warn!(
602-
"error when decoding pagination base64 string \"{}\": {:?}",
603-
paginate, e
604-
);
605-
Nope::NoResults.into()
606-
},
607-
)?),
608-
)
597+
let decoded = base64::decode(&paginate.as_bytes()).map_err(|e| -> IronError {
598+
warn!(
599+
"error when decoding pagination base64 string \"{}\": {:?}",
600+
paginate, e
601+
);
602+
Nope::NoResults.into()
603+
})?;
604+
let query_params = String::from_utf8_lossy(&decoded);
605+
606+
if !query_params.starts_with('?') {
607+
// sometimes we see plain bytes being passed to `paginate`.
608+
// In these cases we just return `NoResults` and don't call
609+
// the crates.io API.
610+
// The whole point of the `paginate` design is that we don't
611+
// know anything about the pagination args and crates.io can
612+
// change them as they whish, so we cannot do any more checks here.
613+
warn!(
614+
"didn't get query args in `paginate` arguments for search: \"{}\"",
615+
query_params
616+
);
617+
return Err(Nope::NoResults.into());
618+
}
619+
620+
get_search_results(&mut conn, &query_params)
609621
} else if !query.is_empty() {
610622
let query_params: String = form_urlencoded::Serializer::new(String::new())
611623
.append_pair("q", &query)
@@ -748,8 +760,10 @@ mod tests {
748760
use chrono::{Duration, TimeZone};
749761
use kuchiki::traits::TendrilSink;
750762
use mockito::{mock, Matcher};
763+
use reqwest::StatusCode;
751764
use serde_json::json;
752765
use std::collections::HashSet;
766+
use test_case::test_case;
753767

754768
#[test]
755769
fn get_releases_by_stars() {
@@ -911,6 +925,45 @@ mod tests {
911925
})
912926
}
913927

928+
#[test]
929+
fn search_invalid_paginate_doesnt_request_cratesio() {
930+
wrapper(|env| {
931+
let response = env
932+
.frontend()
933+
.get(&format!(
934+
"/releases/search?paginate={}",
935+
base64::encode("something_that_doesnt_start_with_?")
936+
))
937+
.send()?;
938+
assert_eq!(response.status(), StatusCode::NOT_FOUND);
939+
Ok(())
940+
})
941+
}
942+
943+
#[test_case(StatusCode::NOT_FOUND)]
944+
#[test_case(StatusCode::INTERNAL_SERVER_ERROR)]
945+
#[test_case(StatusCode::BAD_GATEWAY)]
946+
fn crates_io_errors_are_correctly_returned_and_we_dont_try_parsing(status: StatusCode) {
947+
wrapper(|env| {
948+
let _m = mock("GET", "/api/v1/crates")
949+
.match_query(Matcher::AllOf(vec![
950+
Matcher::UrlEncoded("q".into(), "doesnt_matter_here".into()),
951+
Matcher::UrlEncoded("per_page".into(), "30".into()),
952+
]))
953+
.with_status(status.as_u16() as usize)
954+
.create();
955+
956+
let response = env
957+
.frontend()
958+
.get("/releases/search?query=doesnt_matter_here")
959+
.send()?;
960+
assert_eq!(response.status(), 500);
961+
962+
assert!(response.text()?.contains(&format!("{}", status)));
963+
Ok(())
964+
})
965+
}
966+
914967
#[test]
915968
fn search_encoded_pagination_passed_to_cratesio() {
916969
wrapper(|env| {

0 commit comments

Comments
 (0)