PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.1
Defender Security – Malware Scanner, Login Security & Firewall v6.2.1
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 6.2.1, at src/class-central.php

316 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 return call_user_func( array( $object, $method ), $request );
127 } else {
128 $this->log( sprintf( 'class not found when executing: %s %s', $class_name, $method ), self::INTERNAL_LOG );
129 }
130 }
131
132 /**
133 * Setting up routes for different controller methods.
134 *
135 * @param string $method The function to call.
136 * @param string $class_name Class name.
137 * @param bool $is_private Should this expose for non-auth user.
138 * @param bool $is_redirect Include redirect URL in response if necessary.
139 *
140 * @return void
141 */
142 public function add_route( $method, $class_name, $is_private = true, $is_redirect = false ) {
143 $intention = $this->get_intention( $class_name, $method );
144
145 wd_di()->set(
146 sprintf( 'controller.%s', $intention ),
147 array(
148 $class_name,
149 $method,
150 $is_private,
151 $is_redirect,
152 )
153 );
154 wd_di()->set( sprintf( 'route.%s', $intention ), $intention );
155 wd_di()->set( sprintf( 'nonce.%s', $intention ), wp_create_nonce( $intention ) );
156 }
157
158 /**
159 * Retrieves the route for a given method and class name.
160 *
161 * @param string $method The method name.
162 * @param string $class_name The class name.
163 *
164 * @return mixed The route associated with the intention, or null if not found.
165 */
166 public function get_route( $method, $class_name ): mixed {
167 $intention = $this->get_intention( $class_name, $method );
168
169 try {
170 return wd_di()->get( sprintf( 'route.%s', $intention ) );
171 } catch ( Exception $e ) {
172 $this->log( $e->getMessage(), self::INTERNAL_LOG );
173 }
174
175 return null;
176 }
177
178 /**
179 * Retrieves the nonce for a given method and class name.
180 *
181 * @param string $method The method name.
182 * @param string $class_name The class name.
183 *
184 * @return mixed The nonce associated with the intention, or null if not found.
185 */
186 public function get_nonce( $method, $class_name ): mixed {
187 $intention = $this->get_intention( $class_name, $method );
188
189 try {
190 return wd_di()->get( sprintf( 'nonce.%s', $intention ) );
191 } catch ( Exception $e ) {
192 $this->log( $e->getMessage(), self::INTERNAL_LOG );
193 }
194
195 return null;
196 }
197
198 /**
199 * Check OPcache is enabled or not.
200 */
201 private function check_opcache() {
202 if ( $this->is_opcache_save_comments_disabled() ) {
203 wp_send_json_error(
204 array(
205 'message' => sprintf(
206 /* translators: %s: Name settings. */
207 esc_html__( '%s is disabled. Please contact your hosting provider to enable it.', 'defender-security' ),
208 '<strong>OPcache Save Comments</strong>'
209 ),
210 'type_notice' => 'opcache_disabled',
211 )
212 );
213 }
214 }
215
216 /**
217 * Check OPcache is enabled or not.
218 *
219 * @return bool
220 */
221 public function is_opcache_save_comments_disabled() {
222 // If OPcache is disabled.
223 if ( ini_get( 'opcache.enable' ) !== '1' ) {
224 return false;
225 }
226
227 // If OPcache is enabled and save comments disabled.
228 if ( ini_get( 'opcache.save_comments' ) !== '1' ) {
229 return true;
230 }
231
232 // Any other case.
233 return false;
234 }
235
236 /**
237 * Display message if OPcache is disabled.
238 *
239 * @return string
240 */
241 public function display_opcache_message() {
242 return sprintf(
243 /* translators: 1. Option settings. 2. Name settings. */
244 esc_html__(
245 '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.',
246 'defender-security'
247 ),
248 '<strong>opcache.save_comments</strong>',
249 '<strong>OPcache Save Comments</strong>'
250 );
251 }
252
253 /**
254 * Verify is ajax call is private.
255 * Here private stands for only authenticated user can do ajax call.
256 *
257 * @param string $route Route md5 hash.
258 *
259 * @return bool Return true if the request is private else false.
260 */
261 private function is_private_access( $route ) {
262 $key = sprintf( 'controller.%s', $route );
263
264 try {
265 $package = wd_di()->get( $key );
266
267 return isset( $package[2] ) && true === (bool) $package[2];
268 } catch ( Exception $e ) {
269 $this->log( $e->getMessage(), self::INTERNAL_LOG );
270 }
271
272 return false;
273 }
274
275 /**
276 * Get intention.
277 *
278 * @param string $class_name Class name.
279 * @param string $method Method name.
280 *
281 * @return string
282 * @since 3.11.0
283 */
284 private function get_intention( string $class_name, string $method ) {
285 $intention = sprintf( '%s.%s', $class_name, $method );
286
287 if ( ! defined( 'DEFENDER_DEBUG' ) || false === DEFENDER_DEBUG ) {
288 $intention = hash( 'md5', $intention );
289 }
290
291 return $intention;
292 }
293
294 /**
295 * Add redirect URL in response.
296 *
297 * @param string $route Route md5 hash.
298 *
299 * @return bool Return true if redirect URL is required in response else false.
300 * @since 3.11.0
301 */
302 private function is_redirect( $route ) {
303 $key = sprintf( 'controller.%s', $route );
304
305 try {
306 $package = wd_di()->get( $key );
307
308 return isset( $package[3] ) && true === (bool) $package[3];
309 } catch ( Exception $e ) {
310 $this->log( $e->getMessage(), self::INTERNAL_LOG );
311 }
312
313 return false;
314 }
315 }
316