PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.9.0
Defender Security – Malware Scanner, Login Security & Firewall v5.9.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / class-central.php

class-central.php in Defender Security – Malware Scanner, Login Security & Firewall 5.9.0, at src/class-central.php

317 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles all AJAX requests in this plugin.
4 *
5 * @package WP_Defender
6 */
7
8 namespace WP_Defender;
9
10 use Exception;
11 use Calotes\Helper\HTTP;
12 use WP_Defender\Traits\IO;
13 use Calotes\Component\Request;
14 use Calotes\Component\Response;
15 use WP_Defender\Traits\Permission;
16
17 /**
18 * Handles all AJAX requests in this plugin.
19 */
20 class Central extends Component {
21
22 use IO;
23 use Permission;
24
25 public const INTERNAL_LOG = 'internal.log';
26
27 /**
28 * This will hold the db data of each module, all data must be getting through this.
29 *
30 * @var array
31 */
32 protected $states = array();
33
34 /**
35 * This should be constructed only once.
36 */
37 public function __construct() {
38 $action = defender_base_action();
39 add_action( 'wp_ajax_' . $action, array( $this, 'routing' ) );
40 add_action( 'wp_ajax_nopriv_' . $action, array( $this, 'routing' ) );
41 }
42
43 /**
44 * This is a global ajax call, receive all the requests and dispatch to the right controller.
45 */
46 public function routing() {
47 // This is the intention, we will use it to find the data stored in DI.
48 $route = HTTP::get( 'route', false );
49 $nonce = HTTP::get( '_def_nonce', false );
50 if ( ! is_string( $route ) || '' === trim( $route ) || ! is_string( $nonce ) || '' === trim( $nonce ) ) {
51 exit;
52 }
53
54 $this->check_opcache();
55
56 $route = wp_unslash( $route );
57
58 if (
59 ! is_user_logged_in() &&
60 $this->is_private_access( $route )
61 ) {
62 $data = array(
63 'message' => esc_html__( 'Your session expired. Please login to continue.', 'defender-security' ),
64 'type_notice' => 'session_out',
65 );
66
67 if ( $this->is_redirect( $route ) ) {
68 $data['redirect'] = wp_login_url( wp_get_referer() );
69 }
70
71 wp_send_json_error( $data );
72 }
73
74 // Nonce is not valid.
75 if ( ! wp_verify_nonce( $nonce, $route ) ) {
76 wp_send_json_error(
77 array(
78 'message' => esc_html__( 'Invalid API request.', 'defender-security' ),
79 'type_notice' => 'invalid_request',
80 )
81 );
82 }
83
84 $key = sprintf( 'controller.%s', $route );
85
86 try {
87 $package = wd_di()->get( $key );
88 [ $class, $method, $is_private ] = $package;
89 if ( true === $is_private && ! $this->check_permission() ) {
90 wp_send_json_error(
91 array(
92 'message' => esc_html__( 'You shall not pass.', 'defender-security' ),
93 'type_notice' => 'not_allowed',
94 )
95 );
96 }
97 if ( true === $is_private ) {
98 if ( ! wp_next_scheduled( 'defender_hub_sync' ) ) {
99 // Sync with HUB on every request it made, but not on public call.
100 wp_schedule_single_event( time(), 'defender_hub_sync' );
101 }
102 }
103 $this->execute_intention( $class, $method );
104 } catch ( Exception $e ) {
105 $this->log( $e->getMessage(), self::INTERNAL_LOG );
106 }
107 }
108
109 /**
110 * Execute the method, return should be various base on the method.
111 *
112 * @param string $class_name Class name.
113 * @param string $method The function to call.
114 *
115 * @return Response|void
116 */
117 private function execute_intention( $class_name, $method ) {
118 $object = wd_di()->get( $class_name );
119 if ( is_object( $object ) ) {
120 $request = new Request();
121 // Because the method is getting params from $_REQUEST directly, we don't need to pass any args, just call.
122 // No use reflection method for performance, also this just a simple call.
123 // Manipulate the POST as raw data.
124 $_POST = $request->get_data();
125
126 // @phpstan-ignore-next-line
127 return $object->$method( $request );
128 } else {
129 $this->log( sprintf( 'class not found when executing: %s %s', $class_name, $method ), self::INTERNAL_LOG );
130 }
131 }
132
133 /**
134 * Setting up routes for different controller methods.
135 *
136 * @param string $method The function to call.
137 * @param string $class_name Class name.
138 * @param bool $is_private Should this expose for non-auth user.
139 * @param bool $is_redirect Include redirect URL in response if necessary.
140 *
141 * @return void
142 */
143 public function add_route( $method, $class_name, $is_private = true, $is_redirect = false ) {
144 $intention = $this->get_intention( $class_name, $method );
145
146 wd_di()->set(
147 sprintf( 'controller.%s', $intention ),
148 array(
149 $class_name,
150 $method,
151 $is_private,
152 $is_redirect,
153 )
154 );
155 wd_di()->set( sprintf( 'route.%s', $intention ), $intention );
156 wd_di()->set( sprintf( 'nonce.%s', $intention ), wp_create_nonce( $intention ) );
157 }
158
159 /**
160 * Retrieves the route for a given method and class name.
161 *
162 * @param string $method The method name.
163 * @param string $class_name The class name.
164 *
165 * @return mixed The route associated with the intention, or null if not found.
166 */
167 public function get_route( $method, $class_name ): mixed {
168 $intention = $this->get_intention( $class_name, $method );
169
170 try {
171 return wd_di()->get( sprintf( 'route.%s', $intention ) );
172 } catch ( Exception $e ) {
173 $this->log( $e->getMessage(), self::INTERNAL_LOG );
174 }
175
176 return null;
177 }
178
179 /**
180 * Retrieves the nonce for a given method and class name.
181 *
182 * @param string $method The method name.
183 * @param string $class_name The class name.
184 *
185 * @return mixed The nonce associated with the intention, or null if not found.
186 */
187 public function get_nonce( $method, $class_name ): mixed {
188 $intention = $this->get_intention( $class_name, $method );
189
190 try {
191 return wd_di()->get( sprintf( 'nonce.%s', $intention ) );
192 } catch ( Exception $e ) {
193 $this->log( $e->getMessage(), self::INTERNAL_LOG );
194 }
195
196 return null;
197 }
198
199 /**
200 * Check OPcache is enabled or not.
201 */
202 private function check_opcache() {
203 if ( $this->is_opcache_save_comments_disabled() ) {
204 wp_send_json_error(
205 array(
206 'message' => sprintf(
207 /* translators: %s: Name settings. */
208 esc_html__( '%s is disabled. Please contact your hosting provider to enable it.', 'defender-security' ),
209 '<strong>OPcache Save Comments</strong>'
210 ),
211 'type_notice' => 'opcache_disabled',
212 )
213 );
214 }
215 }
216
217 /**
218 * Check OPcache is enabled or not.
219 *
220 * @return bool
221 */
222 public function is_opcache_save_comments_disabled() {
223 // If OPcache is disabled.
224 if ( ini_get( 'opcache.enable' ) !== '1' ) {
225 return false;
226 }
227
228 // If OPcache is enabled and save comments disabled.
229 if ( ini_get( 'opcache.save_comments' ) !== '1' ) {
230 return true;
231 }
232
233 // Any other case.
234 return false;
235 }
236
237 /**
238 * Display message if OPcache is disabled.
239 *
240 * @return string
241 */
242 public function display_opcache_message() {
243 return sprintf(
244 /* translators: 1. Option settings. 2. Name settings. */
245 esc_html__(
246 'We have detected that your %1$s is disabled on your hosting. For Defender to function properly, please contact your hosting provider and ask them to enable %2$s.',
247 'defender-security'
248 ),
249 '<strong>opcache.save_comments</strong>',
250 '<strong>OPcache Save Comments</strong>'
251 );
252 }
253
254 /**
255 * Verify is ajax call is private.
256 * Here private stands for only authenticated user can do ajax call.
257 *
258 * @param string $route Route md5 hash.
259 *
260 * @return bool Return true if the request is private else false.
261 */
262 private function is_private_access( $route ) {
263 $key = sprintf( 'controller.%s', $route );
264
265 try {
266 $package = wd_di()->get( $key );
267
268 return isset( $package[2] ) && true === (bool) $package[2];
269 } catch ( Exception $e ) {
270 $this->log( $e->getMessage(), self::INTERNAL_LOG );
271 }
272
273 return false;
274 }
275
276 /**
277 * Get intention.
278 *
279 * @param string $class_name Class name.
280 * @param string $method Method name.
281 *
282 * @return string
283 * @since 3.11.0
284 */
285 private function get_intention( string $class_name, string $method ) {
286 $intention = sprintf( '%s.%s', $class_name, $method );
287
288 if ( ! defined( 'DEFENDER_DEBUG' ) || false === DEFENDER_DEBUG ) {
289 $intention = hash( 'md5', $intention );
290 }
291
292 return $intention;
293 }
294
295 /**
296 * Add redirect URL in response.
297 *
298 * @param string $route Route md5 hash.
299 *
300 * @return bool Return true if redirect URL is required in response else false.
301 * @since 3.11.0
302 */
303 private function is_redirect( $route ) {
304 $key = sprintf( 'controller.%s', $route );
305
306 try {
307 $package = wd_di()->get( $key );
308
309 return isset( $package[3] ) && true === (bool) $package[3];
310 } catch ( Exception $e ) {
311 $this->log( $e->getMessage(), self::INTERNAL_LOG );
312 }
313
314 return false;
315 }
316 }
317