Reduce XSS false positives for safe URL helpers and model IDs

Skip XSS detection for:
- Safe URL helpers: route(), url(), asset(), secure_asset(),
  secure_url(), static_url(), action(), mix(), vite()
- Null coalesce with safe helpers: $var ?? url(...)
- Model ID patterns: $model->id (typically safe integers)

These patterns are unlikely to be user-controllable and create
noise that obscures real vulnerabilities.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 18:50:41 +09:00
parent b1cbddfa76
commit 30f2983102

View File

@@ -257,8 +257,22 @@ class XssRule extends BaseRule
$attrName = strtolower($urlMatches[1][$i][0]); $attrName = strtolower($urlMatches[1][$i][0]);
$expression = trim($match[0]); $expression = trim($match[0]);
// Skip if it's clearly a route or URL helper // Skip if it's clearly a safe URL helper (route, url, asset, etc.)
if (preg_match('/^\s*(route|url|asset|secure_asset|action)\s*\(/', $expression)) { // 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; continue;
} }
@@ -285,11 +299,32 @@ class XssRule extends BaseRule
$eventMatches = [1 => array_merge($eventMatchesDouble[1], $eventMatchesSingle[1])]; $eventMatches = [1 => array_merge($eventMatchesDouble[1], $eventMatchesSingle[1])];
foreach ($eventMatches[1] as $match) { 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]); $line = $this->getLineFromOffset($content, $match[1]);
$vulnerabilities[] = $this->createVulnerability( $vulnerabilities[] = $this->createVulnerability(
'XSS', 'XSS',
Vulnerability::SEVERITY_HIGH, Vulnerability::SEVERITY_HIGH,
$this->msg('xss.event_handler', ['expr' => trim($match[0])]), $this->msg('xss.event_handler', ['expr' => $expression]),
$filePath, $filePath,
$line, $line,
null, null,