Add URI::percent_decoding helper method. Safeguard Pathbuf FromSegments implementation.

This commit is contained in:
Sergio Benitez 2016-11-02 16:55:56 +01:00
parent 4326c9103e
commit c98d047038
5 changed files with 101 additions and 22 deletions

View File

@ -4,9 +4,10 @@
extern crate rocket; extern crate rocket;
use std::io; use std::io;
use rocket::response::NamedFile;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rocket::response::NamedFile;
#[get("/")] #[get("/")]
fn index() -> io::Result<NamedFile> { fn index() -> io::Result<NamedFile> {
NamedFile::open("static/index.html") NamedFile::open("static/index.html")

View File

@ -85,7 +85,7 @@
//! [global] //! [global]
//! address = "1.2.3.4" //! address = "1.2.3.4"
//! //!
//! [devolopment] //! [development]
//! address = "localhost" //! address = "localhost"
//! //!
//! [production] //! [production]

View File

@ -1,9 +1,12 @@
//! Borrowed and owned string types for absolute URIs. //! Borrowed and owned string types for absolute URIs.
//!
use std::cell::Cell; use std::cell::Cell;
use std::convert::From; use std::convert::From;
use std::fmt; use std::fmt;
use std::borrow::Cow;
use std::str::Utf8Error;
use url;
use router::Collider; use router::Collider;
@ -77,7 +80,8 @@ impl<'a> URI<'a> {
}) })
} }
/// Returns an iterator over the segments of this URI. Skips empty segments. /// Returns an iterator over the segments of the path in this URI. Skips
/// empty segments.
/// ///
/// ### Examples /// ### Examples
/// ///
@ -118,6 +122,32 @@ impl<'a> URI<'a> {
Segments(self.path) Segments(self.path)
} }
/// Returns the path part of this URI.
///
/// ### Examples
///
/// A URI with only a path:
///
/// ```rust
/// use rocket::http::uri::URI;
///
/// let uri = URI::new("/a/b/c");
/// assert_eq!(uri.path(), "/a/b/c");
/// ```
///
/// A URI with other components:
///
/// ```rust
/// use rocket::http::uri::URI;
///
/// let uri = URI::new("/a/b/c?name=bob#done");
/// assert_eq!(uri.path(), "/a/b/c");
/// ```
#[inline(always)]
pub fn path(&self) -> &'a str {
self.path
}
/// Returns the query part of this URI without the question mark, if there is /// Returns the query part of this URI without the question mark, if there is
/// any. /// any.
/// ///
@ -172,6 +202,41 @@ impl<'a> URI<'a> {
self.fragment self.fragment
} }
/// Returns a URL-decoded version of the string. If the percent encoded
/// values are not valid UTF-8, an `Err` is returned.
///
/// # Examples
///
/// ```rust
/// use rocket::http::uri::URI;
///
/// let uri = URI::new("/Hello%2C%20world%21");
/// let decoded_path = URI::percent_decode(uri.path().as_bytes()).expect("decoded");
/// assert_eq!(decoded_path, "/Hello, world!");
/// ```
pub fn percent_decode<'s>(string: &'s [u8]) -> Result<Cow<'s, str>, Utf8Error> {
let decoder = url::percent_encoding::percent_decode(string);
decoder.decode_utf8()
}
/// Returns a URL-decoded version of the path. Any invalid UTF-8
/// percent-encoded byte sequences will be replaced <20> U+FFFD, the
/// replacement character.
///
/// # Examples
///
/// ```rust
/// use rocket::http::uri::URI;
///
/// let uri = URI::new("/Hello%2C%20world%21");
/// let decoded_path = URI::percent_decode_lossy(uri.path().as_bytes());
/// assert_eq!(decoded_path, "/Hello, world!");
/// ```
pub fn percent_decode_lossy<'s>(string: &'s [u8]) -> Cow<'s, str> {
let decoder = url::percent_encoding::percent_decode(string);
decoder.decode_utf8_lossy()
}
/// Returns the inner string of this URI. /// Returns the inner string of this URI.
/// ///
/// The returned string is in raw form. It contains empty segments. If you'd /// The returned string is in raw form. It contains empty segments. If you'd

View File

@ -1,9 +1,9 @@
use url;
use error::Error;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
use std::str::FromStr; use std::str::FromStr;
use error::Error;
use http::uri::URI;
/// Trait to create instance of some type from a form value; expected from field /// Trait to create instance of some type from a form value; expected from field
/// types in structs deriving `FromForm`. /// types in structs deriving `FromForm`.
/// ///
@ -72,20 +72,20 @@ impl<'v> FromFormValue<'v> for String {
// This actually parses the value according to the standard. // This actually parses the value according to the standard.
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> { fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
let decoder = url::percent_encoding::percent_decode(v.as_bytes()); let result = URI::percent_decode(v.as_bytes());
let res = decoder.decode_utf8().map_err(|_| v).map(|s| s.into_owned()); match result {
match res { Err(_) => Err(v),
e@Err(_) => e,
Ok(mut string) => Ok({ Ok(mut string) => Ok({
// Entirely safe because we're changing the single-byte '+'.
unsafe { unsafe {
for c in string.as_mut_vec() { for c in string.to_mut().as_mut_vec() {
if *c == b'+' { if *c == b'+' {
*c = b' '; *c = b' ';
} }
} }
} }
string string.into_owned()
}) })
} }
} }

View File

@ -1,11 +1,9 @@
use std::str::FromStr; use std::str::{Utf8Error, FromStr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
use std::path::PathBuf; use std::path::PathBuf;
use std::fmt::Debug; use std::fmt::Debug;
use url; use http::uri::{URI, Segments};
use http::uri::Segments;
/// Trait to convert a dynamic path segment string to a concrete value. /// Trait to convert a dynamic path segment string to a concrete value.
/// ///
@ -68,8 +66,7 @@ impl<'a> FromParam<'a> for &'a str {
impl<'a> FromParam<'a> for String { impl<'a> FromParam<'a> for String {
type Error = &'a str; type Error = &'a str;
fn from_param(p: &'a str) -> Result<String, Self::Error> { fn from_param(p: &'a str) -> Result<String, Self::Error> {
let decoder = url::percent_encoding::percent_decode(p.as_bytes()); URI::percent_decode(p.as_bytes()).map_err(|_| p).map(|s| s.into_owned())
decoder.decode_utf8().map_err(|_| p).map(|s| s.into_owned())
} }
} }
@ -133,10 +130,26 @@ impl<'a> FromSegments<'a> for Segments<'a> {
} }
} }
/// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
/// percent-decoded. If a segment is equal to "..", the previous segment (if
/// any) is skipped. For security purposes, any other segments that begin with
/// "*" or "." are ignored. If a percent-decoded segment results in invalid
/// UTF8, an `Err` is returned.
impl<'a> FromSegments<'a> for PathBuf { impl<'a> FromSegments<'a> for PathBuf {
type Error = (); type Error = Utf8Error;
fn from_segments(segments: Segments<'a>) -> Result<PathBuf, ()> {
Ok(segments.collect()) fn from_segments(segments: Segments<'a>) -> Result<PathBuf, Utf8Error> {
let mut buf = PathBuf::new();
for segment in segments {
let decoded = URI::percent_decode(segment.as_bytes())?;
if decoded == ".." {
buf.pop();
} else if !(decoded.starts_with(".") || decoded.starts_with("*")) {
buf.push(&*decoded)
}
}
Ok(buf)
} }
} }