Files
Atomcms-edit/app/Services/SystemFixService.php
T
root b1739cabbf fix(security): eliminate remaining critical vulnerabilities
- SystemFixService: removed ALL shell_exec/sudo calls (30+ instances), replaced with
  safe PHP alternatives (mkdir, chmod, disk_total_space, Artisan calls)
- InstallationController: added ALLOWED_SETTINGS whitelist to prevent arbitrary
  settings manipulation via request data
- ExceptionHandler: removed dangerous npm run build execution and hardcoded
  chown/chmod paths from auto-recovery
- AuthController: fixed user enumeration timing attack by running Hash::make()
  even when user doesn't exist (constant-time comparison)
- DDoSDetectionCommand: added IP validation (FILTER_VALIDATE_IP) before blocking
  to prevent iptables manipulation with spoofed/malicious IPs
- trackRequest: now validates IP before storing in cache
2026-05-19 19:46:38 +02:00

200 lines
7.2 KiB
PHP
Executable File

<?php
namespace App\Services;
use Illuminate\Support\Facades\Artisan;
class SystemFixService
{
private array $results = [];
public function checkAndFixAll(): array
{
$this->results = [];
$this->checkPhpExtensions();
$this->checkDatabase();
$this->fixPermissions();
$this->checkDiskSpace();
$this->clearOldCaches();
return $this->results;
}
private function checkPhpExtensions(): void
{
$requiredExtensions = ['pdo_mysql', 'curl', 'json', 'mbstring', 'xml', 'zip', 'bcmath'];
$missing = [];
foreach ($requiredExtensions as $ext) {
if (! extension_loaded($ext)) {
$missing[] = $ext;
}
}
if ($missing === []) {
$this->results[] = ['item' => 'PHP Extensions', 'status' => 'ok', 'message' => 'Alle vereiste extensies geïnstalleerd'];
} else {
$this->results[] = ['item' => 'PHP Extensions', 'status' => 'warning', 'message' => 'Ontbrekend: ' . implode(', ', $missing) . ' — installeer via: sudo apt install php-' . implode(' php-', $missing)];
}
}
private function checkDatabase(): void
{
try {
$host = setting('emulator_database_host', '127.0.0.1');
$port = setting('emulator_database_port', '3306');
$name = setting('emulator_database_name', config('database.connections.mysql.database', ''));
$username = setting('emulator_database_username', config('database.connections.mysql.username', ''));
$password = setting('emulator_database_password', config('database.connections.mysql.password', ''));
if (empty($name) || empty($username)) {
$this->results[] = ['item' => 'Emulator Database', 'status' => 'warning', 'message' => 'Niet geconfigureerd'];
$this->autoFixEmulatorDatabase();
return;
}
$validationErrors = $this->validateDatabaseInputs($host, $port, $name, $username);
if ($validationErrors !== []) {
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => implode(', ', $validationErrors)];
return;
}
try {
$pdo = new \PDO(
"mysql:host={$host};port={$port};charset=utf8mb4",
$username,
$password,
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_TIMEOUT => 5],
);
$pdo->exec("USE `{$name}`");
$pdo->query('SELECT 1');
$this->results[] = ['item' => 'Emulator Database', 'status' => 'ok', 'message' => "Verbonden met {$name}"];
} catch (\PDOException $e) {
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => $e->getMessage()];
}
} catch (\Exception $e) {
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => $e->getMessage()];
}
}
private function validateDatabaseInputs(string $host, string $port, string $name, string $username): array
{
$errors = [];
if ($host === '' || $host === '0') {
$errors[] = 'Host is leeg';
} elseif (! filter_var($host, FILTER_VALIDATE_IP) && ! preg_match('/^[a-zA-Z0-9.-]+$/', $host)) {
$errors[] = 'Ongeldige host';
}
if ($port === '' || $port === '0') {
$errors[] = 'Port is leeg';
} elseif (! is_numeric($port) || $port < 1 || $port > 65535) {
$errors[] = 'Ongeldige port';
}
if ($name === '' || $name === '0') {
$errors[] = 'Database naam is leeg';
} elseif (! preg_match('/^\w+$/', $name)) {
$errors[] = 'Ongeldige database naam';
}
if ($username === '' || $username === '0') {
$errors[] = 'Gebruikersnaam is leeg';
} elseif (! preg_match('/^\w+$/', $username)) {
$errors[] = 'Ongeldige gebruikersnaam';
}
return $errors;
}
private function autoFixEmulatorDatabase(): void
{
$host = config('database.connections.mysql.host', '127.0.0.1');
$port = config('database.connections.mysql.port', '3306');
$name = config('database.connections.mysql.database', '');
$username = config('database.connections.mysql.username', '');
$password = config('database.connections.mysql.password', '');
if (empty($name) || empty($username)) {
$this->results[] = ['item' => 'Emulator Database Fix', 'status' => 'warning', 'message' => 'Kon geen geldige database vinden'];
return;
}
$settings = app(SettingsService::class);
$settings->set('emulator_database_host', $host);
$settings->set('emulator_database_port', (string) $port);
$settings->set('emulator_database_name', $name);
$settings->set('emulator_database_username', $username);
$settings->set('emulator_database_password', $password);
$this->results[] = ['item' => 'Emulator Database Fix', 'status' => 'fixed', 'message' => "Automatisch gefixt: {$name}@{$host}"];
}
private function fixPermissions(): void
{
$directories = [
storage_path(),
storage_path('logs'),
storage_path('framework'),
storage_path('framework/cache'),
storage_path('framework/sessions'),
storage_path('framework/views'),
bootstrap_path('cache'),
];
$fixed = false;
foreach ($directories as $dir) {
if (! is_dir($dir)) {
@mkdir($dir, 0775, true);
$fixed = true;
}
if (is_writable($dir)) {
continue;
}
@chmod($dir, 0775);
$fixed = true;
}
$this->results[] = ['item' => 'Permissions', 'status' => $fixed ? 'fixed' : 'ok', 'message' => $fixed ? 'Permissions gefixt' : 'Al correct geconfigureerd'];
}
private function checkDiskSpace(): void
{
$total = disk_total_space('/');
$free = disk_free_space('/');
if ($total === false || $free === false) {
$this->results[] = ['item' => 'Disk Space', 'status' => 'warning', 'message' => 'Kon schijfruimte niet bepalen'];
return;
}
$usagePercent = (int) ((($total - $free) / $total) * 100);
if ($usagePercent >= 90) {
$this->results[] = ['item' => 'Disk Space', 'status' => 'error', 'message' => "Schijf {$usagePercent}% vol!"];
} elseif ($usagePercent >= 75) {
$this->results[] = ['item' => 'Disk Space', 'status' => 'warning', 'message' => "Schijf {$usagePercent}% gebruikt"];
} else {
$this->results[] = ['item' => 'Disk Space', 'status' => 'ok', 'message' => "Schijf {$usagePercent}% gebruikt"];
}
}
private function clearOldCaches(): void
{
Artisan::call('cache:clear');
Artisan::call('view:clear');
Artisan::call('config:clear');
$this->results[] = ['item' => 'Cache Cleanup', 'status' => 'fixed', 'message' => 'Caches opgeruimd'];
}
}