db timeout, dont leak inner workings, auto create files dir, fix script injection

This commit is contained in:
neri 2020-07-13 15:22:33 +02:00
parent f7aa5b7b07
commit 7403abbe99
7 changed files with 44 additions and 12 deletions

7
Cargo.lock generated
View File

@ -686,6 +686,7 @@ dependencies = [
"chrono", "chrono",
"env_logger", "env_logger",
"futures", "futures",
"htmlescape",
"log", "log",
"mime", "mime",
"openssl-sys", "openssl-sys",
@ -1078,6 +1079,12 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "htmlescape"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.1" version = "0.2.1"

View File

@ -20,6 +20,7 @@ mime = "0.3.16"
rand = "0.7.3" rand = "0.7.3"
chrono = "0.4.13" chrono = "0.4.13"
openssl-sys = "*" openssl-sys = "*"
htmlescape = "0.3.1"
[features] [features]
vendored = ["openssl-sys/vendored"] vendored = ["openssl-sys/vendored"]

View File

@ -97,7 +97,8 @@ async fn download(
.next() .next()
.await .await
.map_err(|_| error::ErrorInternalServerError("could not run select statement"))? .map_err(|_| error::ErrorInternalServerError("could not run select statement"))?
.ok_or_else(|| error::ErrorNotFound("could not find file"))?; .ok_or_else(|| error::ErrorNotFound("file does not exist or has expired"))?;
let file_id: String = row.get("file_id"); let file_id: String = row.get("file_id");
let file_name: String = row.get("file_name"); let file_name: String = row.get("file_name");
let kind: String = row.get("kind"); let kind: String = row.get("kind");
@ -105,12 +106,19 @@ async fn download(
path.push(&file_id); path.push(&file_id);
if kind == FileKind::TEXT.to_string() { if kind == FileKind::TEXT.to_string() {
let content = fs::read_to_string(path).await?; let content = fs::read_to_string(path).await.map_err(|_| {
let view_html = VIEW_HTML.replace("{text}", &content); error::ErrorInternalServerError("this file should be here but could not be found")
})?;
let encoded = htmlescape::encode_minimal(&content);
let view_html = VIEW_HTML.replace("{text}", &encoded);
let response = HttpResponse::Ok().content_type("text/html").body(view_html); let response = HttpResponse::Ok().content_type("text/html").body(view_html);
Ok(response) Ok(response)
} else { } else {
let file = NamedFile::open(path)?.set_content_disposition(ContentDisposition { let file = NamedFile::open(path)
.map_err(|_| {
error::ErrorInternalServerError("this file should be here but could not be found")
})?
.set_content_disposition(ContentDisposition {
disposition: DispositionType::Attachment, disposition: DispositionType::Attachment,
parameters: vec![DispositionParam::Filename(file_name)], parameters: vec![DispositionParam::Filename(file_name)],
}); });
@ -121,6 +129,7 @@ async fn download(
async fn setup_db() -> PgPool { async fn setup_db() -> PgPool {
let pool = PgPool::builder() let pool = PgPool::builder()
.max_size(5) .max_size(5)
.connect_timeout(std::time::Duration::from_secs(5))
.build(&env::var("DATABASE_URL").unwrap_or_else(|_| "postgresql://localhost".to_owned())) .build(&env::var("DATABASE_URL").unwrap_or_else(|_| "postgresql://localhost".to_owned()))
.await .await
.expect("could not create db pool"); .expect("could not create db pool");
@ -153,6 +162,10 @@ async fn main() -> std::io::Result<()> {
files_dir: PathBuf::from(env::var("FILES_DIR").unwrap_or_else(|_| "./files".to_owned())), files_dir: PathBuf::from(env::var("FILES_DIR").unwrap_or_else(|_| "./files".to_owned())),
}; };
fs::create_dir_all(&config.files_dir)
.await
.expect("could not create directory for storing files");
let (send, recv) = async_std::sync::channel::<()>(1); let (send, recv) = async_std::sync::channel::<()>(1);
task::spawn(deleter::delete_old_files( task::spawn(deleter::delete_old_files(
recv, recv,

View File

@ -66,6 +66,13 @@ pub(crate) async fn parse_multipart(
.map_err(|e| { .map_err(|e| {
error::ErrorBadRequest(format!("field validity_secs is not a number: {}", e)) error::ErrorBadRequest(format!("field validity_secs is not a number: {}", e))
})?; })?;
let max_validity_secs = Duration::days(31).num_seconds();
if validity_secs > max_validity_secs {
return Err(error::ErrorBadRequest(format!(
"maximum allowed validity is {} seconds, but you specified {} seconds",
max_validity_secs, validity_secs
)));
}
let valid_till = Local::now() + Duration::seconds(validity_secs); let valid_till = Local::now() + Duration::seconds(validity_secs);
let kind = kind.ok_or_else(|| error::ErrorBadRequest("no content found"))?; let kind = kind.ok_or_else(|| error::ErrorBadRequest("no content found"))?;
Ok((original_name, valid_till, kind)) Ok((original_name, valid_till, kind))

View File

@ -22,6 +22,10 @@ a:visited {
color: mediumorchid; color: mediumorchid;
} }
label {
margin-right: 0.25em;
}
input, input,
select, select,
textarea { textarea {
@ -33,6 +37,6 @@ textarea {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
input[type='submit'] { input[type="submit"] {
background-color: green; background-color: green;
} }

View File

@ -18,8 +18,8 @@
cols="120" cols="120"
></textarea> ></textarea>
<br /> <br />
Gültig für <label for="validity_secs">Gültig für</label>
<select name="validity_secs"> <select id="validity_secs" name="validity_secs">
<option value="1800">30 minuten</option> <option value="1800">30 minuten</option>
<option value="3600">60 minuten</option> <option value="3600">60 minuten</option>
<option value="43200">12 stunden</option> <option value="43200">12 stunden</option>

View File

@ -9,9 +9,9 @@
<main> <main>
<h1><a href="/">datatrash</a></h1> <h1><a href="/">datatrash</a></h1>
<p> <p>
Uploaded Datei ist verfügbar unter
<a href="{server}/file/{id}"> <a href="{server}/file/{id}">
http://localhost:8000/files/{id} {server}/files/{id}
</a> </a>
</p> </p>
</main> </main>