Skip to content

Commit a196ee3

Browse files
committed
Merge branch 'hotfix/21.0.9' into develop
2 parents 8b80018 + 424b1d4 commit a196ee3

File tree

5 files changed

+630
-641
lines changed

5 files changed

+630
-641
lines changed
Lines changed: 167 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,173 +1,168 @@
1-
<?php declare( strict_types=1 );
2-
3-
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4-
5-
use FernleafSystems\Utilities\Logic\ExecOnce;
6-
use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
7-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
8-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator\ScoreLogic;
9-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\NotBot\NotBotHandler;
10-
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
11-
use FernleafSystems\Wordpress\Services\Services;
12-
13-
class BotSignalsController {
14-
15-
use ExecOnce;
16-
use PluginControllerConsumer;
17-
use PluginCronsConsumer;
18-
19-
private BotEventListener $eventListener;
20-
21-
private array $isBots = [];
22-
23-
protected function run() {
24-
25-
if ( self::con()->this_req->ip_is_public || Services::Request()->query( 'force_notbot' ) ) {
26-
$this->getEventListener()->execute();
27-
add_action( 'init', fn() => \array_map( fn( $c ) => ( new $c() )->execute(), $this->enumerateBotTrackers() ) );
28-
self::con()->comps->not_bot->execute();
29-
$this->registerFrontPageLoad();
30-
$this->registerLoginPageLoad();
31-
}
32-
33-
$this->setupCronHooks();
34-
}
35-
36-
public function runDailyCron() {
37-
( new ScoreLogic() )->getScoringLogic( true );
38-
}
39-
40-
public function isBot( string $IP = '', bool $allowEventFire = true, bool $forceCheck = false ) :bool {
41-
42-
if ( !isset( $this->isBots[ $IP ] ) || $forceCheck ) {
43-
$con = self::con();
44-
45-
$this->isBots[ $IP ] = false;
46-
47-
if ( !$con->comps->opts_lookup->enabledAntiBotEngine() ) {
48-
$con->fireEvent( 'ade_check_option_disabled' );
49-
}
50-
else {
51-
$botScoreMinimum = $con->comps->opts_lookup->getAntiBotMinScore();
52-
if ( $botScoreMinimum > 0 ) {
53-
54-
$score = ( new Calculator\CalculateVisitorBotScores() )
55-
->setIP( empty( $IP ) ? self::con()->this_req->ip : $IP )
56-
->probability();
57-
58-
$this->isBots[ $IP ] = $score < $botScoreMinimum;
59-
60-
if ( $allowEventFire ) {
61-
$con->fireEvent(
62-
'antibot_'.( $this->isBots[ $IP ] ? 'fail' : 'pass' ),
63-
[
64-
'audit_params' => [
65-
'score' => $score,
66-
'minimum' => $botScoreMinimum,
67-
]
68-
]
69-
);
70-
}
71-
}
72-
}
73-
}
74-
75-
return $this->isBots[ $IP ] ?? false;
76-
}
77-
78-
public function getAllowableExt404s() :array {
79-
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_ext_404s' ] ?? [];
80-
return \array_unique( \array_filter(
81-
apply_filters( 'shield/bot_signals_allowable_extensions_404s', $def ),
82-
fn( $ext ) => !empty( $ext ) && \is_string( $ext ) && \preg_match( '#^[a-z\d]+$#i', $ext )
83-
) );
84-
}
85-
86-
public function getAllowablePaths404s() :array {
87-
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_paths_404s' ] ?? [];
88-
return \array_unique( \array_filter(
89-
apply_filters( 'shield/bot_signals_allowable_paths_404s', $def ),
90-
function ( $ext ) {
91-
return !empty( $ext ) && \is_string( $ext );
92-
}
93-
) );
94-
}
95-
96-
public function getAllowableScripts() :array {
97-
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_invalid_scripts' ] ?? [];
98-
return \array_unique( \array_filter(
99-
apply_filters( 'shield/bot_signals_allowable_invalid_scripts', $def ),
100-
function ( $script ) {
101-
return !empty( $script ) && \is_string( $script ) && \strpos( $script, '.php' );
102-
}
103-
) );
104-
}
105-
106-
public function getEventListener() :BotEventListener {
107-
return $this->eventListener ??= new BotEventListener();
108-
}
109-
110-
/**
111-
* @return string[]
112-
*/
113-
private function enumerateBotTrackers() :array {
114-
$con = self::con();
115-
116-
$trackers = [
117-
BotTrack\TrackCommentSpam::class
118-
];
119-
120-
if ( !Services::WpUsers()->isUserLoggedIn() ) {
121-
if ( !$con->this_req->request_bypasses_all_restrictions ) {
122-
if ( !$con->opts->optIs( 'track_loginfailed', 'disabled' ) ) {
123-
$trackers[] = BotTrack\TrackLoginFailed::class;
124-
}
125-
if ( !$con->opts->optIs( 'track_logininvalid', 'disabled' ) ) {
126-
$trackers[] = BotTrack\TrackLoginInvalid::class;
127-
}
128-
}
129-
}
130-
131-
if ( !$con->opts->optIs( 'track_linkcheese', 'disabled' ) ) {
132-
$trackers[] = BotTrack\TrackLinkCheese::class;
133-
}
134-
135-
return $trackers;
136-
}
137-
138-
private function registerFrontPageLoad() {
139-
add_action( self::con()->prefix( 'pre_plugin_shutdown' ), function () {
140-
$req = Services::Request();
141-
if ( $req->isGet() && did_action( 'wp' ) && ( is_page() || is_single() || is_front_page() || is_home() ) ) {
142-
try {
143-
$record = ( new BotSignalsRecord() )
144-
->setIP( self::con()->this_req->ip )
145-
->retrieve();
146-
if ( $req->ts() - $record->frontpage_at > MINUTE_IN_SECONDS*30 ) {
147-
$this->getEventListener()->fireEventForIP( self::con()->this_req->ip, 'frontpage_load' );
148-
}
149-
}
150-
catch ( \Exception $e ) {
151-
}
152-
}
153-
} );
154-
}
155-
156-
private function registerLoginPageLoad() {
157-
add_action( 'login_footer', function () {
158-
$req = Services::Request();
159-
if ( $req->isGet() ) {
160-
try {
161-
$record = ( new BotSignalsRecord() )
162-
->setIP( self::con()->this_req->ip )
163-
->retrieve();
164-
if ( $req->ts() - $record->loginpage_at > MINUTE_IN_SECONDS*10 ) {
165-
$this->getEventListener()->fireEventForIP( self::con()->this_req->ip, 'loginpage_load' );
166-
}
167-
}
168-
catch ( \Exception $e ) {
169-
}
170-
}
171-
} );
172-
}
1+
<?php declare( strict_types=1 );
2+
3+
namespace FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots;
4+
5+
use FernleafSystems\Utilities\Logic\ExecOnce;
6+
use FernleafSystems\Wordpress\Plugin\Shield\Crons\PluginCronsConsumer;
7+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\BotTrack;
8+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\IPs\Lib\Bots\Calculator\ScoreLogic;
9+
use FernleafSystems\Wordpress\Plugin\Shield\Modules\PluginControllerConsumer;
10+
use FernleafSystems\Wordpress\Services\Services;
11+
12+
class BotSignalsController {
13+
14+
use ExecOnce;
15+
use PluginControllerConsumer;
16+
use PluginCronsConsumer;
17+
18+
private BotEventListener $eventListener;
19+
20+
private array $isBots = [];
21+
22+
protected function run() {
23+
24+
if ( self::con()->this_req->ip_is_public || Services::Request()->query( 'force_notbot' ) ) {
25+
$this->getEventListener()->execute();
26+
add_action( 'init', fn() => \array_map( fn( $c ) => ( new $c() )->execute(), $this->enumerateBotTrackers() ) );
27+
self::con()->comps->not_bot->execute();
28+
$this->registerFrontPageLoad();
29+
$this->registerLoginPageLoad();
30+
}
31+
32+
$this->setupCronHooks();
33+
}
34+
35+
public function runDailyCron() {
36+
( new ScoreLogic() )->getScoringLogic( true );
37+
}
38+
39+
public function isBot( string $IP = '', bool $allowEventFire = true, bool $forceCheck = false ) :bool {
40+
41+
if ( !isset( $this->isBots[ $IP ] ) || $forceCheck ) {
42+
$con = self::con();
43+
44+
$this->isBots[ $IP ] = false;
45+
46+
if ( !$con->comps->opts_lookup->enabledAntiBotEngine() ) {
47+
$con->comps->events->fireEvent( 'ade_check_option_disabled' );
48+
}
49+
else {
50+
$botScoreMinimum = $con->comps->opts_lookup->getAntiBotMinScore();
51+
if ( $botScoreMinimum > 0 ) {
52+
53+
$score = ( new Calculator\CalculateVisitorBotScores() )
54+
->setIP( empty( $IP ) ? self::con()->this_req->ip : $IP )
55+
->probability();
56+
57+
$this->isBots[ $IP ] = $score < $botScoreMinimum;
58+
59+
if ( $allowEventFire ) {
60+
$con->comps->events->fireEvent(
61+
'antibot_'.( $this->isBots[ $IP ] ? 'fail' : 'pass' ),
62+
[
63+
'audit_params' => [
64+
'score' => $score,
65+
'minimum' => $botScoreMinimum,
66+
]
67+
]
68+
);
69+
}
70+
}
71+
}
72+
}
73+
74+
return $this->isBots[ $IP ] ?? false;
75+
}
76+
77+
public function getAllowableExt404s() :array {
78+
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_ext_404s' ] ?? [];
79+
return \array_unique( \array_filter(
80+
apply_filters( 'shield/bot_signals_allowable_extensions_404s', $def ),
81+
fn( $ext ) => !empty( $ext ) && \is_string( $ext ) && \preg_match( '#^[a-z\d]+$#i', $ext )
82+
) );
83+
}
84+
85+
public function getAllowablePaths404s() :array {
86+
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_paths_404s' ] ?? [];
87+
return \array_unique( \array_filter(
88+
apply_filters( 'shield/bot_signals_allowable_paths_404s', $def ),
89+
fn( $ext ) => !empty( $ext ) && \is_string( $ext )
90+
) );
91+
}
92+
93+
public function getAllowableScripts() :array {
94+
$def = self::con()->cfg->configuration->def( 'bot_signals' )[ 'allowable_invalid_scripts' ] ?? [];
95+
return \array_unique( \array_filter(
96+
apply_filters( 'shield/bot_signals_allowable_invalid_scripts', $def ),
97+
fn( $script ) => !empty( $script ) && \is_string( $script ) && \strpos( $script, '.php' )
98+
) );
99+
}
100+
101+
public function getEventListener() :BotEventListener {
102+
return $this->eventListener ??= new BotEventListener();
103+
}
104+
105+
/**
106+
* @return string[]
107+
*/
108+
private function enumerateBotTrackers() :array {
109+
$con = self::con();
110+
111+
$trackers = [
112+
BotTrack\TrackCommentSpam::class
113+
];
114+
115+
if ( !Services::WpUsers()->isUserLoggedIn() ) {
116+
if ( !$con->this_req->request_bypasses_all_restrictions ) {
117+
if ( !$con->opts->optIs( 'track_loginfailed', 'disabled' ) ) {
118+
$trackers[] = BotTrack\TrackLoginFailed::class;
119+
}
120+
if ( !$con->opts->optIs( 'track_logininvalid', 'disabled' ) ) {
121+
$trackers[] = BotTrack\TrackLoginInvalid::class;
122+
}
123+
}
124+
}
125+
126+
if ( !$con->opts->optIs( 'track_linkcheese', 'disabled' ) ) {
127+
$trackers[] = BotTrack\TrackLinkCheese::class;
128+
}
129+
130+
return $trackers;
131+
}
132+
133+
private function registerFrontPageLoad() {
134+
add_action( self::con()->prefix( 'pre_plugin_shutdown' ), function () {
135+
$req = Services::Request();
136+
if ( $req->isGet() && did_action( 'wp' ) && ( is_page() || is_single() || is_front_page() || is_home() ) ) {
137+
try {
138+
$record = ( new BotSignalsRecord() )
139+
->setIP( self::con()->this_req->ip )
140+
->retrieve();
141+
if ( $req->ts() - $record->frontpage_at > MINUTE_IN_SECONDS*30 ) {
142+
$this->getEventListener()->fireEventForIP( self::con()->this_req->ip, 'frontpage_load' );
143+
}
144+
}
145+
catch ( \Exception $e ) {
146+
}
147+
}
148+
} );
149+
}
150+
151+
private function registerLoginPageLoad() {
152+
add_action( 'login_footer', function () {
153+
$req = Services::Request();
154+
if ( $req->isGet() ) {
155+
try {
156+
$record = ( new BotSignalsRecord() )
157+
->setIP( self::con()->this_req->ip )
158+
->retrieve();
159+
if ( $req->ts() - $record->loginpage_at > MINUTE_IN_SECONDS*10 ) {
160+
$this->getEventListener()->fireEventForIP( self::con()->this_req->ip, 'loginpage_load' );
161+
}
162+
}
163+
catch ( \Exception $e ) {
164+
}
165+
}
166+
} );
167+
}
173168
}

0 commit comments

Comments
 (0)