Finished /upload endpoint
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use std::{io, fs};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use expanduser::expanduser;
|
||||
@@ -12,8 +12,8 @@ pub fn load_config() -> Config {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
download_path: PathBuf,
|
||||
upload_path: PathBuf,
|
||||
download_path: String,
|
||||
upload_path: String,
|
||||
pub web_port: u16,
|
||||
pub shell_port: u16,
|
||||
shells: HashMap<String, String>
|
||||
@@ -32,11 +32,11 @@ impl Config {
|
||||
}
|
||||
|
||||
pub fn get_download_path(&self) -> PathBuf {
|
||||
self.download_path.clone()
|
||||
expanduser(self.download_path.clone()).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_upload_path(&self) -> PathBuf {
|
||||
self.upload_path.clone()
|
||||
expanduser(self.upload_path.clone()).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_shell<S: Into<String>>(&self, key: S) -> Option<String> {
|
||||
|
||||
@@ -6,7 +6,7 @@ use tokio::net::TcpListener;
|
||||
use tower_http::services::ServeDir;
|
||||
use axum::{
|
||||
middleware,
|
||||
routing::get,
|
||||
routing::{get, post},
|
||||
Router
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ mod print_dir;
|
||||
mod config;
|
||||
mod shells;
|
||||
mod logging;
|
||||
mod upload;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@@ -30,7 +31,7 @@ async fn main() {
|
||||
print_dir::print_interface(&args.interface, &port, &args.directory);
|
||||
|
||||
let app = Router::new()
|
||||
//.route("/upload", post(upload_handler))
|
||||
.route("/upload", post(upload::upload_handler))
|
||||
.route("/shells/:shell", get(shells::shells_handler))
|
||||
.nest_service("/download", ServeDir::new(conf.get_download_path()))
|
||||
.nest_service("/", ServeDir::new(cwd))
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
path::{Path, Component}
|
||||
};
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
extract::Multipart,
|
||||
response::IntoResponse
|
||||
};
|
||||
|
||||
use crate::config;
|
||||
|
||||
fn path_is_valid(path: &str) -> bool {
|
||||
let path = Path::new(path);
|
||||
let mut components = path.components().peekable();
|
||||
if let Some(first) = components.peek() {
|
||||
if !matches!(first, Component::Normal(_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
components.count() == 1
|
||||
}
|
||||
|
||||
pub async fn upload_handler(mut multipart: Multipart) -> impl IntoResponse {
|
||||
|
||||
let conf = config::load_config();
|
||||
|
||||
while let Some(field) = multipart.next_field().await.unwrap() {
|
||||
let file_name = match field.name() {
|
||||
Some(file) => file.to_string(),
|
||||
None => continue
|
||||
};
|
||||
let data = field.bytes().await.unwrap();
|
||||
let path = conf.get_upload_path().join(&file_name);
|
||||
|
||||
if !path_is_valid(&file_name) {
|
||||
return (StatusCode::FORBIDDEN, format!("Directory Traversal: {}", path.display()));
|
||||
}
|
||||
|
||||
let Ok(file) = File::create(&path) else {
|
||||
return (StatusCode::FORBIDDEN, format!("Failed to create file: {}", path.display()));
|
||||
};
|
||||
|
||||
let mut writer = BufWriter::new(file);
|
||||
if let Err(e) = writer.write_all(&data) {
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, format!("An error occurred when writing to {}: {}", path.display(), e))
|
||||
}
|
||||
}
|
||||
|
||||
(StatusCode::CREATED, "Successfully uploaded file(s).".to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user