PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-waf / src / class-waf-rules-manager.php
jetpack / jetpack_vendor / automattic / jetpack-waf / src Last commit date
brute-force-protection 9 months ago exceptions 9 months ago abstract-blocked-login-page.php 2 months ago class-brute-force-protection.php 2 months ago class-compatibility.php 2 months ago class-rest-controller.php 9 months ago class-waf-blocked-login-page.php 9 months ago class-waf-blocklog-manager.php 8 months ago class-waf-cli.php 9 months ago class-waf-constants.php 1 year ago class-waf-initializer.php 2 months ago class-waf-operators.php 2 years ago class-waf-request.php 6 months ago class-waf-rules-manager.php 1 year ago class-waf-runner.php 2 months ago class-waf-runtime.php 2 months ago class-waf-standalone-bootstrap.php 2 months ago class-waf-stats.php 1 year ago class-waf-transforms.php 2 years ago functions.php 1 year ago
class-waf-rules-manager.php
363 lines
1 <?php
2 /**
3 * Class for generating and working with firewall rule files.
4 *
5 * @since 0.9.0
6 *
7 * @package automattic/jetpack-waf
8 */
9
10 namespace Automattic\Jetpack\Waf;
11
12 use Automattic\Jetpack\Connection\Client;
13 use Automattic\Jetpack\IP\Utils as IP_Utils;
14 use Jetpack_Options;
15 use WP_Error;
16
17 /**
18 * Class for generating and working with firewall rule files.
19 */
20 class Waf_Rules_Manager {
21
22 const RULES_VERSION = '1.0.0';
23
24 // WAF Options
25 const VERSION_OPTION_NAME = 'jetpack_waf_rules_version';
26 const AUTOMATIC_RULES_ENABLED_OPTION_NAME = 'jetpack_waf_automatic_rules';
27 const IP_ALLOW_LIST_OPTION_NAME = 'jetpack_waf_ip_allow_list';
28 const IP_ALLOW_LIST_ENABLED_OPTION_NAME = 'jetpack_waf_ip_allow_list_enabled';
29 const IP_BLOCK_LIST_OPTION_NAME = 'jetpack_waf_ip_block_list';
30 const IP_BLOCK_LIST_ENABLED_OPTION_NAME = 'jetpack_waf_ip_block_list_enabled';
31 const RULE_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_last_updated_timestamp';
32 const AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME = 'jetpack_waf_automatic_rules_last_updated_timestamp';
33
34 /**
35 * IP Lists Enabled Option Name
36 *
37 * @deprecated 0.17.0 Use Waf_Rules_Manager::IP_ALLOW_LIST_ENABLED_OPTION_NAME and Waf_Rules_Manager::IP_BLOCK_LIST_ENABLED_OPTION_NAME instead.
38 */
39 const IP_LISTS_ENABLED_OPTION_NAME = 'jetpack_waf_ip_list';
40
41 // Rule Files
42 const AUTOMATIC_RULES_FILE = '/rules/automatic-rules.php';
43 const IP_ALLOW_RULES_FILE = '/rules/allow-ip.php';
44 const IP_BLOCK_RULES_FILE = '/rules/block-ip.php';
45
46 /**
47 * Rules Entrypoint File
48 *
49 * @deprecated 0.22.0 Use JETPACK_WAF_ENTRYPOINT instead.
50 */
51 const RULES_ENTRYPOINT_FILE = '/rules/rules.php';
52
53 /**
54 * Whether automatic rules are enabled.
55 *
56 * @return bool
57 */
58 public static function automatic_rules_enabled() {
59 return (bool) get_option( self::AUTOMATIC_RULES_ENABLED_OPTION_NAME );
60 }
61
62 /**
63 * Whether IP allow list is enabled.
64 *
65 * @return bool
66 */
67 public static function ip_allow_list_enabled() {
68 return (bool) get_option( self::IP_ALLOW_LIST_ENABLED_OPTION_NAME );
69 }
70
71 /**
72 * Whether IP block list is enabled.
73 *
74 * @return bool
75 */
76 public static function ip_block_list_enabled() {
77 return (bool) get_option( self::IP_BLOCK_LIST_ENABLED_OPTION_NAME );
78 }
79
80 /**
81 * Register WordPress hooks for the WAF rules.
82 *
83 * @return void
84 */
85 public static function add_hooks() {
86 // Re-activate the WAF any time an option is added or updated.
87 add_action( 'add_option_' . self::AUTOMATIC_RULES_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
88 add_action( 'update_option_' . self::AUTOMATIC_RULES_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
89 add_action( 'add_option_' . self::IP_ALLOW_LIST_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
90 add_action( 'update_option_' . self::IP_ALLOW_LIST_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
91 add_action( 'add_option_' . self::IP_ALLOW_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
92 add_action( 'update_option_' . self::IP_ALLOW_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
93 add_action( 'add_option_' . self::IP_BLOCK_LIST_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
94 add_action( 'update_option_' . self::IP_BLOCK_LIST_ENABLED_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
95 add_action( 'add_option_' . self::IP_BLOCK_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
96 add_action( 'update_option_' . self::IP_BLOCK_LIST_OPTION_NAME, array( static::class, 'reactivate_on_rules_option_change' ), 10, 0 );
97 // Register the cron job.
98 add_action( 'jetpack_waf_rules_update_cron', array( static::class, 'update_rules_cron' ) );
99 }
100
101 /**
102 * Schedule the cron job to update the WAF rules.
103 *
104 * @return bool|WP_Error True if the event is scheduled, WP_Error on failure.
105 */
106 public static function schedule_rules_cron() {
107 if ( ! wp_next_scheduled( 'jetpack_waf_rules_update_cron' ) ) {
108 return wp_schedule_event( time(), 'twicedaily', 'jetpack_waf_rules_update_cron', array(), true );
109 }
110
111 return true;
112 }
113
114 /**
115 * Tries periodically to update the rules using our API.
116 *
117 * @return bool|WP_Error True if rules update is successful, WP_Error on failure.
118 */
119 public static function update_rules_cron() {
120 try {
121 self::generate_automatic_rules();
122 self::generate_ip_rules();
123 self::generate_rules();
124 } catch ( Waf_Exception $e ) {
125 return $e->get_wp_error();
126 }
127
128 update_option( self::RULE_LAST_UPDATED_OPTION_NAME, time() );
129 return true;
130 }
131
132 /**
133 * Re-activate the WAF any time an option is added or updated.
134 *
135 * @return bool|WP_Error True if re-activation is successful, WP_Error on failure.
136 */
137 public static function reactivate_on_rules_option_change() {
138 try {
139 Waf_Runner::activate();
140 } catch ( Waf_Exception $e ) {
141 return $e->get_wp_error();
142 }
143
144 return true;
145 }
146
147 /**
148 * Updates the rule set if rules version has changed
149 *
150 * @throws Waf_Exception If the firewall mode is invalid.
151 * @throws Waf_Exception If the rules update fails.
152 *
153 * @return void
154 */
155 public static function update_rules_if_changed() {
156 $version = get_option( self::VERSION_OPTION_NAME );
157 if ( self::RULES_VERSION !== $version ) {
158 self::generate_automatic_rules();
159 self::generate_ip_rules();
160 self::generate_rules();
161
162 update_option( self::VERSION_OPTION_NAME, self::RULES_VERSION );
163 }
164 }
165
166 /**
167 * Retrieve rules from the API
168 *
169 * @throws Waf_Exception If site is not registered.
170 * @throws Rules_API_Exception If API did not respond 200.
171 * @throws Rules_API_Exception If data is missing from response.
172 *
173 * @return array
174 */
175 public static function get_rules_from_api() {
176 $blog_id = Jetpack_Options::get_option( 'id' );
177 if ( ! $blog_id ) {
178 throw new Waf_Exception( 'Site is not registered' );
179 }
180
181 $response = Client::wpcom_json_api_request_as_blog(
182 sprintf( '/sites/%s/waf-rules', $blog_id ),
183 '2',
184 array(),
185 null,
186 'wpcom'
187 );
188
189 $response_code = wp_remote_retrieve_response_code( $response );
190
191 if ( 200 !== $response_code ) {
192 throw new Rules_API_Exception( 'API connection failed.', (int) $response_code );
193 }
194
195 $rules_json = wp_remote_retrieve_body( $response );
196 $rules = json_decode( $rules_json, true );
197
198 if ( empty( $rules['data'] ) ) {
199 throw new Rules_API_Exception( 'Data missing from response.' );
200 }
201
202 return $rules['data'];
203 }
204
205 /**
206 * Wraps a require statement in a file_exists check.
207 *
208 * @param string $required_file The file to check if exists and require.
209 * @param string $return_code The PHP code to execute if the file require returns true. Defaults to 'return;'.
210 *
211 * @return string The wrapped require statement.
212 */
213 private static function wrap_require( $required_file, $return_code = 'return;' ) {
214 return "if ( file_exists( '$required_file' ) ) { if ( require( '$required_file' ) ) { $return_code } }";
215 }
216
217 /**
218 * Generates the rules.php script
219 *
220 * @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
221 *
222 * @throws File_System_Exception If file writing fails initializing rule files.
223 * @throws File_System_Exception If file writing fails writing to the rules entrypoint file.
224 *
225 * @return void
226 */
227 public static function generate_rules() {
228 global $wp_filesystem;
229 Waf_Runner::initialize_filesystem();
230 Waf_Constants::define_entrypoint();
231
232 $rules = "<?php\n";
233 $entrypoint_file_path = Waf_Runner::get_waf_file_path( JETPACK_WAF_ENTRYPOINT );
234
235 // Ensure that the folder exists
236 if ( ! $wp_filesystem->is_dir( dirname( $entrypoint_file_path ) ) ) {
237 $wp_filesystem->mkdir( dirname( $entrypoint_file_path ) );
238 }
239
240 // Ensure all potentially required rule files exist
241 $rule_files = array( JETPACK_WAF_ENTRYPOINT, self::AUTOMATIC_RULES_FILE, self::IP_ALLOW_RULES_FILE, self::IP_BLOCK_RULES_FILE );
242 foreach ( $rule_files as $rule_file ) {
243 $rule_file = Waf_Runner::get_waf_file_path( $rule_file );
244 if ( ! $wp_filesystem->is_file( $rule_file ) ) {
245 if ( ! $wp_filesystem->put_contents( $rule_file, "<?php\n" ) ) {
246 throw new File_System_Exception( 'Failed writing rules file to: ' . $rule_file );
247 }
248 }
249 }
250
251 // Add IP allow list
252 if ( self::ip_allow_list_enabled() ) {
253 $rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_ALLOW_RULES_FILE ) ) . "\n";
254 }
255
256 // Add IP block list
257 if ( self::ip_block_list_enabled() ) {
258 $rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::IP_BLOCK_RULES_FILE ), "return \$waf->block( 'block', -1, 'ip block list' );" ) . "\n";
259 }
260
261 // Add automatic rules
262 if ( self::automatic_rules_enabled() ) {
263 $rules .= self::wrap_require( Waf_Runner::get_waf_file_path( self::AUTOMATIC_RULES_FILE ) ) . "\n";
264 }
265
266 // Update the rules file
267 if ( ! $wp_filesystem->put_contents( $entrypoint_file_path, $rules ) ) {
268 throw new File_System_Exception( 'Failed writing rules file to: ' . $entrypoint_file_path );
269 }
270 }
271
272 /**
273 * Generates the automatic-rules.php script
274 *
275 * @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
276 *
277 * @throws Waf_Exception If rules cannot be fetched from the API.
278 * @throws File_System_Exception If file writing fails.
279 *
280 * @return void
281 */
282 public static function generate_automatic_rules() {
283 global $wp_filesystem;
284 Waf_Runner::initialize_filesystem();
285
286 $automatic_rules_file_path = Waf_Runner::get_waf_file_path( self::AUTOMATIC_RULES_FILE );
287
288 // Ensure that the folder exists.
289 if ( ! $wp_filesystem->is_dir( dirname( $automatic_rules_file_path ) ) ) {
290 $wp_filesystem->mkdir( dirname( $automatic_rules_file_path ) );
291 }
292
293 try {
294 $rules = self::get_rules_from_api();
295 } catch ( Waf_Exception $e ) {
296 // Do not throw API exceptions for users who do not have access
297 if ( 401 !== $e->getCode() ) {
298 throw $e;
299 }
300 }
301
302 // If there are no rules available, don't overwrite the existing file.
303 if ( empty( $rules ) ) {
304 return;
305 }
306
307 if ( ! $wp_filesystem->put_contents( $automatic_rules_file_path, $rules ) ) {
308 throw new File_System_Exception( 'Failed writing automatic rules file to: ' . $automatic_rules_file_path );
309 }
310
311 update_option( self::AUTOMATIC_RULES_LAST_UPDATED_OPTION_NAME, time() );
312 }
313
314 /**
315 * Generates the rules.php script
316 *
317 * @global \WP_Filesystem_Base $wp_filesystem WordPress filesystem abstraction.
318 *
319 * @throws File_System_Exception If writing to IP allow list file fails.
320 * @throws File_System_Exception If writing to IP block list file fails.
321 *
322 * @return void
323 */
324 public static function generate_ip_rules() {
325 global $wp_filesystem;
326 Waf_Runner::initialize_filesystem();
327
328 $allow_ip_file_path = Waf_Runner::get_waf_file_path( self::IP_ALLOW_RULES_FILE );
329 $block_ip_file_path = Waf_Runner::get_waf_file_path( self::IP_BLOCK_RULES_FILE );
330
331 // Ensure that the folders exists.
332 if ( ! $wp_filesystem->is_dir( dirname( $allow_ip_file_path ) ) ) {
333 $wp_filesystem->mkdir( dirname( $allow_ip_file_path ) );
334 }
335 if ( ! $wp_filesystem->is_dir( dirname( $block_ip_file_path ) ) ) {
336 $wp_filesystem->mkdir( dirname( $block_ip_file_path ) );
337 }
338
339 $allow_list = IP_Utils::get_ip_addresses_from_string( get_option( self::IP_ALLOW_LIST_OPTION_NAME ) );
340 $block_list = IP_Utils::get_ip_addresses_from_string( get_option( self::IP_BLOCK_LIST_OPTION_NAME ) );
341
342 $allow_rules_content = '';
343 // phpcs:disable WordPress.PHP.DevelopmentFunctions
344 $allow_rules_content .= '$waf_allow_list = ' . var_export( $allow_list, true ) . ";\n";
345 // phpcs:enable
346 $allow_rules_content .= 'return $waf->is_ip_in_array( $waf_allow_list );' . "\n";
347
348 if ( ! $wp_filesystem->put_contents( $allow_ip_file_path, "<?php\n$allow_rules_content" ) ) {
349 throw new File_System_Exception( 'Failed writing allow list file to: ' . $allow_ip_file_path );
350 }
351
352 $block_rules_content = '';
353 // phpcs:disable WordPress.PHP.DevelopmentFunctions
354 $block_rules_content .= '$waf_block_list = ' . var_export( $block_list, true ) . ";\n";
355 // phpcs:enable
356 $block_rules_content .= 'return $waf->is_ip_in_array( $waf_block_list );' . "\n";
357
358 if ( ! $wp_filesystem->put_contents( $block_ip_file_path, "<?php\n$block_rules_content" ) ) {
359 throw new File_System_Exception( 'Failed writing block list file to: ' . $block_ip_file_path );
360 }
361 }
362 }
363