From 5432fd8fedd260d7d5acb132939f344b4d5fa3e7 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 May 2023 00:56:08 +0300 Subject: [PATCH 1/3] Fix 838 --- url/src/parser.rs | 7 ++++++- url/tests/unit.rs | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/url/src/parser.rs b/url/src/parser.rs index c32090e20..946ac860d 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -179,6 +179,7 @@ pub fn default_port(scheme: &str) -> Option { } #[derive(Clone)] +#[derive(Debug)] pub struct Input<'i> { chars: str::Chars<'i>, } @@ -1173,7 +1174,7 @@ impl<'a> Parser<'a> { ) -> Input<'i> { // Relative path state loop { - let segment_start = self.serialization.len(); + let mut segment_start = self.serialization.len(); let mut ends_with_slash = false; loop { let input_before_c = input.clone(); @@ -1202,6 +1203,10 @@ impl<'a> Parser<'a> { } _ => { self.check_url_code_point(c, &input); + if scheme_type.is_file() && is_normalized_windows_drive_letter(&self.serialization[path_start+1..]) { + self.serialization.push('/'); + segment_start += 1; + } if self.context == Context::PathSegmentSetter { if scheme_type.is_special() { self.serialization diff --git a/url/tests/unit.rs b/url/tests/unit.rs index 8957aaf32..cd67594c6 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -1262,3 +1262,26 @@ fn test_authority() { "%C3%A0lex:%C3%A0lex@xn--lex-8ka.xn--p1ai.example.com" ); } + +#[test] +/// https://github.com/servo/rust-url/issues/838 +fn test_file_with_drive() { + let s1 = "fIlE:p:?../"; + let url = url::Url::parse(s1).unwrap(); + assert_eq!(url.to_string(), "file:///p:?../"); + assert_eq!(url.path(), "/p:"); + + let testcases = [ + ("a", "file:///p:/a"), + ("", "file:///p:?../"), + ("?x", "file:///p:?x"), + (".", "file:///p:/"), + ("..", "file:///p:/"), + ("../", "file:///p:/"), + ]; + + for case in &testcases { + let url2 = url::Url::join(&url, case.0).unwrap(); + assert_eq!(url2.to_string(), case.1); + } +} From df88a29c9176435aeef786145af3572bf8747d05 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 May 2023 01:00:36 +0300 Subject: [PATCH 2/3] Also fix issue where path segment could be confused with drive letter because we don't check if the path is empty --- url/src/parser.rs | 2 +- url/tests/unit.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/url/src/parser.rs b/url/src/parser.rs index 946ac860d..ea4c88cd7 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -1254,7 +1254,7 @@ impl<'a> Parser<'a> { } _ => { // If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then - if scheme_type.is_file() && is_windows_drive_letter(segment_before_slash) { + if scheme_type.is_file() && segment_start == path_start + 1 && is_windows_drive_letter(segment_before_slash) { // Replace the second code point in buffer with U+003A (:). if let Some(c) = segment_before_slash.chars().next() { self.serialization.truncate(segment_start); diff --git a/url/tests/unit.rs b/url/tests/unit.rs index cd67594c6..6cb0f37fe 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -1285,3 +1285,16 @@ fn test_file_with_drive() { assert_eq!(url2.to_string(), case.1); } } + +#[test] +/// Similar to test_file_with_drive, but with a path +/// that could be confused for a drive. +fn test_file_with_drive_and_path() { + let s1 = "fIlE:p:/x|?../"; + let url = url::Url::parse(s1).unwrap(); + assert_eq!(url.to_string(), "file:///p:/x|?../"); + assert_eq!(url.path(), "/p:/x|"); + let s2 = "a"; + let url2 = url::Url::join(&url, s2).unwrap(); + assert_eq!(url2.to_string(), "file:///p:/a"); +} From 21f32d64b735323880c2913c44e8f370a49518bb Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 May 2023 01:07:53 +0300 Subject: [PATCH 3/3] Fix lint --- url/src/parser.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/url/src/parser.rs b/url/src/parser.rs index ea4c88cd7..765cc027c 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -178,8 +178,7 @@ pub fn default_port(scheme: &str) -> Option { } } -#[derive(Clone)] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Input<'i> { chars: str::Chars<'i>, } @@ -1203,7 +1202,11 @@ impl<'a> Parser<'a> { } _ => { self.check_url_code_point(c, &input); - if scheme_type.is_file() && is_normalized_windows_drive_letter(&self.serialization[path_start+1..]) { + if scheme_type.is_file() + && is_normalized_windows_drive_letter( + &self.serialization[path_start + 1..], + ) + { self.serialization.push('/'); segment_start += 1; } @@ -1254,7 +1257,10 @@ impl<'a> Parser<'a> { } _ => { // If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then - if scheme_type.is_file() && segment_start == path_start + 1 && is_windows_drive_letter(segment_before_slash) { + if scheme_type.is_file() + && segment_start == path_start + 1 + && is_windows_drive_letter(segment_before_slash) + { // Replace the second code point in buffer with U+003A (:). if let Some(c) = segment_before_slash.chars().next() { self.serialization.truncate(segment_start);