Skip to content

Commit

Permalink
Add support to ban extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
rfc-2549 committed Sep 24, 2023
1 parent 47d733b commit 968a288
Showing 1 changed file with 103 additions and 89 deletions.
192 changes: 103 additions & 89 deletions http/sakisafe.pl
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
#!/usr/bin/perl

# This file is part of sakisafe.

use if $^O eq "openbsd", OpenBSD::Pledge, qw(pledge);
use Mojolicious::Lite -signatures;
use Mojolicious::Routes::Pattern;
use Mojo::Log;
use Time::HiRes;
use List::MoreUtils qw(uniq);
use Carp;
use Term::ANSIColor;
use English;
use MIME::Types;
Expand All @@ -18,88 +16,96 @@
plugin 'RenderFile';

# OpenBSD promises.
pledge("stdio cpath rpath wpath inet flock fattr") if $^O eq "openbsd";
pledge("stdio prot_exec cpath rpath wpath inet flock fattr")
if $^O eq "openbsd";

# 500 MBs

# 100 MBs
my $MAX_SIZE = 1024 * 1024 * 100;
my $MAX_SIZE = 1024 * 1024 * 1000;

my @BANNED = eval { path('banned.txt')->slurp_utf8 } || qw(); # Add banned IP addresses here
my @BANNED = eval { path('banned.txt')->slurp_utf8 }
or qw(); # Add banned IP addresses here
my @BANNED_EXTS = eval { path('banned_exts.txt')->slurp_utf8 }
or qw(); # Add forbidden files extensions here
my $dirname;
my $link;

mkdir "f";

# Mojo logger
my $log = Mojo::Log->new(path => 'sakisafe.log', level => 'trace');
$log->color(1);

# Forward logs to STDERR or STDOUT while also writing to the `sakisafe.log` file.
$log->on(
message => sub ($l, $level, @lines) {
my $time = time;
my ($s, $m, $h, $day, $month, $year) = localtime time;
$time = sprintf(
'%04d-%02d-%02d %02d:%02d:%08.5f',
$year + 1900,
$month + 1, $day, $h, $m, "$s." . ((split '.', $time)[1] // 0)
);
my $log_to_print = '[' . $time . '] ' . '[' . $level . '] ' . join(' ', @lines);
if ($level eq 'trace' || $level eq 'info') {
say $log_to_print;
}
else {
print \*STDERR, $log_to_print . "\n";
}
}
);

# Function to handle file uploads

sub logger ( $level, $address, $message ) {

open( my $fh, ">>", "sakisafe.log" );
printf( $fh "[%s]: %s has uploaded file %s\n", $level, $address, $message );
close($fh);
}

sub handle_file {
my $c = shift;
my $filedata = $c->param("file");
if ($filedata->size > $MAX_SIZE) {
return $c->render(text => "Max upload size: $MAX_SIZE", status => 400);
}

if (List::MoreUtils::any { $c->tx->remote_address } uniq @BANNED) {
$log->info("Attempt to upload by banned IP: " . $c->tx->remote_address);
$c->render(
text => "Hi! Seems like the server admin added your IP address to the banned IP array."
. "As the developer of sakisafe, I can't do anything.",
status => 403
);
return;
}

# Generate random string for the directory
my @chars = ('0' .. '9', 'a' .. 'Z');
$dirname .= $chars[rand @chars] for 1 .. 5;
my $filename = $filedata->filename;
$log->warn("sakisafe warning: could not create directory: $ERRNO") unless mkdir("f/" . $dirname);
$filename .= ".txt" if $filename eq "-";

# TODO: get whether the server is http or https
# There's a CGI ENV variable for that.
my $host = $c->req->url->to_abs->host;
my $ua = $c->req->headers->user_agent;
$filedata->move_to("f/" . $dirname . "/" . $filename);
$link = "http://$host/f/$dirname/$filename";
$c->stash(link => $link, host => $host, dirname => $dirname);

$c->res->headers->header('Location' => "$link" . $filename);

# Only give the link to curl, html template for others.
if ($ua =~ m/curl/) {
$c->render(text => $link . "\n", status => 201,);

$dirname = "";
}
else {
$c->render(template => 'file', status => 201,);
}

$log->info($c->tx->remote_address . " " . $dirname . "/" . $filename);
$dirname = "";
my $c = shift;
my $filedata = $c->param("file");
if ( $filedata->size > $MAX_SIZE ) {
return $c->render(
text => "Max upload size: $MAX_SIZE",
status => 400
);
}

if ( List::MoreUtils::any { /$c->tx->remote_address/ } uniq @BANNED ) {
$c->render(
text =>
"Hi! Seems like the server admin added your IP address to the banned IP array."
. "As the developer of sakisafe, I can't do anything.",
status => 403
);
return;
}

# Generate random string for the directory
my @chars = ( '0' .. '9', 'a' .. 'Z' );
$dirname .= $chars[ rand @chars ] for 1 .. 5;
my $filename = $filedata->filename;
my ($ext) = $filename =~ /(\.[^.]+)$/;
if ( List::MoreUtils::any { /$ext/ } uniq @BANNED_EXTS ) {
$c->render( text => "You cannot this filetype.\n", status => 415 );
say $ext;
logger( "WARN", $c->tx->remote_address, $dirname . "/" . $filename );
return;
}
carp( color("bold yellow"),
"sakisafe warning: could not create directory: $ERRNO",
color("reset") )
unless mkdir( "f/" . $dirname );
$filename .= ".txt" if $filename eq "-";

# TODO: get whether the server is http or https
# There's a CGI ENV variable for that.
my $host = $c->req->url->to_abs->host;
my $ua = $c->req->headers->user_agent;
$filedata->move_to( "f/" . $dirname . "/" . $filename );
$link = "http://$host/f/$dirname/$filename";
$c->stash( link => $link, host => $host, dirname => $dirname );

$c->res->headers->header( 'Location' => "$link" . $filename );

# Only give the link to curl, html template for others.

if ( $ua =~ m/curl/ || $ua eq "" ) {
$c->render(
text => $link . "\n",
status => 201,
);

$dirname = "";
}
else {
$c->render(
template => 'file',
status => 201,
);
}
logger( "INFO", $c->tx->remote_address, $dirname . "/" . $filename );
$dirname = "";
}

# Function to log uploaded files
Expand All @@ -110,19 +116,22 @@ sub handle_file {
# Allow files to be downloaded.

get '/f/:dir/#name' => sub ($c) {
my $dir = $c->param("dir");
my $file = $c->param("name");
my $ext = $file;
$ext =~ s/.*\.//;
my $path = "f/" . $dir . "/" . $file;

#carp "sakisafe warning: could not get file: $ERRNO" unless
$c->render(text => "file not found", status => 404) unless -e $path;
$c->render_file(filepath => $path, format => $ext, content_disposition => 'inline');
my $dir = $c->param("dir");
my $file = $c->param("name");
my $ext = $file;
$ext =~ s/.*\.//;
my $path = "f/" . $dir . "/" . $file;

#carp "sakisafe warning: could not get file: $ERRNO" unless
$c->render( text => "file not found", status => 404 ) unless -e $path;
$c->render_file(
filepath => $path,
format => $ext,
content_disposition => 'inline'
);
};

app->log($log);
app->max_request_size(1024 * 1024 * 100);
app->max_request_size( 1024 * 1024 * 100 );

post '/upload' => sub ($c) { handle_file($c) };

Expand Down Expand Up @@ -158,6 +167,9 @@ sub handle_file {
</body>
</html>
__END__
@@ index.html.ep
<!DOCTYPE html>
<html lang="en">
Expand All @@ -175,20 +187,22 @@ sub handle_file {
<p>POST a file:</p>
<code>curl -F '[email protected]' https://<%= $c->req->url->to_abs->host; %></code>
<p>Post your text directly</p>
<code>curl -F 'file=@-' https://<%= $c->req->url->to_abs->host; %></code>
<code>curl -F 'file=@-' https://<%= $c->req->url->to_abs->host; %></code><br/>
<a href="https://git.suragu.net/svragv/sakisafe">Git repository</a>
</center>
<p>Running sakisafe 2.4.0</p>
<div class="left">
<h2>Or just upload a file here</h2>
<form ENCTYPE='multipart/form-data' method='post' action='/upload'>
<input type='file' name='file' size='30' required/>
<input type='file' name='file' size='30'/>
<input type='submit' value='upload'/>
</form>
</div>
</body>
</html>
__END__
=pod
=head1 sakisafe
Expand Down

0 comments on commit 968a288

Please sign in to comment.