diff --git a/wcfsetup/install/files/lib/acp/action/UserQuickSearchAction.class.php b/wcfsetup/install/files/lib/acp/action/UserQuickSearchAction.class.php index 6ae51316e1f..32aad6143bf 100644 --- a/wcfsetup/install/files/lib/acp/action/UserQuickSearchAction.class.php +++ b/wcfsetup/install/files/lib/acp/action/UserQuickSearchAction.class.php @@ -11,6 +11,7 @@ use wcf\system\menu\acp\ACPMenu; use wcf\system\request\LinkHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Provides special search options. @@ -179,7 +180,9 @@ public function execute(): RedirectResponse } if (empty($this->matches)) { - throw new NamedUserException(WCF::getLanguage()->get('wcf.acp.user.search.error.noMatches')); + throw new NamedUserException( + HtmlString::fromSafeHtml(WCF::getLanguage()->get('wcf.acp.user.search.error.noMatches')) + ); } // store search result in database diff --git a/wcfsetup/install/files/lib/acp/form/LoginForm.class.php b/wcfsetup/install/files/lib/acp/form/LoginForm.class.php index 0a88e7f848b..b640f2af328 100755 --- a/wcfsetup/install/files/lib/acp/form/LoginForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/LoginForm.class.php @@ -25,6 +25,7 @@ use wcf\system\user\authentication\UserAuthenticationFactory; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; use wcf\util\UserUtil; @@ -106,7 +107,9 @@ public function readParameters() if (ENABLE_USER_AUTHENTICATION_FAILURE) { $failures = UserAuthenticationFailure::countIPFailures(UserUtil::getIpAddress()); if (USER_AUTHENTICATION_FAILURE_IP_BLOCK && $failures >= USER_AUTHENTICATION_FAILURE_IP_BLOCK) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked') + )); } if (USER_AUTHENTICATION_FAILURE_IP_CAPTCHA && $failures >= USER_AUTHENTICATION_FAILURE_IP_CAPTCHA) { $this->useCaptcha = true; diff --git a/wcfsetup/install/files/lib/acp/form/PaidSubscriptionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/PaidSubscriptionAddForm.class.php index 923123889be..cd66976f3f3 100644 --- a/wcfsetup/install/files/lib/acp/form/PaidSubscriptionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/PaidSubscriptionAddForm.class.php @@ -15,6 +15,7 @@ use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\ArrayUtil; +use wcf\util\HtmlString; /** * Shows the paid subscription add form. @@ -152,7 +153,9 @@ public function readParameters() ); if (!\count(PaymentMethodHandler::getInstance()->getPaymentMethods())) { - throw new NamedUserException(WCF::getLanguage()->get('wcf.acp.paidSubscription.error.noPaymentMethods')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->get('wcf.acp.paidSubscription.error.noPaymentMethods') + )); } // get available currencies diff --git a/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php index 81759e52777..774c601a7b5 100644 --- a/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php @@ -23,6 +23,7 @@ use wcf\system\WCFACP; use wcf\util\FileUtil; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; use wcf\util\UserUtil; @@ -104,7 +105,9 @@ public function readParameters() if (ENABLE_USER_AUTHENTICATION_FAILURE) { $failures = UserAuthenticationFailure::countIPFailures(UserUtil::getIpAddress()); if (USER_AUTHENTICATION_FAILURE_IP_BLOCK && $failures >= USER_AUTHENTICATION_FAILURE_IP_BLOCK) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked') + )); } } @@ -132,7 +135,9 @@ public function readParameters() )['count'] >= self::ALLOWED_ATTEMPTS_PER_1D_GLOBAL; if ($floodExceeded) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood') + )); } // read applications diff --git a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php index 2d40910b7db..5a05e32a036 100644 --- a/wcfsetup/install/files/lib/acp/page/LicensePage.class.php +++ b/wcfsetup/install/files/lib/acp/page/LicensePage.class.php @@ -18,6 +18,7 @@ use wcf\system\package\PackageUpdateDispatcher; use wcf\system\request\LinkHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Lists the licensed products and offers to install them. @@ -98,11 +99,13 @@ public function readData() throw $e; } - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.acp.license.error.parsingFailed', - [ - 'licenseData' => $licenseApi->readFromFile(), - ] + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable( + 'wcf.acp.license.error.parsingFailed', + [ + 'licenseData' => $licenseApi->readFromFile(), + ] + ) )); } @@ -431,7 +434,7 @@ static function ($packageUpdateServerID) use ($trustedServerIDs) { $statement->execute($conditions->getParameters()); $packageVersions = $statement->fetchMap('packageUpdateVersionID', 'packageVersion'); - return \array_map(fn ($id) => $packageVersions[$id], $availablePackages); + return \array_map(fn($id) => $packageVersions[$id], $availablePackages); } /** diff --git a/wcfsetup/install/files/lib/action/AbstractOauth2Action.class.php b/wcfsetup/install/files/lib/action/AbstractOauth2Action.class.php index c06351cf95e..fead01ebd89 100644 --- a/wcfsetup/install/files/lib/action/AbstractOauth2Action.class.php +++ b/wcfsetup/install/files/lib/action/AbstractOauth2Action.class.php @@ -16,6 +16,7 @@ use wcf\system\user\authentication\oauth\exception\StateValidationException; use wcf\system\user\authentication\oauth\User as OauthUser; use wcf\system\WCF; +use wcf\util\HtmlString; use wcf\util\JSON; /** @@ -199,7 +200,9 @@ protected function codeToAccessToken(string $code): array protected function handleError(string $error): ResponseInterface { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.login.error.' . $error)); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.login.error.' . $error) + )); } /** @@ -263,8 +266,10 @@ public function execute() } catch (NamedUserException | PermissionDeniedException $e) { throw $e; } catch (StateValidationException $e) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.3rdparty.login.error.stateValidation' + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable( + 'wcf.user.3rdparty.login.error.stateValidation' + ) )); } catch (\Exception $e) { $exceptionID = \wcf\functions\exception\logThrowable($e); @@ -274,11 +279,13 @@ public function execute() $type = 'httpError'; } - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.3rdparty.login.error.' . $type, - [ - 'exceptionID' => $exceptionID, - ] + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable( + 'wcf.user.3rdparty.login.error.' . $type, + [ + 'exceptionID' => $exceptionID, + ] + ) )); } } diff --git a/wcfsetup/install/files/lib/action/AbstractOauth2AuthAction.class.php b/wcfsetup/install/files/lib/action/AbstractOauth2AuthAction.class.php index 4cfb45c29fe..6649007b3d0 100644 --- a/wcfsetup/install/files/lib/action/AbstractOauth2AuthAction.class.php +++ b/wcfsetup/install/files/lib/action/AbstractOauth2AuthAction.class.php @@ -29,6 +29,7 @@ use wcf\system\user\authentication\oauth\Success as OAuth2Success; use wcf\system\user\authentication\oauth\User as OauthUser; use wcf\system\WCF; +use wcf\util\HtmlString; use wcf\util\JSON; /** @@ -73,11 +74,11 @@ public function handle(ServerRequestInterface $request): ResponseInterface } catch (NamedUserException $e) { throw $e; } catch (StateValidationException $e) { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->getDynamicVariable( 'wcf.user.3rdparty.login.error.stateValidation' ) - ); + )); } catch (\Exception $e) { $exceptionID = \wcf\functions\exception\logThrowable($e); @@ -86,14 +87,14 @@ public function handle(ServerRequestInterface $request): ResponseInterface $type = 'httpError'; } - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->getDynamicVariable( 'wcf.user.3rdparty.login.error.' . $type, [ 'exceptionID' => $exceptionID, ] ) - ); + )); } } @@ -266,7 +267,7 @@ protected function processUser(OauthUser $oauthUser): ResponseInterface // This account belongs to an existing user, but we are already logged in. // This can't be handled. - throw new NamedUserException($this->getInUseErrorMessage()); + throw new NamedUserException(HtmlString::fromSafeHtml($this->getInUseErrorMessage())); } else { // This account belongs to an existing user, we are not logged in. // Perform the login. @@ -347,9 +348,9 @@ protected function redirectToRegistration(OauthUser $oauthUser): ResponseInterfa protected function handleError(OAuth2Failure $oauth2Failure): ResponseInterface { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.login.error.' . $oauth2Failure->error) - ); + )); } /** diff --git a/wcfsetup/install/files/lib/data/moderation/queue/ModerationQueueReportAction.class.php b/wcfsetup/install/files/lib/data/moderation/queue/ModerationQueueReportAction.class.php index 8a5811464f4..2a886f5961d 100644 --- a/wcfsetup/install/files/lib/data/moderation/queue/ModerationQueueReportAction.class.php +++ b/wcfsetup/install/files/lib/data/moderation/queue/ModerationQueueReportAction.class.php @@ -8,6 +8,7 @@ use wcf\system\flood\FloodControl; use wcf\system\moderation\queue\ModerationQueueReportManager; use wcf\system\WCF; +use wcf\util\HtmlString; use wcf\util\StringUtil; /** @@ -198,7 +199,9 @@ public function validateReport() new \DateInterval('PT10M') ); if ($requests['count'] >= self::ALLOWED_REPORTS_PER_10M) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood') + )); } $this->validatePrepareReport(); diff --git a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php index 60342d96edd..7164c578a41 100644 --- a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php +++ b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php @@ -18,6 +18,7 @@ use wcf\system\package\PackageUpdateUnauthorizedException; use wcf\system\request\LinkHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Executes package update-related actions. @@ -598,7 +599,9 @@ public function validateSearchForUpdates() $this->readBoolean('ignoreCache', true); if (ENABLE_BENCHMARK) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.acp.package.searchForUpdates.benchmark')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.acp.package.searchForUpdates.benchmark') + )); } } @@ -728,7 +731,9 @@ public function validateRefreshDatabase() $this->readBoolean('ignoreCache', true); if (ENABLE_BENCHMARK) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.acp.package.searchForUpdates.benchmark')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.acp.package.searchForUpdates.benchmark') + )); } } diff --git a/wcfsetup/install/files/lib/data/search/SearchAction.class.php b/wcfsetup/install/files/lib/data/search/SearchAction.class.php index 68123b4fd1b..bb67937220b 100644 --- a/wcfsetup/install/files/lib/data/search/SearchAction.class.php +++ b/wcfsetup/install/files/lib/data/search/SearchAction.class.php @@ -11,6 +11,7 @@ use wcf\system\search\SearchResultHandler; use wcf\system\search\SearchResultTextParser; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Executes search-related actions. @@ -84,7 +85,9 @@ public function validateSearch(): void $requestsPer24h['count'] >= self::ALLOWED_REQUESTS_PER_24H || $requestsPer60s['count'] >= self::ALLOWED_REQUESTS_PER_60S ) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood') + )); } } diff --git a/wcfsetup/install/files/lib/form/ContactForm.class.php b/wcfsetup/install/files/lib/form/ContactForm.class.php index 67209cf96ea..65ad51c2177 100644 --- a/wcfsetup/install/files/lib/form/ContactForm.class.php +++ b/wcfsetup/install/files/lib/form/ContactForm.class.php @@ -21,6 +21,7 @@ use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\UserUtil; /** @@ -77,7 +78,9 @@ public function validate() new \DateInterval('PT10M') ); if ($requests['count'] >= self::ALLOWED_MAILS_PER_10M) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.flood') + )); } parent::validate(); diff --git a/wcfsetup/install/files/lib/form/EmailActivationForm.class.php b/wcfsetup/install/files/lib/form/EmailActivationForm.class.php index efbc7336d50..a12b92ba806 100644 --- a/wcfsetup/install/files/lib/form/EmailActivationForm.class.php +++ b/wcfsetup/install/files/lib/form/EmailActivationForm.class.php @@ -13,6 +13,7 @@ use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; /** @@ -76,16 +77,16 @@ private function validateUserID(IntegerFormField $formField): void } if ($this->user->reactivationCode == 0) { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->get('wcf.user.registerActivation.error.userAlreadyEnabled') - ); + )); } // Check whether the new email isn't unique anymore. if (User::getUserByEmail($this->user->newEmail)->userID) { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->get('wcf.user.email.error.notUnique') - ); + )); } } diff --git a/wcfsetup/install/files/lib/form/LostPasswordForm.class.php b/wcfsetup/install/files/lib/form/LostPasswordForm.class.php index 6c488b0a353..fd40d21a624 100644 --- a/wcfsetup/install/files/lib/form/LostPasswordForm.class.php +++ b/wcfsetup/install/files/lib/form/LostPasswordForm.class.php @@ -21,6 +21,7 @@ use wcf\system\user\authentication\configuration\UserAuthenticationConfigurationFactory; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\UserUtil; /** @@ -119,7 +120,9 @@ public function validate() new \DateInterval('PT24H') ); if ($requests['count'] >= self::ALLOWED_RESETS_PER_24H) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.lostPassword.error.flood')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.lostPassword.error.flood') + )); } parent::validate(); @@ -152,10 +155,10 @@ public function save() // check whether a lost password request was sent in the last 24 hours if ($this->user->lastLostPasswordRequestTime && TIME_NOW - 86400 < $this->user->lastLostPasswordRequestTime) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( + throw new NamedUserException(HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable( 'wcf.user.lostPassword.error.tooManyRequests', ['hours' => \ceil(($this->user->lastLostPasswordRequestTime - (TIME_NOW - 86400)) / 3600)] - )); + ))); } // generate a new lost password key diff --git a/wcfsetup/install/files/lib/form/MultifactorAuthenticationForm.class.php b/wcfsetup/install/files/lib/form/MultifactorAuthenticationForm.class.php index 2495d8c2421..a8ea15a1d0e 100644 --- a/wcfsetup/install/files/lib/form/MultifactorAuthenticationForm.class.php +++ b/wcfsetup/install/files/lib/form/MultifactorAuthenticationForm.class.php @@ -15,6 +15,7 @@ use wcf\system\user\multifactor\Setup; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; /** * Represents the multi-factor authentication form. @@ -73,20 +74,20 @@ public function readParameters() $this->user = WCF::getSession()->getPendingUserChange(); if (!$this->user) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( + throw new NamedUserException(HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable( 'wcf.user.security.multifactor.authentication.noPendingUserChange' - )); + ))); } $this->setups = Setup::getAllForUser($this->user); if (empty($this->setups)) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( + throw new NamedUserException(HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable( 'wcf.user.security.multifactor.authentication.noSetup', [ 'user' => $this->user, ] - )); + ))); } \uasort($this->setups, static function (Setup $a, Setup $b) { diff --git a/wcfsetup/install/files/lib/form/MultifactorManageForm.class.php b/wcfsetup/install/files/lib/form/MultifactorManageForm.class.php index 36cf853c390..9cd0dda4182 100644 --- a/wcfsetup/install/files/lib/form/MultifactorManageForm.class.php +++ b/wcfsetup/install/files/lib/form/MultifactorManageForm.class.php @@ -21,6 +21,7 @@ use wcf\system\user\multifactor\IMultifactorMethod; use wcf\system\user\multifactor\Setup; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Represents the multi-factor setup form. @@ -142,7 +143,9 @@ public function save() } catch (DatabaseException $e) { WCF::getDB()->rollBackTransaction(); - throw new NamedUserException('wcf.user.security.multifactor.error.setupAllocationFailed'); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.security.multifactor.error.setupAllocationFailed') + )); } } diff --git a/wcfsetup/install/files/lib/form/NewPasswordForm.class.php b/wcfsetup/install/files/lib/form/NewPasswordForm.class.php index 13fbee92a55..904ebe7354a 100644 --- a/wcfsetup/install/files/lib/form/NewPasswordForm.class.php +++ b/wcfsetup/install/files/lib/form/NewPasswordForm.class.php @@ -16,6 +16,7 @@ use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\JSON; use wcf\util\StringUtil; use wcf\util\UserRegistrationUtil; @@ -189,6 +190,8 @@ private function forwardToIndexPage(): void private function throwInvalidLinkException(): void { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.error.invalidLink')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.error.invalidLink') + )); } } diff --git a/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php b/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php index 2837fb455c9..1beef531cd1 100644 --- a/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php +++ b/wcfsetup/install/files/lib/form/RegisterActivationForm.class.php @@ -15,6 +15,7 @@ use wcf\system\user\command\CreateRegistrationNotification; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; /** @@ -81,9 +82,9 @@ private function validateUsername(TextFormField $formField): void } if ($this->user->isEmailConfirmed()) { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->get('wcf.user.registerActivation.error.userAlreadyEnabled') - ); + )); } if (!empty($this->user->getBlacklistMatches())) { diff --git a/wcfsetup/install/files/lib/form/RegisterForm.class.php b/wcfsetup/install/files/lib/form/RegisterForm.class.php index 4d03da9e82d..3790d722d79 100644 --- a/wcfsetup/install/files/lib/form/RegisterForm.class.php +++ b/wcfsetup/install/files/lib/form/RegisterForm.class.php @@ -29,6 +29,7 @@ use wcf\system\user\group\assignment\UserGroupAssignmentHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\JSON; use wcf\util\StringUtil; use wcf\util\UserRegistrationUtil; @@ -132,7 +133,9 @@ public function readParameters() // registration disabled if (!UserAuthenticationConfigurationFactory::getInstance()->getConfigration()->canRegister) { - throw new NamedUserException(WCF::getLanguage()->get('wcf.user.register.error.disabled')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->get('wcf.user.register.error.disabled') + )); } if (WCF::getSession()->getVar('__3rdPartyProvider')) { @@ -218,9 +221,9 @@ public function validate() $this->spamCheckEvent = new RegistrationSpamChecking($this->username, $this->email, UserUtil::getIpAddress()); EventHandler::getInstance()->fire($this->spamCheckEvent); if ($this->spamCheckEvent->hasMatches() && \REGISTER_ANTISPAM_ACTION === 'block') { - throw new NamedUserException( + throw new NamedUserException(HtmlString::fromSafeHtml( WCF::getLanguage()->getDynamicVariable('wcf.user.register.error.blacklistMatches') - ); + )); } if (REGISTER_ENABLE_DISCLAIMER && !$this->termsConfirmed) { diff --git a/wcfsetup/install/files/lib/form/SearchForm.class.php b/wcfsetup/install/files/lib/form/SearchForm.class.php index cd384cc7193..9d3d62363dd 100644 --- a/wcfsetup/install/files/lib/form/SearchForm.class.php +++ b/wcfsetup/install/files/lib/form/SearchForm.class.php @@ -16,6 +16,7 @@ use wcf\system\search\SearchEngine; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; /** @@ -344,11 +345,15 @@ public function throwNoMatchesException() @\header('HTTP/1.1 404 Not Found'); if (empty($this->query)) { - throw new NamedUserException(WCF::getLanguage()->get('wcf.search.error.user.noMatches')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->get('wcf.search.error.user.noMatches') + )); } else { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.search.error.noMatches', - ['query' => $this->query] + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable( + 'wcf.search.error.noMatches', + ['query' => $this->query] + ) )); } } diff --git a/wcfsetup/install/files/lib/http/error/ErrorDetail.class.php b/wcfsetup/install/files/lib/http/error/ErrorDetail.class.php index 07508317072..d7c640efc03 100644 --- a/wcfsetup/install/files/lib/http/error/ErrorDetail.class.php +++ b/wcfsetup/install/files/lib/http/error/ErrorDetail.class.php @@ -3,6 +3,7 @@ namespace wcf\http\error; use Psr\Http\Message\ServerRequestInterface; +use wcf\util\HtmlString; /** * Stores additional metadata for response generation for erroneous requests. @@ -17,12 +18,11 @@ final class ErrorDetail private const ATTRIBUTE = self::class; private function __construct( - private ?string $message = null, + private HtmlString|string|null $message = null, private ?\Throwable $throwable = null, - ) { - } + ) {} - public function getMessage(): ?string + public function getMessage(): HtmlString|string|null { return $this->message; } @@ -44,7 +44,7 @@ public static function fromThrowable(\Throwable $e): self /** * Creates a new ErrorDetail object with the given message. */ - public static function fromMessage(string $message): self + public static function fromMessage(HtmlString|string $message): self { return self::fromMessageWithThrowable( $message, @@ -56,7 +56,7 @@ public static function fromMessage(string $message): self * Creates a new ErrorDetail object with the given message and the * Throwable as context. */ - public static function fromMessageWithThrowable(string $message, \Throwable $e): self + public static function fromMessageWithThrowable(HtmlString|string $message, \Throwable $e): self { return new self( $message, diff --git a/wcfsetup/install/files/lib/http/error/HtmlErrorRenderer.class.php b/wcfsetup/install/files/lib/http/error/HtmlErrorRenderer.class.php index 5c61e76713f..3a0f348c957 100644 --- a/wcfsetup/install/files/lib/http/error/HtmlErrorRenderer.class.php +++ b/wcfsetup/install/files/lib/http/error/HtmlErrorRenderer.class.php @@ -5,6 +5,7 @@ use Psr\Http\Message\StreamInterface; use wcf\system\WCF; use wcf\util\HeaderUtil; +use wcf\util\HtmlString; use wcf\util\StringUtil; /** @@ -19,13 +20,13 @@ final class HtmlErrorRenderer { public function render( string $title, - string $message, + HtmlString|string $message, ?\Throwable $exception = null, bool $showLogin = false ): StreamInterface { return $this->renderHtmlMessage( $title, - StringUtil::encodeHTML($message), + $message instanceof HtmlString ? $message->__toString() : StringUtil::encodeHTML($message), $exception, $showLogin ); diff --git a/wcfsetup/install/files/lib/http/error/NotFoundHandler.class.php b/wcfsetup/install/files/lib/http/error/NotFoundHandler.class.php index c1df968e012..67e5f2af08c 100644 --- a/wcfsetup/install/files/lib/http/error/NotFoundHandler.class.php +++ b/wcfsetup/install/files/lib/http/error/NotFoundHandler.class.php @@ -12,6 +12,7 @@ use wcf\system\request\RequestHandler; use wcf\system\session\SessionHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Returns a "Not Found" response. @@ -28,7 +29,9 @@ final class NotFoundHandler implements RequestHandlerInterface public function handle(ServerRequestInterface $request): ResponseInterface { $errorDetail = ErrorDetail::fromRequest($request); - $message = $errorDetail?->getMessage() ?? WCF::getLanguage()->getDynamicVariable('wcf.page.error.illegalLink'); + $message = $errorDetail?->getMessage() ?? HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.illegalLink') + ); if (!RequestHandler::getInstance()->isACPRequest()) { BoxHandler::disablePageLayout(); diff --git a/wcfsetup/install/files/lib/http/error/OperationNotPermittedHandler.class.php b/wcfsetup/install/files/lib/http/error/OperationNotPermittedHandler.class.php index e979b54e561..471e3024048 100644 --- a/wcfsetup/install/files/lib/http/error/OperationNotPermittedHandler.class.php +++ b/wcfsetup/install/files/lib/http/error/OperationNotPermittedHandler.class.php @@ -13,6 +13,7 @@ use wcf\system\request\RequestHandler; use wcf\system\session\SessionHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Returns an "Operation Not Permitted" response. @@ -29,7 +30,9 @@ final class OperationNotPermittedHandler implements RequestHandlerInterface public function handle(ServerRequestInterface $request): ResponseInterface { $errorDetail = ErrorDetail::fromRequest($request); - $message = $errorDetail?->getMessage() ?? WCF::getLanguage()->getDynamicVariable('wcf.global.error.title'); + $message = $errorDetail?->getMessage() ?? HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.global.error.title') + ); if (!RequestHandler::getInstance()->isACPRequest()) { BoxHandler::disablePageLayout(); diff --git a/wcfsetup/install/files/lib/http/error/PermissionDeniedHandler.class.php b/wcfsetup/install/files/lib/http/error/PermissionDeniedHandler.class.php index b30a4ce4878..bdec922f28d 100644 --- a/wcfsetup/install/files/lib/http/error/PermissionDeniedHandler.class.php +++ b/wcfsetup/install/files/lib/http/error/PermissionDeniedHandler.class.php @@ -13,6 +13,7 @@ use wcf\system\request\RequestHandler; use wcf\system\session\SessionHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Returns a "Permission Denied" response. @@ -29,7 +30,9 @@ final class PermissionDeniedHandler implements RequestHandlerInterface public function handle(ServerRequestInterface $request): ResponseInterface { $errorDetail = ErrorDetail::fromRequest($request); - $message = $errorDetail?->getMessage() ?? WCF::getLanguage()->getDynamicVariable('wcf.page.error.permissionDenied'); + $message = $errorDetail?->getMessage() ?? HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.page.error.permissionDenied') + ); if (!RequestHandler::getInstance()->isACPRequest()) { BoxHandler::disablePageLayout(); diff --git a/wcfsetup/install/files/lib/http/error/XsrfValidationFailedHandler.class.php b/wcfsetup/install/files/lib/http/error/XsrfValidationFailedHandler.class.php index 0049cdb7ec0..dc064ca55c8 100644 --- a/wcfsetup/install/files/lib/http/error/XsrfValidationFailedHandler.class.php +++ b/wcfsetup/install/files/lib/http/error/XsrfValidationFailedHandler.class.php @@ -12,6 +12,7 @@ use wcf\system\request\RequestHandler; use wcf\system\session\SessionHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Returns a "XSRF validation failed" response. @@ -28,7 +29,9 @@ final class XsrfValidationFailedHandler implements RequestHandlerInterface public function handle(ServerRequestInterface $request): ResponseInterface { $errorDetail = ErrorDetail::fromRequest($request); - $message = WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.sessionExpired'); + $message = HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.sessionExpired') + ); if (!RequestHandler::getInstance()->isACPRequest()) { BoxHandler::disablePageLayout(); diff --git a/wcfsetup/install/files/lib/http/middleware/CheckUserBan.class.php b/wcfsetup/install/files/lib/http/middleware/CheckUserBan.class.php index b5c18ec0382..4bdc0cbad10 100644 --- a/wcfsetup/install/files/lib/http/middleware/CheckUserBan.class.php +++ b/wcfsetup/install/files/lib/http/middleware/CheckUserBan.class.php @@ -11,6 +11,7 @@ use wcf\http\error\PermissionDeniedHandler; use wcf\http\Helper; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Checks whether the user is banned and deletes their sessions. @@ -37,8 +38,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } return (new PermissionDeniedHandler())->handle( - ErrorDetail::fromMessage(WCF::getLanguage()->getDynamicVariable('wcf.user.error.isBanned')) - ->attachToRequest($request) + ErrorDetail::fromMessage( + HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable('wcf.user.error.isBanned')) + )->attachToRequest($request) ); } diff --git a/wcfsetup/install/files/lib/http/middleware/HandleExceptions.class.php b/wcfsetup/install/files/lib/http/middleware/HandleExceptions.class.php index c5f8a342597..67bb579ecb0 100644 --- a/wcfsetup/install/files/lib/http/middleware/HandleExceptions.class.php +++ b/wcfsetup/install/files/lib/http/middleware/HandleExceptions.class.php @@ -47,7 +47,13 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface throw new \LogicException('Unreachable'); } - return $handler->handle(ErrorDetail::fromThrowable($e)->attachToRequest($request)); + if ($e instanceof NamedUserException) { + $detail = ErrorDetail::fromMessageWithThrowable($e->getHtmlString() ?: $e->getMessage(), $e); + } else { + $detail = ErrorDetail::fromThrowable($e); + } + + return $handler->handle($detail->attachToRequest($request)); } } } diff --git a/wcfsetup/install/files/lib/page/DisclaimerPage.class.php b/wcfsetup/install/files/lib/page/DisclaimerPage.class.php index 858514dcc4d..02d86dec282 100644 --- a/wcfsetup/install/files/lib/page/DisclaimerPage.class.php +++ b/wcfsetup/install/files/lib/page/DisclaimerPage.class.php @@ -6,6 +6,7 @@ use wcf\system\exception\NamedUserException; use wcf\system\user\authentication\configuration\UserAuthenticationConfigurationFactory; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Shows the disclaimer. @@ -34,7 +35,9 @@ public function readParameters() !WCF::getUser()->userID && !UserAuthenticationConfigurationFactory::getInstance()->getConfigration()->canRegister ) { - throw new NamedUserException(WCF::getLanguage()->get('wcf.user.register.error.disabled')); + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->get('wcf.user.register.error.disabled') + )); } } } diff --git a/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php b/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php index 26ebd425b23..45e529cda0d 100644 --- a/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php +++ b/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php @@ -23,6 +23,7 @@ use wcf\system\user\activity\event\UserActivityEventHandler; use wcf\system\user\notification\UserNotificationHandler; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Provides methods for comment object handling. @@ -234,10 +235,10 @@ public static function enforceFloodControl() $lastTime = FloodControl::getInstance()->getLastTime('com.woltlab.wcf.comment'); if ($lastTime !== null && $lastTime > TIME_NOW - $floodControlTime) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( + throw new NamedUserException(HtmlString::fromSafeHtml(WCF::getLanguage()->getDynamicVariable( 'wcf.comment.error.floodControl', ['lastCommentTime' => $lastTime] - )); + ))); } } diff --git a/wcfsetup/install/files/lib/system/exception/NamedUserException.class.php b/wcfsetup/install/files/lib/system/exception/NamedUserException.class.php index 8a8f1dbf3be..c97ebf44920 100644 --- a/wcfsetup/install/files/lib/system/exception/NamedUserException.class.php +++ b/wcfsetup/install/files/lib/system/exception/NamedUserException.class.php @@ -6,6 +6,7 @@ use wcf\system\session\SessionHandler; use wcf\system\WCF; use wcf\system\WCFACP; +use wcf\util\HtmlString; /** * NamedUserException shows a (well) styled page with the given error message. @@ -16,6 +17,26 @@ */ class NamedUserException extends UserException { + public function __construct( + protected readonly HtmlString|string $htmlString = "", + int $code = 0, + ?\Throwable $previous = null + ) { + parent::__construct($htmlString, $code, $previous); + } + + /** + * @since 6.2 + */ + public function getHtmlString(): ?HtmlString + { + if ($this->htmlString instanceof HtmlString) { + return $this->htmlString; + } + + return null; + } + /** * Shows a styled page with the given error message. */ diff --git a/wcfsetup/install/files/lib/system/user/multifactor/TMultifactorRequirementEnforcer.class.php b/wcfsetup/install/files/lib/system/user/multifactor/TMultifactorRequirementEnforcer.class.php index ba2fe960c80..f9507fe4448 100644 --- a/wcfsetup/install/files/lib/system/user/multifactor/TMultifactorRequirementEnforcer.class.php +++ b/wcfsetup/install/files/lib/system/user/multifactor/TMultifactorRequirementEnforcer.class.php @@ -4,6 +4,7 @@ use wcf\system\exception\NamedUserException; use wcf\system\WCF; +use wcf\util\HtmlString; /** * Provides a method enforce the multi-factor requirement. @@ -28,8 +29,10 @@ private function enforceMultifactorAuthentication(): void WCF::getUser()->requiresMultifactor() && !WCF::getUser()->multifactorActive ) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.security.requiresMultifactor' + throw new NamedUserException(HtmlString::fromSafeHtml( + WCF::getLanguage()->getDynamicVariable( + 'wcf.user.security.requiresMultifactor' + ) )); } } diff --git a/wcfsetup/install/files/lib/util/HtmlString.class.php b/wcfsetup/install/files/lib/util/HtmlString.class.php new file mode 100644 index 00000000000..f19d54dbf98 --- /dev/null +++ b/wcfsetup/install/files/lib/util/HtmlString.class.php @@ -0,0 +1,35 @@ + + * @since 6.2 + */ +final class HtmlString implements \Stringable +{ + private function __construct( + private readonly string $value + ) {} + + public static function fromText(string $value): self + { + return new self( + StringUtil::encodeHTML($value), + ); + } + + public static function fromSafeHtml(string $value): self + { + return new self($value); + } + + public function __toString(): string + { + return $this->value; + } +}