mirror of https://github.com/rwf2/Rocket.git
Allow accessing raw file name from 'DataField'.
This commit is contained in:
parent
0654890e3d
commit
fa1b75ba74
|
@ -22,6 +22,7 @@ all-features = true
|
|||
default = []
|
||||
tls = ["rocket_http/tls"]
|
||||
secrets = ["rocket_http/private-cookies"]
|
||||
trusted_client = []
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.0"
|
||||
|
|
|
@ -461,7 +461,7 @@ impl<'v> FromFormField<'v> for Capped<TempFile<'v>> {
|
|||
async fn from_data(
|
||||
f: DataField<'v, '_>
|
||||
) -> Result<Self, Errors<'v>> {
|
||||
Ok(TempFile::from(f.request, f.data, f.file_name, Some(f.content_type)).await?)
|
||||
Ok(TempFile::from(f.request, f.data, f.file_name.and_then(|f| f.name()), Some(f.content_type)).await?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,62 @@ pub struct ValueField<'r> {
|
|||
pub value: &'r str,
|
||||
}
|
||||
|
||||
|
||||
/// A file name found in a multipart field
|
||||
pub struct FileName<'r> {
|
||||
raw_name: &'r str
|
||||
}
|
||||
|
||||
impl<'r> FileName<'r> {
|
||||
pub(crate) fn new(raw_name: &'r str) -> FileName<'r> {
|
||||
FileName {
|
||||
raw_name
|
||||
}
|
||||
}
|
||||
|
||||
/// The form field's sanitized file name.
|
||||
///
|
||||
/// ## Santization
|
||||
///
|
||||
/// A "sanitized" file name is a non-empty string, stripped of its file
|
||||
/// exntesion, which does not contain any path delimiters (`/` or `\`), is
|
||||
/// not a hidden (`.`) or `*`-prefixed name, and does not contain special
|
||||
/// characters (`:`, `>`, or `<`). If the submitted file name matches any of
|
||||
/// these properties or none was submitted, the return value will be `None`.
|
||||
pub fn name(&self) -> Option<&'r str> {
|
||||
fn sanitize(file_name: &str) -> Option<&str> {
|
||||
let file_name = std::path::Path::new(file_name)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.map(|n| n.find('.').map(|i| n.split_at(i).0).unwrap_or(n))?;
|
||||
|
||||
if file_name.is_empty()
|
||||
|| file_name.starts_with(|c| c == '.' || c == '*')
|
||||
|| file_name.ends_with(|c| c == ':' || c == '>' || c == '<')
|
||||
|| file_name.contains(|c| c == '/' || c == '\\')
|
||||
{
|
||||
return None
|
||||
}
|
||||
|
||||
Some(file_name)
|
||||
}
|
||||
|
||||
sanitize(self.raw_name)
|
||||
}
|
||||
|
||||
/// The raw file name. The client can send arbitrary text here.
|
||||
/// In almost every case, use name() instead, which returns a safe sanitized file name.
|
||||
/// Only use this if the server is not publicly accessible and
|
||||
/// you can trust the client to send correct data **and** you really need the orinal file name.
|
||||
/// Don't rely on the file extension to figure out the type of data.
|
||||
///
|
||||
/// Note: This function requires the "trusted_client" feature to be enabled.
|
||||
#[cfg(feature = "trusted_client")]
|
||||
pub fn raw_name(&self) -> &'r str {
|
||||
self.raw_name
|
||||
}
|
||||
}
|
||||
|
||||
/// A multipart form field with an underlying data stream.
|
||||
///
|
||||
/// Rocket preprocesses all form fields into either [`ValueField`]s or
|
||||
|
@ -25,16 +81,8 @@ pub struct ValueField<'r> {
|
|||
pub struct DataField<'r, 'i> {
|
||||
/// The (decoded) name of the form field.
|
||||
pub name: NameView<'r>,
|
||||
/// The form field's sanitized file name.
|
||||
///
|
||||
/// ## Santization
|
||||
///
|
||||
/// A "sanitized" file name is a non-empty string, stripped of its file
|
||||
/// exntesion, which does not contain any path delimiters (`/` or `\`), is
|
||||
/// not a hidden (`.`) or `*`-prefixed name, and does not contain special
|
||||
/// characters (`:`, `>`, or `<`). If the submitted file name matches any of
|
||||
/// these properties or none was submitted, `file_name` will be `None`.
|
||||
pub file_name: Option<&'r str>,
|
||||
/// The form fields's file name.
|
||||
pub file_name: Option<FileName<'r>>,
|
||||
/// The form field's Content-Type, as submitted, which may or may not
|
||||
/// reflect on `data`.
|
||||
pub content_type: ContentType,
|
||||
|
|
|
@ -182,7 +182,7 @@ impl<'r, 'i> MultipartParser<'r, 'i> {
|
|||
content_type,
|
||||
request: self.request,
|
||||
name: NameView::new(name),
|
||||
file_name: file_name.and_then(sanitize),
|
||||
file_name: file_name.and_then(|name| Some(FileName::new(name))),
|
||||
data: Data::from(field),
|
||||
})
|
||||
} else {
|
||||
|
@ -204,23 +204,6 @@ impl<'r, 'i> MultipartParser<'r, 'i> {
|
|||
}
|
||||
}
|
||||
|
||||
fn sanitize(file_name: &str) -> Option<&str> {
|
||||
let file_name = std::path::Path::new(file_name)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.map(|n| n.find('.').map(|i| n.split_at(i).0).unwrap_or(n))?;
|
||||
|
||||
if file_name.is_empty()
|
||||
|| file_name.starts_with(|c| c == '.' || c == '*')
|
||||
|| file_name.ends_with(|c| c == ':' || c == '>' || c == '<')
|
||||
|| file_name.contains(|c| c == '/' || c == '\\')
|
||||
{
|
||||
return None
|
||||
}
|
||||
|
||||
Some(file_name)
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new() -> Self {
|
||||
Buffer {
|
||||
|
|
Loading…
Reference in New Issue