Image Upload #1

Merged
cameron merged 7 commits from feature/image-upload into master 2020-10-17 23:25:52 +00:00
4 changed files with 29 additions and 16 deletions
Showing only changes of commit eccb45ced0 - Show all commits

19
Cargo.lock generated
View File

@@ -1133,6 +1133,7 @@ dependencies = [
"image", "image",
"jsonwebtoken", "jsonwebtoken",
"notify", "notify",
"path-absolutize",
"rayon", "rayon",
"serde", "serde",
"serde_json", "serde_json",
@@ -1596,6 +1597,24 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "path-absolutize"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6ab2aaa5faefed84db46e4398eab15fa51325606462b5da8b0e230af3ac59a"
dependencies = [
"path-dedot",
]
[[package]]
name = "path-dedot"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658c6e985fce9c25289fe7c86c08a3cbe82c19a3cd5b3bc5945c8c632552e460"
dependencies = [
"once_cell",
]
[[package]] [[package]]
name = "pem" name = "pem"
version = "0.8.1" version = "0.8.1"

View File

@@ -26,3 +26,4 @@ walkdir = "2"
rayon = "1.3" rayon = "1.3"
notify = "4.0" notify = "4.0"
tokio = "0.2" tokio = "0.2"
path-absolutize = "3.0.6"

View File

@@ -1,3 +1,4 @@
use path_absolutize::*;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::read_dir; use std::fs::read_dir;
use std::io; use std::io;
@@ -47,10 +48,10 @@ fn is_valid_full_path(base: &Path, path: &str) -> Option<PathBuf> {
let mut full_path = PathBuf::from(base); let mut full_path = PathBuf::from(base);
full_path.push(&path); full_path.push(&path);
full_path full_path
.canonicalize() .absolutize()
.and_then(|p| { .and_then(|p| {
if p.starts_with(base) { if p.starts_with(base) {
Ok(p) Ok(p.into_owned())
} else { } else {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
@@ -59,9 +60,9 @@ fn is_valid_full_path(base: &Path, path: &str) -> Option<PathBuf> {
} }
}) })
.ok() .ok()
} else if let Ok(path) = path.canonicalize().and_then(|path| { } else if let Ok(path) = path.absolutize().and_then(|path| {
if path.starts_with(base) { if path.starts_with(base) {
Ok(path) Ok(path.into_owned())
} else { } else {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
@@ -88,9 +89,7 @@ mod tests {
assert_eq!(None, is_valid_path("fake/../../../")); assert_eq!(None, is_valid_path("fake/../../../"));
assert_eq!(None, is_valid_path("../../../etc/passwd")); assert_eq!(None, is_valid_path("../../../etc/passwd"));
assert_eq!(None, is_valid_path("..//etc/passwd")); assert_eq!(None, is_valid_path("..//etc/passwd"));
assert_eq!(None, is_valid_path("..%2f..%2f/etc/passwd")); assert_eq!(None, is_valid_path("../../etc/passwd"));
assert_eq!(None, is_valid_path("%2e%2e%2f%2e%2e%2f/etc/passwd"));
assert_eq!(None, is_valid_path("\\\\attacker.com\\shared\\mal.php"));
} }
#[test] #[test]

View File

@@ -153,13 +153,8 @@ async fn upload_image(_: Claims, mut payload: mp::Multipart) -> impl Responder {
let path = file_path.unwrap_or_else(|| dotenv::var("BASE_PATH").unwrap()); let path = file_path.unwrap_or_else(|| dotenv::var("BASE_PATH").unwrap());
if !file_content.is_empty() { if !file_content.is_empty() {
let full_path = PathBuf::from(&path); let full_path = PathBuf::from(&path).join(file_name.unwrap());
if let Some(full_path) = is_valid_path(full_path.to_str().unwrap_or("")) {
if let Some(mut full_path) = is_valid_path(full_path.to_str().unwrap_or("")) {
// TODO: Validate this file_name as is subject to path traversals which could lead to
// writing outside the base dir.
full_path = full_path.join(file_name.unwrap());
if !full_path.is_file() { if !full_path.is_file() {
let mut file = File::create(full_path).unwrap(); let mut file = File::create(full_path).unwrap();
file.write_all(&file_content).unwrap(); file.write_all(&file_content).unwrap();
@@ -206,8 +201,7 @@ async fn stream_video(
// Extract video playlist dir to dotenv // Extract video playlist dir to dotenv
if !playlist.starts_with("tmp") || playlist.contains("..") { if !playlist.starts_with("tmp") || playlist.contains("..") {
HttpResponse::NotFound().finish() HttpResponse::NotFound().finish()
} } else if let Ok(file) = NamedFile::open(playlist) {
else if let Ok(file) = NamedFile::open(playlist) {
file.into_response(&request).unwrap() file.into_response(&request).unwrap()
} else { } else {
HttpResponse::NotFound().finish() HttpResponse::NotFound().finish()