From 51b705a629cf09d453a15497003f1ed9aeb7d545 Mon Sep 17 00:00:00 2001 From: Craig Pastro Date: Fri, 19 Mar 2021 11:01:48 +0900 Subject: [PATCH] Content-Disposition header is required in multipart --- .../src/header/common/content_disposition.rs | 2 +- actix-multipart/src/error.rs | 3 ++ actix-multipart/src/server.rs | 33 ++++++++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index 6076d033c..46721a8d7 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -72,7 +72,7 @@ impl<'a> From<&'a str> for DispositionType { #[derive(Clone, Debug, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum DispositionParam { - /// For [`DispositionType::FormData`] (i.e. *multipart/form-data*), the name of an field from + /// For [`DispositionType::FormData`] (i.e. *multipart/form-data*), the name of a field from /// the form. Name(String), /// A plain file name. diff --git a/actix-multipart/src/error.rs b/actix-multipart/src/error.rs index cdbb5d395..d47a9032c 100644 --- a/actix-multipart/src/error.rs +++ b/actix-multipart/src/error.rs @@ -10,6 +10,9 @@ pub enum MultipartError { /// Content-Type header is not found #[display(fmt = "No Content-type header found")] NoContentType, + /// Content-Disposition header satisfying RFC7578 is not found (see https://tools.ietf.org/html/rfc7578#section-4.2) + #[display(fmt = "No content-disposition header satisfying RFC7578 found")] + NoContentDisposition, /// Can not parse Content-Type header #[display(fmt = "Can not parse Content-Type header")] ParseContentType, diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index d9ff3d574..d6e0e2809 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -13,7 +13,10 @@ use futures_util::stream::{LocalBoxStream, Stream, StreamExt}; use actix_utils::task::LocalWaker; use actix_web::error::{ParseError, PayloadError}; -use actix_web::http::header::{self, ContentDisposition, HeaderMap, HeaderName, HeaderValue}; +use actix_web::http::header::{ + self, ContentDisposition, DispositionParam, DispositionType, HeaderMap, HeaderName, + HeaderValue, +}; use crate::error::MultipartError; @@ -402,14 +405,28 @@ impl Field { } /// Get the content disposition of the field, if it exists - pub fn content_disposition(&self) -> Option { + pub fn content_disposition(&self) -> Result { // RFC 7578: 'Each part MUST contain a Content-Disposition header field - // where the disposition type is "form-data".' - if let Some(content_disposition) = self.headers.get(&header::CONTENT_DISPOSITION) { - ContentDisposition::from_raw(content_disposition).ok() - } else { - None - } + // where the disposition type is "form-data". The Content-Disposition + // header field MUST also contain an additional parameter of "name"; the + // value of the "name" parameter is the original field name from the + // form. + self.headers + .get(&header::CONTENT_DISPOSITION) + .and_then(|content_disposition| { + ContentDisposition::from_raw(content_disposition).ok() + }) + .filter(|content_disposition| { + content_disposition.disposition == DispositionType::FormData + && content_disposition + .parameters + .iter() + .any(|param| match param { + DispositionParam::Name(_) => true, + _ => false, + }) + }) + .ok_or(MultipartError::NoContentDisposition) } }