diff options
| author | grothedev <grothedev@gmail.com> | 2026-03-06 11:06:13 -0600 |
|---|---|---|
| committer | grothedev <grothedev@gmail.com> | 2026-03-06 11:06:13 -0600 |
| commit | 4e4827e9f5ef58085e032a88276dc8ae2b4462b0 (patch) | |
| tree | 0f818a0e0b1fec7e8495e3175b14bc44627aed6d | |
| parent | 0609605200821c58b2b3f49248998fc10ea71f97 (diff) | |
fix some security issues
| -rw-r--r-- | app/Http/Controllers/SiteController.php | 14 | ||||
| -rwxr-xr-x | resources/views/template.blade.php | 1 | ||||
| -rwxr-xr-x | routes/web.php | 92 |
3 files changed, 51 insertions, 56 deletions
diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php index a970bde..0d6aa6c 100644 --- a/app/Http/Controllers/SiteController.php +++ b/app/Http/Controllers/SiteController.php @@ -112,17 +112,13 @@ class SiteController extends Controller public function search4chan(Request $req){ $query = $req->input('query'); - $board = ""; Log::info('search4chan()'); - if ( $req->input('board') != null) { - $board = $req->input('board').'/'.$query; - } - $cmd = "python ./4chansearch.py ${query}"; - if ( $board != "" ){ - $cmd .= "-b ${board}"; + + $cmd = 'python ./4chansearch.py ' . escapeshellarg($query); + if ($req->input('board') != null) { + $cmd .= ' -b ' . escapeshellarg($req->input('board')); } - $shellcmd = escapeshellcmd($cmd); - exec($shellcmd, $res, $ret); + exec($cmd, $res, $ret); return $res; } diff --git a/resources/views/template.blade.php b/resources/views/template.blade.php index 20a25f8..4305656 100755 --- a/resources/views/template.blade.php +++ b/resources/views/template.blade.php @@ -4,6 +4,7 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TG</title> + <meta name="csrf-token" content="{{ csrf_token() }}"> <link rel="stylesheet" href="/css/home.css"> @yield('head') </head> diff --git a/routes/web.php b/routes/web.php index 7e418bb..8370fd2 100755 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ <?php use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Storage; use App\Http\Controllers\SiteController; use App\Http\Controllers\ProfileController; use App\Http\Controllers\WritingController; @@ -13,69 +14,44 @@ Route::get('/', function () { return view('home'); }); -Route::get('/env', [SiteController::class, 'env']); - Route::get('/dq', [SiteController::class, 'duneQuote']); -//temporarily desabled for security -//Route::post('/f', [SiteController::class, 'uploadFiles']); //TODO later use file resource or FPR service Route::get('/f/{path}', function($path){ + // Sanitize: strip directory traversal, only allow basename + $path = basename($path); $storpath = storage_path('app/public/uploads/' . $path); if (!file_exists($storpath)){ - return response()->json(['error' => 'file not found ' . $storpath]); + abort(404, 'File not found.'); } - $mime = Storage::mimeType($storpath); - - //return response()->json([ - // 'success' => true, - // 'message' => 'file uploaded', - // 'url' => $storpath, - //]); - -// this is how you would return the file which would download the file in browser + $mime = Storage::mimeType('public/uploads/' . $path); return response()->file($storpath, [ 'Content-Type' => $mime, 'Content-Disposition' => 'inline; filename="'.$path.'"' ]); }); -/*Route::get('/yt', function () { - return view('youtube'); -});*/ - -// Get video info and direct stream URL (recommended approach) -//Route::post('/yt/info', [YouTubeController::class, 'getVideoInfo']); -// Stream video through Laravel proxy (with range support) -//Route::get('/yt/stream', [YouTubeController::class, 'streamVideo']); -// Stream directly via yt-dlp (not recommended - no seeking support) -//Route::get('/yt/stream-direct', [YouTubeController::class, 'streamDirect']); -// Get available formats for a video -//Route::post('/yt/formats', [YouTubeController::class, 'getFormats']); - Route::get('/mu/{path}', function($path){ + // Sanitize: strip directory traversal, only allow basename + $path = basename($path); $storpath = storage_path('app/public/band/' . $path); if (!file_exists($storpath)){ - return response()->json(['error' => 'file not found ' . $storpath]); + abort(404, 'File not found.'); } - $mime = Storage::mimeType($storpath); + $mime = Storage::mimeType('public/band/' . $path); return response()->file($storpath, [ 'Content-Type' => $mime, 'Content-Disposition' => 'inline; filename="'.$path.'"' ]); }); -Route::resource('w', WritingController::class); -Route::resource('l', LinkController::class)->only(['index', 'show', 'create']); -Route::resource('f', \App\Http\Controllers\FileController::class); +// Public read-only routes +Route::resource('w', WritingController::class)->only(['index', 'show']); +Route::resource('l', LinkController::class)->only(['index', 'show']); +Route::resource('f', \App\Http\Controllers\FileController::class)->only(['index', 'show']); Route::get('/test', [SiteController::class, 'test']); -Route::get('/4', [SiteController::class, 'search4chan']); - -Route::get('/dashboard', function () { - return view('dashboard'); -})->middleware(['auth', 'verified'])->name('dashboard'); - +// Auth-protected routes Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); @@ -83,36 +59,49 @@ Route::middleware('auth')->group(function () { Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); Route::get('/dashboard/stats', [DashboardController::class, 'statistics'])->name('dashboard.stats'); Route::resource('l', LinkController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); + Route::resource('w', WritingController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); + Route::resource('f', \App\Http\Controllers\FileController::class)->only(['create', 'store', 'edit', 'update', 'destroy']); }); +// Admin-only routes Route::middleware(['auth', \App\Http\Middleware\Admin::class])->group(function () { Route::get('/admin', [AdminController::class, 'index'])->name('admin'); Route::post('/l/import', [LinkController::class, 'import'])->name('l.import'); + Route::get('/env', [SiteController::class, 'env']); + Route::get('/4', [SiteController::class, 'search4chan']); }); +// Session updates - auth required, whitelisted keys only Route::post('/update-session', function(Request $req){ if ($req->input('key') && $req->input('value')){ + $allowed = ['theme', 'locale', 'sidebar']; + if (!in_array($req->input('key'), $allowed)) { + abort(403, 'Session key not allowed.'); + } session()->put($req->input('key'), $req->input('value')); return redirect()->back(); } -}); +})->middleware('auth'); require __DIR__.'/auth.php'; -Route::get('/toy/{v}', function($v){ +Route::get('/toy/{v?}', function($v = null){ if (is_null($v)){ return view('toys.index'); } + // Only allow alphanumeric and hyphens + if (!preg_match('/^[a-zA-Z0-9\-]+$/', $v)) { + abort(404); + } + if (!view()->exists('toys.'.$v)) { + abort(404); + } return view('toys.'.$v); }); -// Audio stream endpoint - streams from ffmpeg +// Audio stream - requires auth to prevent abuse Route::get('/stream/audio', function() { - // Configure ffmpeg command to output mp3 stream - // Example: stream from microphone or other source - //$command = 'ffmpeg -re -i /dev/null -f mp3 -'; - //$command = 'ffmpeg -re -f pulse -i defaul -f mp3 -'; - $command = 'ffmpeg -re -i http://somafm.com/vaporwaves.pls :-f mp3 -'; + $command = 'ffmpeg -re -i http://somafm.com/vaporwaves.pls -f mp3 -'; return response()->stream(function() use ($command) { $handle = popen($command, 'r'); @@ -132,8 +121,17 @@ Route::get('/stream/audio', function() { 'Cache-Control' => 'no-cache', 'X-Accel-Buffering' => 'no' ]); -}); +})->middleware('auth'); Route::get('/newtab', function() { return view('newtab'); }); + +// Catch-all: only allow specific whitelisted views +Route::get('/{v}', function($v){ + $allowed = ['notes', 'kyanite', 'marked', 'v']; + if (!in_array($v, $allowed)) { + abort(404); + } + return view($v); +})->where('v', '[a-zA-Z0-9\-]+'); |
