Skip to content

Commit 7497553

Browse files
committed
Assure we don't accidentally parse a valid-looking URL to url and cause long compute times.
The issue is that the `url` crate can be fooled by long URLs into parsing way-too-long hosts inefficiently enough to allow DoS attacks by slowing it down. Some predicate was implemented to detect such URLs before passing them to `url`, but unfortunately a somewhat valid looking URL could be crafted to bypass that check and still run into the `url` parser, which seemed to have no problem with the format of the URL. Testing showed that this is a very special case, which is now handled as well. We hope that one day `url` will handle this all by itself.
1 parent 82874cd commit 7497553

File tree

3 files changed

+16
-12
lines changed

3 files changed

+16
-12
lines changed

gix-url/src/parse.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ pub(crate) fn find_scheme(input: &BStr) -> InputScheme {
8585
pub(crate) fn url(input: &BStr, protocol_end: usize) -> Result<crate::Url, Error> {
8686
const MAX_LEN: usize = 1024;
8787
let bytes_to_path = input[protocol_end + "://".len()..]
88-
.find(b"/")
88+
.iter()
89+
.skip_while(|b| **b == b'/')
90+
.position(|b| *b == b'/')
8991
.unwrap_or(input.len() - protocol_end);
9092
if bytes_to_path > MAX_LEN {
9193
return Err(Error::TooLong {
146 KB
Binary file not shown.

gix-url/tests/parse/mod.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,17 @@ mod unknown {
139139

140140
#[test]
141141
fn fuzzed() {
142-
let base = Path::new("tests").join("fixtures").join("fuzzed");
143-
let location = base.join(Path::new("very-long").with_extension("url"));
144-
let url = std::fs::read(&location).unwrap();
145-
let start = std::time::Instant::now();
146-
gix_url::parse(url.as_bstr()).ok();
147-
assert!(
148-
start.elapsed() < Duration::from_millis(100),
149-
"URL at '{}' parsed too slowly, took {:.00}s",
150-
location.display(),
151-
start.elapsed().as_secs_f32()
152-
)
142+
for name in ["very-long", "very-long2"] {
143+
let base = Path::new("tests").join("fixtures").join("fuzzed");
144+
let location = base.join(Path::new(name).with_extension("url"));
145+
let url = std::fs::read(&location).unwrap();
146+
let start = std::time::Instant::now();
147+
gix_url::parse(url.as_bstr()).ok();
148+
assert!(
149+
start.elapsed() < Duration::from_millis(100),
150+
"URL at '{}' parsed too slowly, took {:.00}s",
151+
location.display(),
152+
start.elapsed().as_secs_f32()
153+
)
154+
}
153155
}

0 commit comments

Comments
 (0)