diff --git a/accessibility-checker.php b/accessibility-checker.php index dd55a623c..deebb0ff7 100755 --- a/accessibility-checker.php +++ b/accessibility-checker.php @@ -10,7 +10,7 @@ * Plugin Name: Accessibility Checker * Plugin URI: https://a11ychecker.com * Description: Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance. - * Version: 1.42.0 + * Version: 1.42.1 * Requires PHP: 7.4 * Author: Equalize Digital * Author URI: https://equalizedigital.com @@ -36,7 +36,7 @@ // Current plugin version. if ( ! defined( 'EDAC_VERSION' ) ) { - define( 'EDAC_VERSION', '1.42.0' ); + define( 'EDAC_VERSION', '1.42.1' ); } // Current database version. diff --git a/admin/class-ajax.php b/admin/class-ajax.php index 545b3b75a..736ebfc22 100644 --- a/admin/class-ajax.php +++ b/admin/class-ajax.php @@ -819,17 +819,54 @@ public function add_ignore() { } global $wpdb; - $table_name = $wpdb->prefix . 'accessibility_checker'; - $raw_ids = isset( $_REQUEST['ids'] ) ? (array) wp_unslash( $_REQUEST['ids'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization handled below. - $ids = array_map( + $table_name = $wpdb->prefix . 'accessibility_checker'; + $raw_ids = isset( $_REQUEST['ids'] ) ? (array) wp_unslash( $_REQUEST['ids'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization handled below. + $ids = array_map( function ( $value ) { return (int) $value; }, $raw_ids ); // Sanitizing array elements to integers. - $action = isset( $_REQUEST['ignore_action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['ignore_action'] ) ) : ''; - $type = isset( $_REQUEST['ignore_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['ignore_type'] ) ) : ''; - $siteid = get_current_blog_id(); + $action = isset( $_REQUEST['ignore_action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['ignore_action'] ) ) : ''; + $type = isset( $_REQUEST['ignore_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['ignore_type'] ) ) : ''; + $siteid = get_current_blog_id(); + + // Capability check: verify edit_post on every post affected by this request. + $batch_object = null; + $first_id = reset( $ids ); + $valid_table = edac_get_valid_table_name( $table_name ); + + if ( ! $first_id || ! $valid_table ) { + wp_send_json_error( new \WP_Error( '-2', __( 'No ignore data to return', 'accessibility-checker' ) ) ); + } + + if ( isset( $_REQUEST['largeBatch'] ) && 'true' === $_REQUEST['largeBatch'] ) { + // largeBatch updates every row sharing the same object string across the site; + // collect all distinct postids that would be affected and check each one. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Permission check requires direct lookup. + $batch_object = $wpdb->get_var( $wpdb->prepare( 'SELECT object FROM %i WHERE id = %d', $valid_table, $first_id ) ); + if ( ! $batch_object ) { + wp_send_json_error( new \WP_Error( '-2', __( 'No ignore data to return', 'accessibility-checker' ) ) ); + } + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Permission check requires direct lookup. + $affected_post_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT postid FROM %i WHERE siteid = %d AND object = %s', $valid_table, $siteid, $batch_object ) ); + } else { + // Small batch: look up the post for every supplied ID in one query. + $id_placeholders = implode( ', ', array_fill( 0, count( $ids ), '%d' ) ); + $query_args = array_merge( [ $valid_table ], $ids ); + $affected_post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT postid FROM %i WHERE id IN ({$id_placeholders})", $query_args ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Permission check requires direct lookup. + } + + if ( empty( $affected_post_ids ) ) { + wp_send_json_error( new \WP_Error( '-2', __( 'No ignore data to return', 'accessibility-checker' ) ) ); + } + + foreach ( $affected_post_ids as $affected_post_id ) { + if ( ! current_user_can( 'edit_post', (int) $affected_post_id ) ) { + wp_send_json_error( new \WP_Error( '-5', __( 'Permission Denied', 'accessibility-checker' ) ) ); + } + } + $ignre = ( 'enable' === $action ) ? 1 : 0; $ignre_user = ( 'enable' === $action ) ? get_current_user_id() : null; $ignre_user_info = ( 'enable' === $action ) ? get_userdata( $ignre_user ) : ''; @@ -844,14 +881,8 @@ function ( $value ) { // instead of IDs. It is a much less efficient query than by IDs - but many IDs run // into request size limits which caused this to not function at all. if ( isset( $_REQUEST['largeBatch'] ) && 'true' === $_REQUEST['largeBatch'] ) { - // Get the 'object' from the first id. - $first_id = $ids[0]; - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need to get the latest value, not a cached value. - $object = $wpdb->get_var( $wpdb->prepare( 'SELECT object FROM %i WHERE id = %d', $table_name, $first_id ) ); - - if ( ! $object ) { - wp_send_json_error( new \WP_Error( '-2', __( 'No ignore data to return', 'accessibility-checker' ) ) ); - } + // $batch_object was already resolved during the capability check above. + $object = $batch_object; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Safe variable used for table name, caching not required for one time operation. $wpdb->query( $wpdb->prepare( 'UPDATE %i SET ignre = %d, ignre_user = %d, ignre_date = %s, ignre_comment = %s, ignre_reason = %s, ignre_global = %d WHERE siteid = %d and object = %s', $table_name, $ignre, $ignre_user, $ignre_date, $ignre_comment, $ignre_reason, $ignore_global, $siteid, $object ) ); } else { diff --git a/changelog.txt b/changelog.txt index 4b651a8d1..2da73846f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ *** Accessibility Checker *** +2026-05-20 - version 1.42.1 +* Updated - improved permission handling for the ignore feature. + 2026-05-18 - version 1.42.0 * Updated - add support for role="none" in several of the link and image alt related rules. * Updated - added support for named anchors as jump links. diff --git a/package.json b/package.json index 1b7a9ca34..779d04d2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "accessibility-checker", - "version": "1.42.0", + "version": "1.42.1", "description": "Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance.", "author": "Equalize Digital", "license": "GPL-2.0+", diff --git a/readme.txt b/readme.txt index 31e21529b..cd832946f 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: equalizedigital, alh0319, stevejonesdev Tags: accessibility, EAA, WCAG, ADA, WP accessibility, accessibility scanner Requires at least: 6.7 Tested up to: 7.0 -Stable tag: 1.42.0 +Stable tag: 1.42.1 Requires PHP: 7.4 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -279,6 +279,9 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == +2026-05-20 - version 1.42.1 +* Updated - improved permission handling for the ignore feature. + 2026-05-18 - version 1.42.0 * Updated - add support for role="none" in several of the link and image alt related rules. * Updated - added support for named anchors as jump links. diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index d4ff4420f..946529035 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -375,40 +375,11 @@ public function testSrOnlyFormatDoesNotEnqueueOnOtherAdminPages() { * @param bool $is_block_editor Whether the screen should behave as block editor. */ private function set_mock_screen( bool $is_block_editor ): void { - $GLOBALS['current_screen'] = new class( $is_block_editor ) { - /** - * True or false whether the screen is block editor. - * - * @var bool - */ - private $is_block_editor; - - /** - * Constructor. - * - * @param bool $is_block_editor Whether the screen should behave as block editor. - */ - public function __construct( bool $is_block_editor ) { - $this->is_block_editor = $is_block_editor; - } - - /** - * Mock is_block_editor method. - * - * @return bool - */ - public function is_block_editor(): bool { - return $this->is_block_editor; - } - - /** - * Mock in_admin method. - * - * @return bool - */ - public function in_admin(): bool { - return true; - } - }; + // As of WP 7.0, get_current_screen() requires an actual WP_Screen + // instance. Use WP_Screen::get() to obtain one without the side effects + // of set_current_screen() (e.g. setting $hook_suffix, $typenow, firing + // the current_screen action). + $GLOBALS['current_screen'] = WP_Screen::get( 'post' ); + $GLOBALS['current_screen']->is_block_editor = $is_block_editor; } } diff --git a/tests/phpunit/Admin/MetaBoxesTest.php b/tests/phpunit/Admin/MetaBoxesTest.php index d5c07adb3..456e22812 100644 --- a/tests/phpunit/Admin/MetaBoxesTest.php +++ b/tests/phpunit/Admin/MetaBoxesTest.php @@ -128,32 +128,12 @@ public function test_register_meta_boxes_keeps_classic_editor_when_setting_disab * @param bool $is_block_editor Whether the screen should behave as block editor. */ private function set_mock_screen( bool $is_block_editor ): void { - $GLOBALS['current_screen'] = new class( $is_block_editor ) { - /** - * True or false whether the screen is block editor. - * - * @var bool - */ - private $is_block_editor; - - /** - * Constructor. - * - * @param bool $is_block_editor Whether the screen should behave as block editor. - */ - public function __construct( bool $is_block_editor ) { - $this->is_block_editor = $is_block_editor; - } - - /** - * Mock is_block_editor method. - * - * @return bool - */ - public function is_block_editor(): bool { - return $this->is_block_editor; - } - }; + // As of WP 7.0, get_current_screen() requires an actual WP_Screen + // instance. Use WP_Screen::get() to obtain one without the side effects + // of set_current_screen() (e.g. setting $hook_suffix, $typenow, firing + // the current_screen action). + $GLOBALS['current_screen'] = WP_Screen::get( 'post' ); + $GLOBALS['current_screen']->is_block_editor = $is_block_editor; } /**