Implement upload filename sanitisation
This commit is contained in:
parent
f54d5bd780
commit
7b4cd7c26e
5 changed files with 57 additions and 22 deletions
|
@ -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");
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
27
src/pasta.rs
27
src/pasta.rs
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue