Skip to content

Commit

Permalink
Merge pull request #5929 from WoltLab/spam-checking-event
Browse files Browse the repository at this point in the history
Use an event for the spam check during registration
  • Loading branch information
BurntimeX authored Jun 1, 2024
2 parents bca5000 + 9334f30 commit 7af70bc
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 56 deletions.
31 changes: 13 additions & 18 deletions com.woltlab.wcf/option.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,6 @@
<parent>security.general</parent>
<showorder>15</showorder>
</category>
<category name="security.blacklist">
<parent>security</parent>
</category>
<category name="security.blacklist.sfs">
<parent>security.blacklist</parent>
<showorder>1</showorder>
</category>
<category name="security.blacklist.custom">
<parent>security.blacklist</parent>
<showorder>2</showorder>
</category>
<category name="security.antispam">
<parent>security</parent>
</category>
Expand All @@ -186,6 +175,9 @@
<category name="security.antispam.recaptcha">
<parent>security.antispam</parent>
</category>
<category name="security.blacklist.sfs">
<parent>security.antispam</parent>
</category>
<category name="security.censorship">
<parent>security</parent>
</category>
Expand Down Expand Up @@ -660,13 +652,6 @@ moreThanOnce:wcf.acp.option.blacklist_sfs_enable.moreThanOnce
simpleMatch:wcf.acp.option.blacklist_sfs_enable.simpleMatch]]></selectoptions>
<defaultvalue>90percentile</defaultvalue>
</option>
<option name="blacklist_sfs_action">
<categoryname>security.blacklist.sfs</categoryname>
<optiontype>select</optiontype>
<selectoptions><![CDATA[disable:wcf.acp.option.blacklist_sfs_action.disable
block:wcf.acp.option.blacklist_sfs_action.block]]></selectoptions>
<defaultvalue>disable</defaultvalue>
</option>
<!-- /security.blacklist.stopforumspam -->
<!-- security.antispam.captcha -->
<option name="captcha_type">
Expand Down Expand Up @@ -1090,6 +1075,13 @@ XING</selectoptions>
2:wcf.acp.option.register_activation_method.byAdmin
3:wcf.acp.option.register_activation_method.byUserAndAdmin</selectoptions>
</option>
<option name="register_antispam_action">
<categoryname>user.register</categoryname>
<optiontype>select</optiontype>
<selectoptions><![CDATA[disable:wcf.acp.option.register_antispam_action.disable
block:wcf.acp.option.register_antispam_action.block]]></selectoptions>
<defaultvalue>disable</defaultvalue>
</option>
<option name="register_username_min_length">
<categoryname>user.register</categoryname>
<optiontype>integer</optiontype>
Expand Down Expand Up @@ -1613,5 +1605,8 @@ DESC:wcf.global.sortOrder.descending</selectoptions>
<option name="article_enable_visit_tracking"/>
<option name="enable_woltlab_news"/>
<option name="users_online_record_no_guests"/>
<option name="blacklist_sfs_action"/>
<category name="security.blacklist"/>
<category name="security.blacklist.custom"/>
</delete>
</data>
2 changes: 1 addition & 1 deletion constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
\define('REGISTER_USERNAME_MAX_LENGTH', 25);
\define('REGISTER_USERNAME_FORCE_ASCII', 2);
\define('REGISTER_MIN_USER_AGE', 0);
\define('REGISTER_ANTISPAM_ACTION', 0);
\define('GITHUB_PUBLIC_KEY', '');
\define('GITHUB_PRIVATE_KEY', '');
\define('TWITTER_PUBLIC_KEY', '');
Expand Down Expand Up @@ -214,7 +215,6 @@
\define('BLACKLIST_SFS_USERNAME', '90percentile');
\define('BLACKLIST_SFS_EMAIL_ADDRESS', 'moreThanOnce');
\define('BLACKLIST_SFS_IP_ADDRESS', '90percentile');
\define('BLACKLIST_SFS_ACTION', 'disable');
\define('ENABLE_ENTERPRISE_MODE', 0);
\define('MESSAGE_ENABLE_USER_CONSENT', 1);
\define('MODIFICATION_LOG_EXPIRATION', 0);
Expand Down
13 changes: 13 additions & 0 deletions wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ static function (\wcf\event\session\PreserveVariablesCollecting $event) {
\wcf\system\event\listener\UsernameValidatingCheckCharactersListener::class
);

$eventHandler->register(
\wcf\event\user\RegistrationSpamChecking::class,
\wcf\system\event\listener\RegistrationSpamCheckingSfsListener::class
);
$eventHandler->register(
\wcf\event\page\ContactFormSpamChecking::class,
\wcf\system\event\listener\ContactFormSpamCheckingSfsListener::class
);
$eventHandler->register(
\wcf\event\message\MessageSpamChecking::class,
\wcf\system\event\listener\MessageSpamCheckingSfsListener::class
);

$eventHandler->register(
\wcf\event\package\PackageListChanged::class,
static function () {
Expand Down
23 changes: 23 additions & 0 deletions wcfsetup/install/files/lib/data/comment/CommentAction.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
use wcf\data\object\type\ObjectType;
use wcf\data\object\type\ObjectTypeCache;
use wcf\data\user\User;
use wcf\event\message\MessageSpamChecking;
use wcf\system\bbcode\BBCodeHandler;
use wcf\system\captcha\CaptchaHandler;
use wcf\system\comment\CommentHandler;
use wcf\system\comment\manager\ICommentManager;
use wcf\system\event\EventHandler;
use wcf\system\exception\NamedUserException;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\SystemException;
Expand All @@ -36,6 +38,7 @@
use wcf\system\WCF;
use wcf\util\MessageUtil;
use wcf\util\UserRegistrationUtil;
use wcf\util\UserUtil;

/**
* Executes comment-related actions.
Expand Down Expand Up @@ -403,6 +406,16 @@ public function validateAddComment()
if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) {
throw new PermissionDeniedException();
}

$event = new MessageSpamChecking(
$this->parameters['htmlInputProcessor'],
WCF::getUser()->userID ? WCF::getUser() : null,
UserUtil::getIpAddress(),
);
EventHandler::getInstance()->fire($event);
if ($event->defaultPrevented()) {
throw new PermissionDeniedException();
}
}

/**
Expand Down Expand Up @@ -564,6 +577,16 @@ public function validateAddResponse()

$this->validateGetGuestDialog();
$this->validateMessage(true);

$event = new MessageSpamChecking(
$this->parameters['htmlInputProcessor'],
WCF::getUser()->userID ? WCF::getUser() : null,
UserUtil::getIpAddress(),
);
EventHandler::getInstance()->fire($event);
if ($event->defaultPrevented()) {
throw new PermissionDeniedException();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace wcf\event\message;

use wcf\data\user\User;
use wcf\event\IInterruptableEvent;
use wcf\event\TInterruptableEvent;
use wcf\system\html\input\HtmlInputProcessor;

/**
* Indicates that a new message by a user is currently validated. If this event is interrupted,
* the message is considered to be spam.
*
* @author Marcel Werk
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.1
*/
final class MessageSpamChecking implements IInterruptableEvent
{
use TInterruptableEvent;

public function __construct(
public readonly HtmlInputProcessor $processor,
public readonly ?User $user = null,
public readonly string $ipAddress = '',
public readonly string $subject = '',
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace wcf\event\page;

use wcf\event\IInterruptableEvent;
use wcf\event\TInterruptableEvent;

/**
* Indicates that a new contact form message is currently validated. If this event is interrupted,
* the message is considered to be spam.
*
* @author Marcel Werk
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.1
*/
final class ContactFormSpamChecking implements IInterruptableEvent
{
use TInterruptableEvent;

public function __construct(
public readonly string $email,
public readonly string $ipAddress,
public readonly array $messages,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace wcf\event\user;

use wcf\event\IPsr14Event;

/**
* Indicates that a registration by a new user is currently validated. If $matches is not empty,
* the registration is considered to be a spammer or an undesirable user.
*
* @author Marcel Werk
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.1
*/
final class RegistrationSpamChecking implements IPsr14Event
{
private array $matches = [];

public function __construct(
public readonly string $username,
public readonly string $email,
public readonly string $ipAddress
) {
}

public function hasMatches(): bool
{
return $this->matches !== [];
}

public function addMatch(string $key): void
{
$this->matches[$key] = $key;
}

public function getMatches(): array
{
return \array_values($this->matches);
}
}
38 changes: 29 additions & 9 deletions wcfsetup/install/files/lib/form/ContactForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace wcf\form;

use wcf\data\blacklist\entry\BlacklistEntry;
use wcf\data\contact\option\ContactOption;
use wcf\data\contact\option\ContactOptionAction;
use wcf\data\contact\recipient\ContactRecipientList;
use wcf\event\page\ContactFormSpamChecking;
use wcf\system\attachment\AttachmentHandler;
use wcf\system\email\Mailbox;
use wcf\system\event\EventHandler;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\UserInputException;
Expand Down Expand Up @@ -169,16 +171,34 @@ public function validate()
}
}

if (BLACKLIST_SFS_ENABLE) {
$matches = BlacklistEntry::getMatches(
'',
$this->email,
UserUtil::getIpAddress()
);
if ($matches !== [] && BLACKLIST_SFS_ACTION === 'block') {
throw new PermissionDeniedException();
$this->handleSpamCheck();
}

private function handleSpamCheck(): void
{
$messages = [];
foreach ($this->optionHandler->getOptions() as $option) {
$object = $option['object'];
\assert($object instanceof ContactOption);
if (!$object->isMessage || !$object->getOptionValue()) {
continue;
}

$messages[] = $object->getOptionValue();
if ($object->optionType === 'date' && !$object->getOptionValue()) {
continue;
}
}

$spamCheckEvent = new ContactFormSpamChecking(
$this->email,
UserUtil::getIpAddress(),
$messages,
);
EventHandler::getInstance()->fire($spamCheckEvent);
if ($spamCheckEvent->defaultPrevented()) {
throw new PermissionDeniedException();
}
}

/**
Expand Down
21 changes: 21 additions & 0 deletions wcfsetup/install/files/lib/form/MessageForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
use wcf\data\smiley\category\SmileyCategory;
use wcf\data\smiley\Smiley;
use wcf\data\smiley\SmileyCache;
use wcf\event\message\MessageSpamChecking;
use wcf\system\attachment\AttachmentHandler;
use wcf\system\bbcode\BBCodeHandler;
use wcf\system\event\EventHandler;
use wcf\system\exception\UserInputException;
use wcf\system\html\input\HtmlInputProcessor;
use wcf\system\html\upcast\HtmlUpcastProcessor;
Expand All @@ -16,6 +18,7 @@
use wcf\system\WCF;
use wcf\util\MessageUtil;
use wcf\util\StringUtil;
use wcf\util\UserUtil;

/**
* MessageForm is an abstract form implementation for a message with optional captcha support.
Expand Down Expand Up @@ -347,4 +350,22 @@ public function assignVariables()
'tmpHash' => $this->tmpHash,
]);
}

/**
* This method triggers the event for the spam check and returns the result.
*
* @since 6.1
*/
protected function messageIsProbablySpam(): bool
{
$event = new MessageSpamChecking(
$this->htmlInputProcessor,
WCF::getUser()->userID ? WCF::getUser() : null,
UserUtil::getIpAddress(),
$this->subject,
);
EventHandler::getInstance()->fire($event);

return $event->defaultPrevented();
}
}
Loading

0 comments on commit 7af70bc

Please sign in to comment.