From 5356f7d2f8f9a6376b0a27fc862f79f4590b7f15 Mon Sep 17 00:00:00 2001 From: Yutaka Kurosaki Date: Sat, 31 Jan 2026 19:04:44 +0900 Subject: [PATCH] Simplify XSS safe pattern: check for e() anywhere in expression Instead of checking for specific patterns like nl2br(e($var)), now checks if e(), htmlspecialchars(), or htmlentities() appears anywhere in the expression. This covers more use cases: - {!! e($var) !!} - {!! nl2br(e($var)) !!} - {!! wordwrap(e($var), 80) !!} - {!! str_replace('x', 'y', e($var)) !!} Still flags expressions with escape-breaking functions: - {!! html_entity_decode(e($var)) !!} -> flagged Co-Authored-By: Claude Opus 4.5 --- src/Rules/XssRule.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Rules/XssRule.php b/src/Rules/XssRule.php index bd0f345..42e1ae8 100644 --- a/src/Rules/XssRule.php +++ b/src/Rules/XssRule.php @@ -493,13 +493,25 @@ class XssRule extends BaseRule '/^\s*action\s*\(/', // action() helper '/^\s*mix\s*\(/', // mix() helper (Laravel Mix) '/^\s*vite\s*\(/', // vite() helper (Vite) - // nl2br with e() - common safe pattern for displaying user text with line breaks - // e() escapes HTML first, then nl2br() adds
tags - '/^\s*nl2br\s*\(\s*e\s*\(/', // nl2br(e($var)) - '/^\s*nl2br\s*\(\s*htmlspecialchars\s*\(/', // nl2br(htmlspecialchars($var)) - '/^\s*nl2br\s*\(\s*htmlentities\s*\(/', // nl2br(htmlentities($var)) ]; + // Check if expression contains escape functions (e, htmlspecialchars, htmlentities) + // If escaped and no escape-breaking functions, it's safe + if (preg_match('/\b(e|htmlspecialchars|htmlentities)\s*\(/', $expression)) { + // Make sure no escape-breaking functions are used + $escapeBreakers = ['html_entity_decode', 'htmlspecialchars_decode', 'urldecode', 'rawurldecode']; + $hasEscapeBreaker = false; + foreach ($escapeBreakers as $breaker) { + if (stripos($expression, $breaker) !== false) { + $hasEscapeBreaker = true; + break; + } + } + if (!$hasEscapeBreaker) { + return true; + } + } + foreach ($safePatterns as $pattern) { if (preg_match($pattern, $expression)) { return true;