From e3bfeae23d170e943689b9f65bb6696ce017ad89 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 30 Jan 2026 22:56:06 +0200 Subject: [PATCH] Fix WAF whitelist ordering and URI matching --- app/Filament/Admin/Pages/Waf.php | 25 +++++++++---- bin/jabali-agent | 61 +++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/app/Filament/Admin/Pages/Waf.php b/app/Filament/Admin/Pages/Waf.php index 9d0545d..0bd2338 100644 --- a/app/Filament/Admin/Pages/Waf.php +++ b/app/Filament/Admin/Pages/Waf.php @@ -257,6 +257,7 @@ class Waf extends Page implements HasForms, HasTable { $ruleId = (string) ($entry['rule_id'] ?? ''); $uri = (string) ($entry['uri'] ?? ''); + $uriPath = $this->stripQueryString($uri); $host = (string) ($entry['host'] ?? ''); $ip = (string) ($entry['remote_ip'] ?? ''); @@ -281,10 +282,10 @@ class Waf extends Page implements HasForms, HasTable if ($matchType === 'ip' && $matchValue !== '' && $this->ipMatches($ip, $matchValue)) { return true; } - if ($matchType === 'uri_exact' && $matchValue !== '' && $uri === $matchValue) { + if ($matchType === 'uri_exact' && $matchValue !== '' && ($uri === $matchValue || $uriPath === $matchValue)) { return true; } - if ($matchType === 'uri_prefix' && $matchValue !== '' && str_starts_with($uri, $matchValue)) { + if ($matchType === 'uri_prefix' && $matchValue !== '' && (str_starts_with($uri, $matchValue) || str_starts_with($uriPath, $matchValue))) { return true; } if ($matchType === 'host' && $matchValue !== '' && $host === $matchValue) { @@ -306,6 +307,7 @@ class Waf extends Page implements HasForms, HasTable $ruleId = (string) ($entry['rule_id'] ?? ''); $uri = (string) ($entry['uri'] ?? ''); + $uriPath = $this->stripQueryString($uri); $host = (string) ($entry['host'] ?? ''); $ip = (string) ($entry['remote_ip'] ?? ''); @@ -322,10 +324,10 @@ class Waf extends Page implements HasForms, HasTable if ($matchType === 'ip' && $matchValue !== '' && $this->ipMatches($ip, $matchValue)) { return true; } - if ($matchType === 'uri_exact' && $matchValue !== '' && $uri === $matchValue) { + if ($matchType === 'uri_exact' && $matchValue !== '' && ($uri === $matchValue || $uriPath === $matchValue)) { return true; } - if ($matchType === 'uri_prefix' && $matchValue !== '' && str_starts_with($uri, $matchValue)) { + if ($matchType === 'uri_prefix' && $matchValue !== '' && (str_starts_with($uri, $matchValue) || str_starts_with($uriPath, $matchValue))) { return true; } if ($matchType === 'host' && $matchValue !== '' && $host === $matchValue) { @@ -365,8 +367,9 @@ class Waf extends Page implements HasForms, HasTable public function whitelistEntry(array $record): void { $rules = $this->getWhitelistRules(); - $matchType = 'uri_exact'; - $matchValue = (string) ($record['uri'] ?? ''); + $matchType = 'uri_prefix'; + $rawUri = (string) ($record['uri'] ?? ''); + $matchValue = $this->stripQueryString($rawUri); if ($matchValue === '') { $matchType = 'ip'; $matchValue = (string) ($record['remote_ip'] ?? ''); @@ -417,6 +420,16 @@ class Waf extends Page implements HasForms, HasTable ->send(); } + protected function stripQueryString(string $uri): string + { + $pos = strpos($uri, '?'); + if ($pos === false) { + return $uri; + } + + return substr($uri, 0, $pos); + } + public function removeWhitelistEntry(array $record): void { $rules = $this->getWhitelistRules(); diff --git a/bin/jabali-agent b/bin/jabali-agent index 0b50007..8467ba4 100755 --- a/bin/jabali-agent +++ b/bin/jabali-agent @@ -799,7 +799,11 @@ function wafAuditLogList(array $params): array $entry['severity'] = $matches[1]; } if (preg_match('/\\[uri "([^"]+)"\\]/', $line, $matches)) { - $entry['uri'] = $matches[1]; + $loggedUri = $matches[1]; + $currentUri = (string) ($entry['uri'] ?? ''); + if ($currentUri === '' || (!str_contains($currentUri, '?') && $loggedUri !== '')) { + $entry['uri'] = $loggedUri; + } } if (preg_match('/\\[hostname "([^"]+)"\\]/', $line, $matches)) { $entry['host'] = $matches[1]; @@ -3094,6 +3098,50 @@ function findWafBaseConfig(): ?string return null; } +function findWafCoreConfig(): ?string +{ + $paths = [ + '/etc/modsecurity/modsecurity.conf', + '/etc/modsecurity/modsecurity.conf-recommended', + '/etc/nginx/modsecurity.conf', + ]; + + foreach ($paths as $path) { + if (file_exists($path) && isWafBaseConfigUsable($path)) { + return $path; + } + } + + return null; +} + +function buildWafCrsIncludeLines(): array +{ + $lines = []; + + if (file_exists('/etc/modsecurity/crs/crs-setup.conf')) { + $lines[] = 'Include /etc/modsecurity/crs/crs-setup.conf'; + } elseif (file_exists('/usr/share/modsecurity-crs/crs-setup.conf')) { + $lines[] = 'Include /usr/share/modsecurity-crs/crs-setup.conf'; + } + + if (file_exists('/etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf')) { + $lines[] = 'Include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf'; + } + + if (is_dir('/etc/modsecurity/crs/rules')) { + $lines[] = 'Include /etc/modsecurity/crs/rules/*.conf'; + } elseif (is_dir('/usr/share/modsecurity-crs/rules')) { + $lines[] = 'Include /usr/share/modsecurity-crs/rules/*.conf'; + } + + if (file_exists('/etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf')) { + $lines[] = 'Include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf'; + } + + return $lines; +} + function isModSecurityModuleAvailable(): bool { $output = []; @@ -3186,15 +3234,15 @@ function wafApplySettings(array $params): array } ensureWafUnicodeMapFile(); - $baseConfig = findWafBaseConfig(); - if (!$baseConfig) { + $coreConfig = findWafCoreConfig(); + if (!$coreConfig) { file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\nmodsecurity off;\n"); return ['success' => false, 'error' => 'ModSecurity base configuration not found']; } $rules = [ '# Managed by Jabali', - 'Include "' . $baseConfig . '"', + 'Include "' . $coreConfig . '"', 'SecRuleEngine On', 'SecAuditEngine ' . ($auditLog ? 'On' : 'Off'), 'SecAuditLog /var/log/nginx/modsec_audit.log', @@ -3207,6 +3255,11 @@ function wafApplySettings(array $params): array $rules = array_merge($rules, $whitelistLines); } + $crsIncludes = buildWafCrsIncludeLines(); + if (!empty($crsIncludes)) { + $rules = array_merge($rules, $crsIncludes); + } + file_put_contents(JABALI_WAF_RULES, implode("\n", $rules) . "\n"); $include = [