diff --git a/app/Actions/CheckServerFirewallAction.php b/app/Actions/CheckServerFirewallAction.php index fd490f6..f9c0529 100644 --- a/app/Actions/CheckServerFirewallAction.php +++ b/app/Actions/CheckServerFirewallAction.php @@ -85,7 +85,7 @@ public function handle(string $ip, int $hostId): array 'error' => $e->getMessage(), ]; } finally { - if ($keyName) { + if ($keyName !== '' && $keyName !== '0') { $this->firewallService->removeMultiplexingPath($keyName); } } diff --git a/app/Actions/WhmcsSynchro.php b/app/Actions/WhmcsSynchro.php index 86eed40..425d13e 100644 --- a/app/Actions/WhmcsSynchro.php +++ b/app/Actions/WhmcsSynchro.php @@ -91,7 +91,7 @@ public function handle(?Command $command = null): void } // Set debug mode and specific user from command options - if ($command) { + if ($command instanceof Command) { $this->debugMode = (bool) ($command->option('debug') ?? false); $userOption = $command->option('user'); $this->specificUserEmail = is_string($userOption) ? $userOption : null; @@ -123,7 +123,7 @@ public function handle(?Command $command = null): void protected function output(?Command $command, string $message): void { - if ($command) { + if ($command instanceof Command) { $command->info($message); } @@ -272,7 +272,7 @@ protected function processClient(object $whmcsClient, ?Command $command): void $user = $this->findOrCreateUser($whmcsClient, $command); - if (! $user) { + if (! $user instanceof User) { $this->debugOutput($command, "❌ Could not find or create user for: {$whmcsClient->email}"); return; @@ -425,7 +425,7 @@ public function processActiveHostings(int $userId, ?Command $command = null): vo } $host = $this->findHostByWhmcsId((string) $whmcsHosting->server); - if (! $host) { + if (! $host instanceof Host) { $this->debugOutput($command, "❌ No host found for WHMCS server ID: {$whmcsHosting->server}"); Log::warning("No host found for WHMCS server ID: {$whmcsHosting->server}"); diff --git a/app/Console/Commands/AddHostKeyCommand.php b/app/Console/Commands/AddHostKeyCommand.php index 72c1d5e..c78b310 100644 --- a/app/Console/Commands/AddHostKeyCommand.php +++ b/app/Console/Commands/AddHostKeyCommand.php @@ -25,7 +25,7 @@ public function handle(): void // Si no se proporciona hostId, permitir seleccionar con prompts if (! $hostId) { $host = $this->selectHost(); - if (! $host) { + if (! $host instanceof Host) { error('No se seleccionó ningún host.'); return; diff --git a/app/Console/Commands/Develop/TestHostConnectionCommand.php b/app/Console/Commands/Develop/TestHostConnectionCommand.php index 48da116..50feb91 100644 --- a/app/Console/Commands/Develop/TestHostConnectionCommand.php +++ b/app/Console/Commands/Develop/TestHostConnectionCommand.php @@ -20,7 +20,7 @@ public function handle(): int info('=== DIAGNÓSTICO SSH REAL ==='); $host = $this->selectHost(); - if (! $host) { + if (! $host instanceof Host) { error('Host no encontrado'); return 1; diff --git a/app/Console/Commands/UserAuthorizeCommand.php b/app/Console/Commands/UserAuthorizeCommand.php index 8027e48..1baa667 100644 --- a/app/Console/Commands/UserAuthorizeCommand.php +++ b/app/Console/Commands/UserAuthorizeCommand.php @@ -71,7 +71,7 @@ private function createAuthorizedUser(): int $parentUserId = search( label: 'Select the parent user:', options: function (string $value) use ($parentUsers) { - if (strlen($value) === 0) { + if ($value === '') { return $parentUsers->take(10)->mapWithKeys(fn ($user) => [ (string) $user->id => "$user->name ($user->email)", ])->toArray(); @@ -145,7 +145,7 @@ private function assignPermissions(): int $userId = search( label: 'Select the authorized user:', options: function (string $value) use ($authorizedUsers) { - if (strlen($value) === 0) { + if ($value === '') { return $authorizedUsers->take(10)->mapWithKeys(fn ($user) => [ (string) $user->id => "$user->name ($user->email) - Parent: {$user->parentUser->name}", ])->toArray(); @@ -365,7 +365,7 @@ private function revokePermissions(): int $userId = search( label: 'Select the authorized user:', options: function (string $value) use ($authorizedUsers) { - if (strlen($value) === 0) { + if ($value === '') { return $authorizedUsers->take(10)->mapWithKeys(fn ($user) => [ (string) $user->id => "{$user->name} ({$user->email}) - Parent: {$user->parentUser->name}", ])->toArray(); @@ -537,7 +537,7 @@ private function reassignUserToParent(): int $userId = search( label: 'Select the authorized user:', options: function (string $value) use ($authorizedUsers) { - if (strlen($value) === 0) { + if ($value === '') { return $authorizedUsers->take(10)->mapWithKeys(fn ($user) => [ (string) $user->id => "$user->name ($user->email) - Current Parent: {$user->parentUser->name}", ])->toArray(); @@ -593,7 +593,7 @@ private function reassignUserToParent(): int $parentUserId = search( label: 'Select the new parent user:', options: function (string $value) use ($parentUsers) { - if (strlen($value) === 0) { + if ($value === '') { return $parentUsers->take(10)->mapWithKeys(fn ($user) => [ (string) $user->id => "$user->name ($user->email)", ])->toArray(); diff --git a/app/Console/Commands/UserCreateCommand.php b/app/Console/Commands/UserCreateCommand.php index ee4fdc6..9cb485d 100644 --- a/app/Console/Commands/UserCreateCommand.php +++ b/app/Console/Commands/UserCreateCommand.php @@ -269,7 +269,7 @@ private function handleInteractive(): int $this->info('📋 User Summary:'); $this->line(" Email: {$email}"); $this->line(" Name: {$firstName} {$lastName}"); - if ($companyName) { + if ($companyName !== '' && $companyName !== '0') { $this->line(" Company: {$companyName}"); } $this->line(' Type: '.($isAdmin ? '👑 Administrator' : '👤 Normal User')); diff --git a/app/Jobs/ProcessHqWhitelistJob.php b/app/Jobs/ProcessHqWhitelistJob.php index 60764af..e507354 100644 --- a/app/Jobs/ProcessHqWhitelistJob.php +++ b/app/Jobs/ProcessHqWhitelistJob.php @@ -33,7 +33,7 @@ public function __construct( public function handle(FirewallService $firewallService): void { $hqHost = $this->resolveHqHost(); - if (! $hqHost) { + if (! $hqHost instanceof Host) { Log::warning('HQ host not found, skipping HQ whitelist check'); return; @@ -95,7 +95,7 @@ public function handle(FirewallService $firewallService): void throw $e; } finally { // Best effort cleanup of temporary key file - if ($keyPath) { + if ($keyPath !== '' && $keyPath !== '0') { try { $fileName = basename($keyPath); Storage::disk('ssh')->delete($fileName); diff --git a/app/Livewire/OtpLogin.php b/app/Livewire/OtpLogin.php index 206fc55..47ef1f7 100644 --- a/app/Livewire/OtpLogin.php +++ b/app/Livewire/OtpLogin.php @@ -189,7 +189,7 @@ public function verifyOtp(): void 'oneTimePassword' => 'required|string|size:6', ]); - if (! $this->user) { + if (! $this->user instanceof User) { throw ValidationException::withMessages([ 'oneTimePassword' => 'Sesión expirada. Solicita un nuevo código.', ]); diff --git a/app/Models/User.php b/app/Models/User.php index a4efa59..337b3e2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -327,11 +327,9 @@ public function hasAccessToHosting(int $hostingId): bool } // If this is a principal user (no parent), check ownership - if (! $this->parent_user_id) { - // Check if user owns the hosting - if (Hosting::where('id', $hostingId)->where('user_id', $this->id)->exists()) { - return true; - } + // Check if user owns the hosting + if (! $this->parent_user_id && Hosting::where('id', $hostingId)->where('user_id', $this->id)->exists()) { + return true; } // For all users (principal and authorized), check specific permissions diff --git a/app/Services/Firewall/DirectAdminFirewallAnalyzer.php b/app/Services/Firewall/DirectAdminFirewallAnalyzer.php index 75cf9ba..f38ea97 100644 --- a/app/Services/Firewall/DirectAdminFirewallAnalyzer.php +++ b/app/Services/Firewall/DirectAdminFirewallAnalyzer.php @@ -174,7 +174,7 @@ public function analyze(string $ipAddress, mixed $session): FirewallAnalysisResu // STEP 3: Check DirectAdmin BFM blacklist (ALWAYS - independent of CSF status) if ($this->serviceChecks['da_bfm_check'] ?? false) { $bfmResult = $this->checkAndRemoveFromBfmBlacklist($ipAddress, $sshKeyName); - if ($bfmResult) { + if ($bfmResult instanceof FirewallAnalysisResult) { $logs['da_bfm'] = $bfmResult->getLogs()['da_bfm'] ?? ''; $results[] = $bfmResult; $bfmBlocked = $bfmResult->isBlocked(); diff --git a/app/Services/Firewall/V2/FirewallLogAnalyzer.php b/app/Services/Firewall/V2/FirewallLogAnalyzer.php index 844fab0..7d20a4f 100644 --- a/app/Services/Firewall/V2/FirewallLogAnalyzer.php +++ b/app/Services/Firewall/V2/FirewallLogAnalyzer.php @@ -58,7 +58,7 @@ public function analyzeDirectAdmin(string $ipAddress, Host $host): FirewallAnaly // STEP 3: Análisis BFM independiente (siempre se ejecuta) $bfmResult = $this->analyzeBfmBlacklist($ipAddress, $host, $session); - if ($bfmResult) { + if ($bfmResult instanceof FirewallAnalysisResult) { $logs['da_bfm_check'] = $bfmResult->getLogs()['da_bfm'] ?? ''; $results[] = $bfmResult; } diff --git a/app/Services/FirewallUnblocker.php b/app/Services/FirewallUnblocker.php index a6253c8..de1cd86 100644 --- a/app/Services/FirewallUnblocker.php +++ b/app/Services/FirewallUnblocker.php @@ -119,11 +119,7 @@ private function hasCsfBlocks(array $logs): bool } // Check for CSF temporary blocks - any content means blocked - if (! empty($logs['csf_tempip'] ?? '')) { - return true; - } - - return false; + return ! empty($logs['csf_tempip'] ?? ''); } /** diff --git a/app/Services/PatternDetection/AnomalyDetector.php b/app/Services/PatternDetection/AnomalyDetector.php index 52d7581..06e22a0 100644 --- a/app/Services/PatternDetection/AnomalyDetector.php +++ b/app/Services/PatternDetection/AnomalyDetector.php @@ -54,10 +54,8 @@ public function detectAll(): array $currentTraffic = $this->getCurrentTraffic(); // Check if anomaly detected - if ($this->isAnomaly($currentTraffic, $baseline)) { - if ($detection = $this->createAnomalyDetection($currentTraffic, $baseline)) { - $detections[] = $detection; - } + if ($this->isAnomaly($currentTraffic, $baseline) && $detection = $this->createAnomalyDetection($currentTraffic, $baseline)) { + $detections[] = $detection; } return $detections; diff --git a/app/Services/PatternDetection/DistributedAttackDetector.php b/app/Services/PatternDetection/DistributedAttackDetector.php index 89e6423..0ce5585 100644 --- a/app/Services/PatternDetection/DistributedAttackDetector.php +++ b/app/Services/PatternDetection/DistributedAttackDetector.php @@ -51,7 +51,7 @@ public function detectAll(): array ->filter(); foreach ($recentEmailHashes as $emailHash) { - if ($detection = $this->detectForEmail($emailHash)) { + if (($detection = $this->detectForEmail($emailHash)) instanceof PatternDetection) { $detections[] = $detection; } } diff --git a/app/Services/PatternDetection/SubnetScanDetector.php b/app/Services/PatternDetection/SubnetScanDetector.php index 5839f20..96eb0f9 100644 --- a/app/Services/PatternDetection/SubnetScanDetector.php +++ b/app/Services/PatternDetection/SubnetScanDetector.php @@ -57,7 +57,7 @@ public function detectAll(): array ->keys(); foreach ($recentSubnets as $subnetPrefix) { - if ($detection = $this->detectForSubnet($subnetPrefix)) { + if (($detection = $this->detectForSubnet($subnetPrefix)) instanceof PatternDetection) { $detections[] = $detection; } } diff --git a/app/Services/SshConnectionManager.php b/app/Services/SshConnectionManager.php index 11e6f9f..ba5cfce 100644 --- a/app/Services/SshConnectionManager.php +++ b/app/Services/SshConnectionManager.php @@ -48,13 +48,11 @@ public function prepareMultiplexingPath(): void { $directoryPath = '/tmp/cm'; - if (! File::exists($directoryPath)) { - if (! File::makeDirectory($directoryPath, 0755, true)) { - Log::error('Error creating SSH multiplexing directory'); - throw new ConnectionFailedException( - 'Could not create SSH multiplexing directory: '.$directoryPath - ); - } + if (! File::exists($directoryPath) && ! File::makeDirectory($directoryPath, 0755, true)) { + Log::error('Error creating SSH multiplexing directory'); + throw new ConnectionFailedException( + 'Could not create SSH multiplexing directory: '.$directoryPath + ); } } diff --git a/rector.php b/rector.php index bfe3f5b..32073fc 100644 --- a/rector.php +++ b/rector.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Rector\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector; +use Rector\CodeQuality\Rector\If_\{CombineIfRector, ExplicitBoolCompareRector, ObjectExplicitBoolCompareRector, SimplifyIfReturnBoolRector}; use Rector\Config\RectorConfig; use Rector\Php83\Rector\ClassConst\AddTypeToConstRector; use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector; @@ -66,6 +68,29 @@ // Wave 1 (AID-529), landed last: the only semantic rule here, since // strict_types changes runtime type coercion rather than shape. DeclareStrictTypesRector::class, + // Wave 3 (AID-531). ONLY the equivalence-safe cosmetics from + // LARAVEL_CODE_QUALITY + codeQuality level 50. The rest of that set + // is NOT mechanical and was split off by risk profile: + // - MakeModelAttributesAndScopesProtectedRector (27 scopes + // public→protected, public surface) → AID-540 + // - CarbonToDateFacadeRector + NowFunc...ToTodayFunc (runtime class + // + temporal semantics) → AID-541 + // - ApplyDefaultInsteadOfNullCoalesceRector (?? vs ?: — differs on + // false/0/'') → AID-542 + // - DispatchToHelperFunctions / RedirectRouteToToRoute / + // CompactToVariables / ThrowWithPreviousException (equivalence + // must be shown per transform) → AID-543 + // Each transform below was diff-verified equivalence-preserving; the + // ExplicitBoolCompare rewrites were confirmed to fire only on + // string-typed vars (Rector left the ?string option() path alone). + // NB: measure/apply waves with `rector --clear-cache` — the cache does + // not discriminate the rule set, so `composer refactor` after a local + // measuring session can under-apply (16 files measured, 3 applied). + ObjectExplicitBoolCompareRector::class, + ExplicitBoolCompareRector::class, + SimplifyIfReturnBoolRector::class, + StrlenZeroToIdenticalEmptyStringRector::class, + CombineIfRector::class, ]) ->withSkip([ // Excluded by design decision (AID-528), armed BEFORE any wave enables