Implement upload filename sanitisation

This commit is contained in:
Daniel Szabo 2022-07-31 21:31:35 +01:00
parent f54d5bd780
commit 7b4cd7c26e
5 changed files with 57 additions and 22 deletions

View file

@ -8,6 +8,7 @@ use actix_web::{get, web, Error, HttpResponse, Responder};
use askama::Template; use askama::Template;
use bytesize::ByteSize; use bytesize::ByteSize;
use futures::TryStreamExt; use futures::TryStreamExt;
use log::warn;
use rand::Rng; use rand::Rng;
use std::io::Write; use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -105,30 +106,41 @@ pub async fn create(
continue; continue;
} }
"file" => { "file" => {
let content_disposition = field.content_disposition(); let path = field.content_disposition().get_filename();
let filename = match content_disposition.get_filename() { let path = match path {
Some("") => continue, Some("") => continue,
Some(filename) => filename.replace(' ', "_").to_string(), Some(p) => p,
None => continue, None => continue,
}; };
let mut file = match PastaFile::from_unsanitized(&path) {
Ok(f) => f,
Err(e) => {
warn!("Unsafe file name: {e:?}");
continue;
}
};
std::fs::create_dir_all(format!("./pasta_data/{}", &new_pasta.id_as_animals())) std::fs::create_dir_all(format!("./pasta_data/{}", &new_pasta.id_as_animals()))
.unwrap(); .unwrap();
let filepath = format!("./pasta_data/{}/{}", &new_pasta.id_as_animals(), &filename); let filepath = format!(
let mut f = web::block(|| std::fs::File::create(filepath)).await??; "./pasta_data/{}/{}",
&new_pasta.id_as_animals(),
&file.name()
);
let mut f = web::block(|| std::fs::File::create(filepath)).await??;
let mut size = 0; let mut size = 0;
while let Some(chunk) = field.try_next().await? { while let Some(chunk) = field.try_next().await? {
size += chunk.len(); size += chunk.len();
f = web::block(move || f.write_all(&chunk).map(|_| f)).await??; f = web::block(move || f.write_all(&chunk).map(|_| f)).await??;
} }
new_pasta.file = Some(PastaFile { file.size = ByteSize::b(size as u64);
name: filename,
size: ByteSize::b(size as u64), new_pasta.file = Some(file);
});
new_pasta.pasta_type = String::from("text"); new_pasta.pasta_type = String::from("text");
} }
_ => {} _ => {}

View file

@ -1,15 +1,32 @@
use std::fmt;
use chrono::{Datelike, Timelike, Local, TimeZone};
use serde::{Deserialize, Serialize};
use bytesize::ByteSize; use bytesize::ByteSize;
use chrono::{Datelike, Local, TimeZone, Timelike};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::path::Path;
use crate::util::animalnumbers::to_animal_names; use crate::util::animalnumbers::to_animal_names;
use crate::util::syntaxhighlighter::html_highlight; use crate::util::syntaxhighlighter::html_highlight;
#[derive(Serialize, Deserialize, PartialEq, Eq)] #[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct PastaFile { pub struct PastaFile {
pub name: String, pub size: ByteSize , pub name: String,
pub size: ByteSize,
}
impl PastaFile {
pub fn from_unsanitized(path: &str) -> Result<Self, &'static str> {
let path = Path::new(path);
let name = path.file_name().ok_or("Path did not contain a file name")?;
let name = name.to_string_lossy().replace(' ', "_");
Ok(Self {
name,
size: ByteSize::b(0),
})
}
pub fn name(&self) -> &str {
&self.name
}
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View file

@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use linkify::{LinkFinder, LinkKind}; use linkify::{LinkFinder, LinkKind};
use std::fs; use std::fs;
use crate::{dbio, pasta::PastaFile, Pasta}; use crate::{dbio, Pasta};
pub fn remove_expired(pastas: &mut Vec<Pasta>) { pub fn remove_expired(pastas: &mut Vec<Pasta>) {
// get current time - this will be needed to check which pastas have expired // get current time - this will be needed to check which pastas have expired
@ -22,15 +22,20 @@ pub fn remove_expired(pastas: &mut Vec<Pasta>) {
true true
} else { } else {
// remove the file itself // remove the file itself
if let Some(PastaFile { name, .. }) = &p.file { if let Some(file) = &p.file {
if fs::remove_file(format!("./pasta_data/{}/{}", p.id_as_animals(), name)).is_err() if fs::remove_file(format!(
"./pasta_data/{}/{}",
p.id_as_animals(),
file.name()
))
.is_err()
{ {
log::error!("Failed to delete file {}!", name) log::error!("Failed to delete file {}!", file.name())
} }
// and remove the containing directory // and remove the containing directory
if fs::remove_dir(format!("./pasta_data/{}/", p.id_as_animals())).is_err() { if fs::remove_dir(format!("./pasta_data/{}/", p.id_as_animals())).is_err() {
log::error!("Failed to delete directory {}!", name) log::error!("Failed to delete directory {}!", file.name())
} }
} }
false false

View file

@ -3,8 +3,9 @@
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a> <a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
{% if pasta.file.is_some() %} {% if pasta.file.is_some() %}
<a style="margin-right: 0.5rem; margin-left: 0.5rem" <a style="margin-right: 0.5rem; margin-left: 0.5rem"
href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name}}">Attached file href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">
'{{pasta.file.as_ref().unwrap().name}}' [{{pasta.file.as_ref().unwrap().size}}]</a> Attached file'{{pasta.file.as_ref().unwrap().name()}}' [{{pasta.file.as_ref().unwrap().size}}]
</a>
{%- endif %} {%- endif %}
{% if pasta.editable %} {% if pasta.editable %}
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a> <a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>

View file

@ -49,7 +49,7 @@
<td> <td>
<a style="margin-right:1rem" href="/raw/{{pasta.id_as_animals()}}">Raw</a> <a style="margin-right:1rem" href="/raw/{{pasta.id_as_animals()}}">Raw</a>
{% if pasta.file.is_some() %} {% if pasta.file.is_some() %}
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name}}">File</a> <a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">File</a>
{%- endif %} {%- endif %}
{% if pasta.editable %} {% if pasta.editable %}
<a style="margin-right:1rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a> <a style="margin-right:1rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>