From 3ea4378bd389e1cb3eee6eac7922610b43c1f365 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 30 Jan 2026 22:12:43 +0200 Subject: [PATCH] Add remove whitelist action to WAF logs --- app/Filament/Admin/Pages/Waf.php | 91 ++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/app/Filament/Admin/Pages/Waf.php b/app/Filament/Admin/Pages/Waf.php index 0a9c9c1..02c6872 100644 --- a/app/Filament/Admin/Pages/Waf.php +++ b/app/Filament/Admin/Pages/Waf.php @@ -295,6 +295,46 @@ class Waf extends Page implements HasForms, HasTable return false; } + protected function ruleMatchesEntry(array $rule, array $entry): bool + { + if (!is_array($rule)) { + return false; + } + if (isset($rule['enabled']) && !$rule['enabled']) { + return false; + } + + $ruleId = (string) ($entry['rule_id'] ?? ''); + $uri = (string) ($entry['uri'] ?? ''); + $host = (string) ($entry['host'] ?? ''); + $ip = (string) ($entry['remote_ip'] ?? ''); + + $idsRaw = (string) ($rule['rule_ids'] ?? ''); + $ids = preg_split('/[,\s]+/', $idsRaw, -1, PREG_SPLIT_NO_EMPTY) ?: []; + $ids = array_map('trim', $ids); + if ($ruleId !== '' && !empty($ids) && !in_array($ruleId, $ids, true)) { + return false; + } + + $matchType = (string) ($rule['match_type'] ?? ''); + $matchValue = (string) ($rule['match_value'] ?? ''); + + if ($matchType === 'ip' && $matchValue !== '' && $this->ipMatches($ip, $matchValue)) { + return true; + } + if ($matchType === 'uri_exact' && $matchValue !== '' && $uri === $matchValue) { + return true; + } + if ($matchType === 'uri_prefix' && $matchValue !== '' && str_starts_with($uri, $matchValue)) { + return true; + } + if ($matchType === 'host' && $matchValue !== '' && $host === $matchValue) { + return true; + } + + return false; + } + protected function ipMatches(string $ip, string $rule): bool { if ($ip === '' || $rule === '') { @@ -376,6 +416,50 @@ class Waf extends Page implements HasForms, HasTable ->send(); } + public function removeWhitelistEntry(array $record): void + { + $rules = $this->getWhitelistRules(); + $beforeCount = count($rules); + + $rules = array_values(array_filter($rules, function (array $rule) use ($record): bool { + return ! $this->ruleMatchesEntry($rule, $record); + })); + + if (count($rules) === $beforeCount) { + Notification::make() + ->title(__('No matching whitelist rule found')) + ->warning() + ->send(); + return; + } + + Setting::set('waf_whitelist_rules', json_encode(array_values($rules), JSON_UNESCAPED_SLASHES)); + $this->wafFormData['whitelist_rules'] = $rules; + + try { + $agent = new AgentClient; + $agent->wafApplySettings( + Setting::get('waf_enabled', '0') === '1', + (string) Setting::get('waf_paranoia', '1'), + Setting::get('waf_audit_log', '1') === '1', + $rules + ); + } catch (Exception $e) { + Notification::make() + ->title(__('Whitelist updated, but apply failed')) + ->body($e->getMessage()) + ->warning() + ->send(); + } + + $this->auditEntries = $this->markWhitelisted($this->auditEntries); + + Notification::make() + ->title(__('Whitelist removed')) + ->success() + ->send(); + } + public function table(Table $table): Table { return $table @@ -435,6 +519,13 @@ class Waf extends Page implements HasForms, HasTable ->color('primary') ->visible(fn (array $record): bool => empty($record['whitelisted'])) ->action(fn (array $record) => $this->whitelistEntry($record)), + \Filament\Actions\Action::make('removeWhitelist') + ->label(__('Remove whitelist')) + ->icon('heroicon-o-x-mark') + ->color('danger') + ->visible(fn (array $record): bool => !empty($record['whitelisted'])) + ->requiresConfirmation() + ->action(fn (array $record) => $this->removeWhitelistEntry($record)), ]) ->emptyStateHeading(__('No blocked rules found')) ->emptyStateDescription(__('No ModSecurity denials found in the audit log.'))