diff --git a/src/Rules/XssRule.php b/src/Rules/XssRule.php index c66da22..1c3cfb2 100644 --- a/src/Rules/XssRule.php +++ b/src/Rules/XssRule.php @@ -257,8 +257,22 @@ class XssRule extends BaseRule $attrName = strtolower($urlMatches[1][$i][0]); $expression = trim($match[0]); - // Skip if it's clearly a route or URL helper - if (preg_match('/^\s*(route|url|asset|secure_asset|action)\s*\(/', $expression)) { + // Skip if it's clearly a safe URL helper (route, url, asset, etc.) + // These generate URLs from application config, not user input + if (preg_match('/^\s*(route|url|asset|secure_asset|secure_url|static_url|action|mix|vite)\s*\(/', $expression)) { + continue; + } + + // Skip if expression uses null coalesce with safe helpers + if (preg_match('/\?\?\s*(route|url|asset|secure_asset|secure_url|static_url|action|mix|vite)\s*\(/', $expression)) { + continue; + } + + // Skip if expression only contains model ID access patterns (e.g., $model->id) + // These are typically safe integers from the database + if (preg_match('/^[\s\w\$\-\>\.\'\"\/\(\),]+$/', $expression) && + preg_match('/\$\w+->id\b/', $expression) && + !preg_match('/\$_(GET|POST|REQUEST|COOKIE|SERVER)/', $expression)) { continue; } @@ -285,11 +299,32 @@ class XssRule extends BaseRule $eventMatches = [1 => array_merge($eventMatchesDouble[1], $eventMatchesSingle[1])]; foreach ($eventMatches[1] as $match) { + $expression = trim($match[0]); + + // Skip if it's clearly a safe URL helper (route, url, asset, etc.) + // These generate URLs from application config, not user input + if (preg_match('/^\s*(route|url|asset|secure_asset|secure_url|static_url|action|mix|vite)\s*\(/', $expression)) { + continue; + } + + // Skip if expression uses null coalesce with safe helpers + if (preg_match('/\?\?\s*(route|url|asset|secure_asset|secure_url|static_url|action|mix|vite)\s*\(/', $expression)) { + continue; + } + + // Skip if expression only contains model ID access patterns (e.g., $model->id) + // These are typically safe integers from the database + if (preg_match('/^[\s\w\$\-\>\.\'\"\/\(\),]+$/', $expression) && + preg_match('/\$\w+->id\b/', $expression) && + !preg_match('/\$_(GET|POST|REQUEST|COOKIE|SERVER)/', $expression)) { + continue; + } + $line = $this->getLineFromOffset($content, $match[1]); $vulnerabilities[] = $this->createVulnerability( 'XSS', Vulnerability::SEVERITY_HIGH, - $this->msg('xss.event_handler', ['expr' => trim($match[0])]), + $this->msg('xss.event_handler', ['expr' => $expression]), $filePath, $line, null,