From 941668567264e427a2070bcacee5750e6a3dbc8d Mon Sep 17 00:00:00 2001 From: Konstantinos Galanakis Date: Thu, 23 Mar 2023 18:25:28 +0200 Subject: [PATCH 01/37] Initial commit --- .eslintignore | 1 + .eslintrc.js | 18 + .eslintrc.json | 14 - config/webpack.config.common.js | 12 +- config/webpack.settings.js | 1 + includes/admin.php | 45 +++ includes/assets.php | 24 ++ includes/classes/class-fixinsecurecontent.php | 313 ++++++++++++++++++ includes/partials/admin-page.php | 90 +++++ includes/rest.php | 94 +++++- includes/wp-cli/insecure-content-warning.php | 197 +---------- insecure-content-warning.php | 3 + package-lock.json | 90 +++-- package.json | 3 +- src/css/admin.css | 31 ++ src/js/admin.js | 132 ++++++++ 16 files changed, 822 insertions(+), 246 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json create mode 100644 includes/admin.php create mode 100644 includes/classes/class-fixinsecurecontent.php create mode 100644 includes/partials/admin-page.php create mode 100644 src/js/admin.js diff --git a/.eslintignore b/.eslintignore index 44db04a..626181d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ src/js/shared/vendor gulp-tasks/ webpack.config.babel.js gulpfile.babel.js +node_modules/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b5d865f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + env: { jquery: true }, + extends: ['@10up/eslint-config/wordpress'], + rules: { + 'react/no-array-index-key': 'off', + 'jsdoc/newline-after-description': 'off', + "react/jsx-indent" : ["error", 0], + "react/jsx-indent-props" : ["error", 0], + }, + globals: { + module: true, + process: true, + wp: true, + jquery: true, + tinyMCE: true, + insecureContentAdmin: true, + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 66baa4b..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "@10up/eslint-config/wordpress", - "rules": { - "react/no-array-index-key": "off" - }, - "globals": { - "module": true, - "process": true, - "wp": true, - "jQuery": true, - "tinyMCE": true, - "insecureContentAdmin": true - } -} diff --git a/config/webpack.config.common.js b/config/webpack.config.common.js index c296322..55559be 100644 --- a/config/webpack.config.common.js +++ b/config/webpack.config.common.js @@ -16,7 +16,7 @@ const settings = require('./webpack.settings.js'); /** * Configure entries. * - * @return {Object[]} Array of webpack settings. + * @returns {object[]} Array of webpack settings. */ const configureEntries = () => { const entries = {}; @@ -66,7 +66,7 @@ module.exports = { enforce: 'pre', loader: 'eslint-loader', options: { - fix: true, + fix: false, }, }, @@ -155,14 +155,14 @@ module.exports = { // Fancy WebpackBar. new WebpackBar(), - new DependencyExtractorWebpackPlugin( { + new DependencyExtractorWebpackPlugin({ injectPolyfill: false, combineAssets: false, - requestToExternal( request ) { - if ( request === 'underscore' ) { + requestToExternal(request) { + if (request === 'underscore') { return '_'; } }, - } ), + }), ], }; diff --git a/config/webpack.settings.js b/config/webpack.settings.js index 7a344df..1429890 100644 --- a/config/webpack.settings.js +++ b/config/webpack.settings.js @@ -2,6 +2,7 @@ module.exports = { entries: { // JS files. + admin: './src/js/admin.js', gutenberg: './src/js/gutenberg.js', 'classic-editor': './src/js/classic-editor.js', diff --git a/includes/admin.php b/includes/admin.php new file mode 100644 index 0000000..045640b --- /dev/null +++ b/includes/admin.php @@ -0,0 +1,45 @@ + $posts_per_page, + 'post_type' => 'all' === $post_type ? 'any' : $post_type, + 'offset' => $post_offset, + 'nopaging' => true, + ); + + $query = new WP_Query( $args ); + + return $query->found_posts; + } + + /** + * Runs the fixing procedure for insecure content. + * + * @param bool|string $include Comma separated IDs of post if fixing post content for multiple posts. + * @param bool $all Flag for running the fix on all posts. + * @param string $post_type The post type. + * @param int $posts_per_page The batch size. + * @param int $post_offset The post offset. + * @param bool $dry_run Flag for dry or normal run. + * @param array $args WP CLI arguments. + * + * @return array|void + */ + public function fix( $include, $all, $post_type, $posts_per_page, $post_offset, $dry_run, $args = array() ) { + $this->dry_run = $dry_run; + + if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::log( __( 'Checking post content...', 'insecure-content-warning' ) ); + } + + if ( false === $all && ! empty( $args[0] ) ) { + $this->fix_insecure_content( $args[0] ); + } elseif ( false === $all && empty( $args ) && false !== $include ) { + $post_ids_list = explode( ',', $include ); + foreach ( $post_ids_list as $icw_post_id ) { + $this->fix_insecure_content( trim( $icw_post_id ) ); + } + $this->total_post_count = count( $post_ids_list ); + } else { + $total = 0; + + // Loop through all posts and fix content. + while ( true ) { + $args = array( + 'posts_per_page' => $posts_per_page, + 'post_type' => $post_type, + 'offset' => $post_offset, + 'orderby' => 'ID', + 'order' => 'ASC', + ); + $query = new WP_Query( $args ); + + if ( $query->have_posts() ) { + while ( $query->have_posts() ) { + $query->the_post(); + $this->fix_insecure_content( get_the_ID() ); + } + } else { + break; + } + + // Set offset for next page. + $post_offset += $posts_per_page; + $total += $query->post_count; + + if ( ! defined( 'WP_CLI' ) ) { + // when fixing post in a REST request, do it only for the current batch and not until all the posts + // have been fixed. + break; + } + + // Wait for a while before fixing the rest. + usleep( 1000 ); + } + + $this->total_post_count = $total; + } + + if ( defined( 'WP_CLI' ) && WP_CLI ) { + // translators: Message to show when the fixing of insecure content is completed. + $message = PHP_EOL . sprintf( __( 'Total posts checked for insecure URL(s): %s', 'insecure-content-warning' ), $this->total_post_count ) . PHP_EOL; + WP_CLI::log( WP_CLI::colorize( "%c{$message}%n " ) ); + Utils\format_items( 'table', $this->fixed_post_count, array( 'URL(s) fixed summary' ) ); + } else { + if ( null === $this->fixed_post_count ) { + return __( 'No post(s) found', 'insecure-content-warning' ); + } + + $output = ''; + + foreach ( $this->warning_count as $warning ) { + $warning_message = array_shift( $warning ); + $output .= '' . __( 'Warning', 'insecure-content-warning' ) . ': ' . $warning_message . PHP_EOL; + } + + foreach ( $this->fixed_post_count as $fixed_post ) { + $output .= array_shift( $fixed_post ) . PHP_EOL; + } + + return $output; + } + } + + /** + * Fix insecure content for given Post ID. + * + * @param int $post_id Post ID. + * + * @return array|string + */ + protected function fix_insecure_content( $post_id ) { + $current_post = get_post( $post_id ); + + // Check and make sure post exists. + if ( empty( $current_post ) ) { + // translators: Message to show when the system is unable to fetch details for the post. + $message = sprintf( __( 'Unable to fetch details for post %s', 'insecure-content-warning' ), $post_id ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { + // translators: Message to show when the system is unable to fetch details for the post. + WP_CLI::warning( $message ); + } else { + $this->warning_count[] = array( __( 'Warning', 'insecure-content-warning' ) => $message ); + } + return ''; + } + + // Loop through post content and get HTTPS URLs wherever possible. + $post_content = $current_post->post_content; + $urls = $this->parse_content_for_insecure_urls( $post_content ); + $count = 0; + + // Check if post content has insecure URLs. + if ( empty( $urls ) ) { + // translators: Message to show when no insecure content URL found in the post. + $message = sprintf( __( 'No insecure content URL found in post %s', 'insecure-content-warning' ), $post_id ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::warning( $message ); + } else { + $this->warning_count[] = array( __( 'Warning', 'insecure-content-warning' ) => $message ); + } + + if ( $this->dry_run ) { + // translators: Summary to show on dry run fix. + $this->fixed_post_count[] = array( __( 'URL(s) fixed summary', 'insecure-content-warning' ) => sprintf( __( '0/0 URL(s) will be fixed in post %s', 'insecure-content-warning' ), $post_id ) ); + } else { + // translators: Summary to show on fix. + $this->fixed_post_count[] = array( __( 'URL(s) fixed summary', 'insecure-content-warning' ) => sprintf( __( '0/0 URL(s) fixed in post %s', 'insecure-content-warning' ), $post_id ) ); + } + + return ''; + } + + foreach ( $urls as $url ) { + if ( $this->does_secure_content_exist( $url ) ) { + $ssl_url = preg_replace( '/^http:/i', 'https:', $url ); + $post_content = str_replace( $url, $ssl_url, $post_content ); + $count ++; + } + } + + if ( false === $this->dry_run ) { + // Update post content with HTTPS URLs. + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => $post_content, + ) + ); + } + + if ( $this->dry_run ) { + // translators: Summary to show on dry run fix. + $dry_run_message = sprintf( __( '%1$s/%2$s insecure URLs will be fixed in post %3$s.', 'insecure-content-warning' ), $count, count( $urls ), $post_id ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::debug( $dry_run_message ); + } + $this->fixed_post_count[] = array( 'URL(s) fixed summary' => $dry_run_message ); + } else { + // translators: Summary to show on fix. + $success_message = sprintf( __( '%1$s/%2$s insecure URLs fixed in post %3$s.', 'insecure-content-warning' ), $count, count( $urls ), $post_id ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::debug( $success_message ); + } + $this->fixed_post_count[] = array( 'URL(s) fixed summary' => $success_message ); + } + } + + /** + * Check if a SSL version of the URL exists. + * + * @param string $url URL to check for SSL version. + * + * @return bool + */ + protected function does_secure_content_exist( $url ) { + // Check if a https version of the URL exists. + $secure_version_exists = false; + $ssl = preg_replace( '/^http:/i', 'https:', $url ); + $response = wp_remote_get( $ssl ); + $response_code = wp_remote_retrieve_response_code( $response ); + + if ( 200 === $response_code ) { + $secure_version_exists = true; + } + + return $secure_version_exists; + } + + /** + * Create list of URLs. + * + * @param string $post_content Post Content. + * + * @return array + */ + protected function parse_content_for_insecure_urls( $post_content ) { + // Check all src and create an array of URLs to check https URL availability. + $src_urls = array(); + $src_regex = '/src="([^"]+)"/'; + preg_match_all( $src_regex, $post_content, $matches, PREG_SET_ORDER, 0 ); + + // If we have matches, loop through and get clean URL. + if ( ! empty( $matches ) ) { + foreach ( $matches as $match ) { + $matched_url = $match[1]; + $base_url = explode( '?', $matched_url )[0]; + if ( 'https://' !== substr( $base_url, 0, 8 ) ) { + $src_urls[] = $base_url; + } + } + } + + return $src_urls; + } +} diff --git a/includes/partials/admin-page.php b/includes/partials/admin-page.php new file mode 100644 index 0000000..707f8a0 --- /dev/null +++ b/includes/partials/admin-page.php @@ -0,0 +1,90 @@ + true, + 'public' => true, + ), + 'objects' +); +?> + +
+

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + + +
+
+ +
+ + +
+ + +
diff --git a/includes/rest.php b/includes/rest.php index 4d88f45..06f0a21 100644 --- a/includes/rest.php +++ b/includes/rest.php @@ -7,6 +7,10 @@ namespace ICW\Rest; +use ICW\FIX\FixInsecureContent; +use WP_REST_Request; +use WP_REST_Response; + /** * Setup actions and filters */ @@ -27,14 +31,34 @@ function rest_routes() { 'permission_callback' => '__return_true', ) ); + + register_rest_route( + 'icw/v1', + '/count-for-fix/', + array( + 'methods' => 'POST', + 'callback' => __NAMESPACE__ . '\\count_for_fix_endpoint', + 'permission_callback' => '__return_true', + ) + ); + + register_rest_route( + 'icw/v1', + '/fix/', + array( + 'methods' => 'POST', + 'callback' => __NAMESPACE__ . '\\fix_endpoint', + 'permission_callback' => '__return_true', + ) + ); } /** * Endpoint for asset checker * - * @param \WP_REST_Request $request Request object. + * @param WP_REST_Request $request Request object. * - * @return \WP_REST_Response + * @return WP_REST_Response */ function check_endpoint( $request ) { $params = $request->get_params(); @@ -50,3 +74,69 @@ function check_endpoint( $request ) { return rest_ensure_response( $secure_version_exists ); } + +/** + * Prepares the parameters for the fix-related endpoints from the provided WP_REST_Request + * + * @param WP_REST_Request $request Request object. + * + * @return array + */ +function prepare_fix_params( WP_REST_Request $request ): array { + $params = $request->get_params(); + + $args = wp_parse_args( + $params, + array( + 'postIds' => false, + 'postSelection' => 'all', + 'postType' => 'post', + 'batchSize' => 10, + 'offset' => 0, + 'dryRun' => false, + ) + ); + + return array( + 'include' => false === $params['postIds'] ? rest_sanitize_boolean( $params['postIds'] ) : sanitize_text_field( $params['postIds'] ), + 'all' => 'all' === sanitize_text_field( $params['postSelection'] ), + 'post_type' => 'all' !== sanitize_text_field( $params['postSelection'] ) ? sanitize_text_field( $params['postType'] ) : 'any', + 'batch_size' => absint( $args['batchSize'] ), + 'post_offset' => absint( $args['offset'] ), + 'dry_run' => ! empty( $params['dryRun'] ) && rest_sanitize_boolean( $params['dryRun'] ), + ); +} + +/** + * Endpoint for insecure content fixing + * + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response + */ +function fix_endpoint( WP_REST_Request $request ): WP_REST_Response { + $params = prepare_fix_params( $request ); + + $fixed_post_count = FixInsecureContent::get_instance()->fix( $params['include'], $params['all'], $params['post_type'], $params['batch_size'], $params['post_offset'], $params['dry_run'] ); + + return rest_ensure_response( $fixed_post_count ?? array() ); +} + +/** + * Endpoint for counting the post the insecure content fixing will be applied to. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response + */ +function count_for_fix_endpoint( WP_REST_Request $request ): WP_REST_Response { + $params = prepare_fix_params( $request ); + + if ( ! empty( $params['include'] ) ) { + return rest_ensure_response( count( explode( ',', trim( $params['include'] ) ) ) ); + } + + $posts_to_be_fixed_count = FixInsecureContent::get_instance()->count_post_to_check( $params['post_type'], $params['batch_size'], $params['post_offset'] ); + + return rest_ensure_response( $posts_to_be_fixed_count ?? array() ); +} diff --git a/includes/wp-cli/insecure-content-warning.php b/includes/wp-cli/insecure-content-warning.php index e7e4787..9f4fa33 100644 --- a/includes/wp-cli/insecure-content-warning.php +++ b/includes/wp-cli/insecure-content-warning.php @@ -7,6 +7,7 @@ namespace ICW\CLI; +use ICW\FIX\FixInsecureContent; use WP_CLI; use WP_CLI\Utils; use WP_CLI_Command; @@ -71,35 +72,6 @@ * @package ICW\CLI */ class InsecureContentWarning_CLI_Command extends \WP_CLI_Command { - - /** - * Is current run a dry run? - * - * @var bool - */ - private $dry_run; - - /** - * Total number of posts scanned for URLs. - * - * @var int - */ - private $total_post_count = 1; - - /** - * Data of posts where 1 or more URLs were found to be insecure and command fixed summary. - * - * @var array - */ - private $fixed_post_count; - - /** - * Data of posts that didn't have any insecure URLs. - * - * @var array - */ - private $no_url_post_count; - /** * Fix insecure content. * @@ -188,172 +160,9 @@ public function fix( $args, $assoc_args ) { $post_type = Utils\get_flag_value( $assoc_args, 'post_type', 'post' ); $posts_per_page = Utils\get_flag_value( $assoc_args, 'limit', 10 ); $post_offset = Utils\get_flag_value( $assoc_args, 'offset', 0 ); - $this->dry_run = Utils\get_flag_value( $assoc_args, 'dry-run', false ); - - WP_CLI::log( 'Checking post content...' ); - - if ( false === $all && ! empty( $args[0] ) ) { - $this->fix_insecure_content( $args[0] ); - } elseif ( false === $all && empty( $args ) && false !== $include ) { - $post_ids_list = explode( ',', $include ); - foreach ( $post_ids_list as $icw_post_id ) { - $this->fix_insecure_content( $icw_post_id ); - } - $this->total_post_count = count( $post_ids_list ); - } else { - $total = 0; - - // Loop through all posts and fix content. - while ( true ) { - $args = array( - 'posts_per_page' => $posts_per_page, - 'post_type' => $post_type, - 'post_status' => 'publish', - 'offset' => $post_offset, - ); - $query = new \WP_Query( $args ); - - if ( $query->have_posts() ) { - while ( $query->have_posts() ) { - $query->the_post(); - $this->fix_insecure_content( get_the_ID() ); - } - } else { - break; - } - - // Set offset for next page. - $post_offset += $posts_per_page; - $total += $query->post_count; - - // Wait for a while before fixing the rest. - usleep( 1000 ); - } - - $this->total_post_count = $total; - } - - WP_CLI::log( PHP_EOL . WP_CLI::colorize( "%cTotal posts checked for insecure URL(s): {$this->total_post_count}%n " ) . PHP_EOL ); - Utils\format_items( 'table', $this->fixed_post_count, array( 'URL(s) fixed summary' ) ); - } - - /** - * Fix insecure content for given Post ID. - * - * @param int $post_id Post ID. - * - * @return array|string - */ - protected function fix_insecure_content( $post_id ) { - $current_post = get_post( $post_id ); - - // Check and make sure post exists. - if ( empty( $current_post ) ) { - WP_CLI::warning( "Unable to fetch details for post ${post_id}" ); - - return ''; - } - - // Loop through post content and get HTTPS URLs wherever possible. - $post_content = $current_post->post_content; - $urls = $this->parse_content_for_insecure_urls( $post_content ); - $count = 0; - - // Check if post content has insecure URLs. - if ( empty( $urls ) ) { - WP_CLI::debug( "No insecure content URL found in post ${post_id}" ); - if ( $this->dry_run ) { - $this->fixed_post_count[] = array( - 'URL(s) fixed summary' => '0/0 URL(s) will be fixed in post ' . $post_id, - ); - } else { - $this->fixed_post_count[] = array( - 'URL(s) fixed summary' => '0/0 URL(s) fixed in post ' . $post_id, - ); - } - - return ''; - } - - foreach ( $urls as $url ) { - if ( $this->does_secure_content_exist( $url ) ) { - $ssl_url = preg_replace( '/^http:/i', 'https:', $url ); - $post_content = str_replace( $url, $ssl_url, $post_content ); - $count ++; - } - } - - if ( false === $this->dry_run ) { - // Update post content with HTTPS URLs. - wp_update_post( - array( - 'ID' => $post_id, - 'post_content' => $post_content, - ) - ); - } - - if ( $this->dry_run ) { - $dry_run_message = sprintf( '%s/%s insecure URLs will be fixed in post %s.', $count, count( $urls ), $post_id ); - WP_CLI::debug( $dry_run_message ); - $this->fixed_post_count[] = array( - 'URL(s) fixed summary' => $dry_run_message, - ); - } else { - $success_message = sprintf( '%s/%s insecure URLs fixed in post %s.', $count, count( $urls ), $post_id ); - WP_CLI::debug( $success_message ); - $this->fixed_post_count[] = array( - 'URL(s) fixed summary' => $success_message, - ); - } - } - - /** - * Check if a SSL version of the URL exists. - * - * @param string $url URL to check for SSL version. - * - * @return bool - */ - protected function does_secure_content_exist( $url ) { - // Check if a https version of the URL exists. - $secure_version_exists = false; - $ssl = preg_replace( '/^http:/i', 'https:', $url ); - $response = wp_remote_get( $ssl ); - $response_code = wp_remote_retrieve_response_code( $response ); - - if ( 200 === $response_code ) { - $secure_version_exists = true; - } - - return $secure_version_exists; - } - - /** - * Create list of URLs. - * - * @param string $post_content Post Content. - * - * @return array - */ - protected function parse_content_for_insecure_urls( $post_content ) { - // Check all src and create an array of URLs to check https URL availability. - $src_urls = array(); - $src_regex = '/src="([^"]+)"/'; - preg_match_all( $src_regex, $post_content, $matches, PREG_SET_ORDER, 0 ); - - // If we have matches, loop through and get clean URL. - if ( ! empty( $matches ) ) { - foreach ( $matches as $match ) { - $matched_url = $match[1]; - $base_url = explode( '?', $matched_url )[0]; - if ( 'https://' !== substr( $base_url, 0, 8 ) ) { - $src_urls[] = $base_url; - } - } - } + $dry_run = Utils\get_flag_value( $assoc_args, 'dry-run', false ); - return $src_urls; + FixInsecureContent::get_instance()->fix( $include, $all, $post_type, $posts_per_page, $post_offset, $dry_run, $args ); } } diff --git a/insecure-content-warning.php b/insecure-content-warning.php index 33b1376..97768a6 100644 --- a/insecure-content-warning.php +++ b/insecure-content-warning.php @@ -26,6 +26,8 @@ require_once INSECURE_CONTENT_INC . 'assets.php'; require_once INSECURE_CONTENT_INC . 'rest.php'; +require_once INSECURE_CONTENT_INC . 'admin.php'; +require_once INSECURE_CONTENT_INC . '/classes/class-fixinsecurecontent.php'; if ( defined( 'WP_CLI' ) && WP_CLI ) { require_once INSECURE_CONTENT_INC . 'wp-cli/insecure-content-warning.php'; @@ -33,3 +35,4 @@ Assets\setup(); Rest\setup(); +Admin\setup(); diff --git a/package-lock.json b/package-lock.json index 6f69339..c3c5c59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -165,6 +165,23 @@ } } }, + "@babel/eslint-parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz", + "integrity": "sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==", + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + } + } + }, "@babel/generator": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", @@ -1533,6 +1550,25 @@ "glob-to-regexp": "^0.3.0" } }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "requires": { + "eslint-scope": "5.1.1" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4189,7 +4225,8 @@ "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", - "normalize-path": "~3.0.0" + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "dependencies": { "braces": { @@ -4206,7 +4243,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { "to-regex-range": "^5.0.1" } @@ -4224,23 +4260,13 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "requires": { - "picomatch": "^2.2.1" - } + "dev": true }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true, "requires": { "is-number": "^7.0.0" } @@ -6761,12 +6787,12 @@ } }, "eslint-plugin-jsdoc": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.2.0.tgz", - "integrity": "sha512-r8yRB6jGay9tJkx1BherKFtOkpDud086VZenUqZiZe0F7cD4OABhte0xcj3/7mXPuJbaou8WF3JzEtTdDnCzhA==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-25.0.0.tgz", + "integrity": "sha512-VWdWD6IRDUIVKXybDDKDhSMGH/gUSEbcjrOcCkhp2Dz/AiTGqaOu4DLtCKMd3aMXTSdjFoaCtW2ATcrunac+Yw==", "dev": true, "requires": { - "comment-parser": "^0.7.2", + "comment-parser": "^0.7.4", "debug": "^4.1.1", "jsdoctypeparser": "^6.1.0", "lodash": "^4.17.15", @@ -6792,12 +6818,6 @@ "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.1.tgz", "integrity": "sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w==", "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, @@ -6956,7 +6976,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "requires": { "estraverse": "^5.2.0" }, @@ -6964,16 +6983,14 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" } } }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", @@ -12255,7 +12272,8 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true }, "pify": { "version": "4.0.1", @@ -13808,6 +13826,15 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "redent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", @@ -14326,6 +14353,11 @@ "commander": "^2.8.1" } }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", diff --git a/package.json b/package.json index 409d201..5e21488 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "postenv:start": "./tests/bin/initialize.sh" }, "dependencies": { + "@babel/eslint-parser": "^7.21.3", "@wordpress/element": "^5.0.0", "normalize.css": "^8.0.1", "sprintf-js": "^1.1.1", @@ -65,7 +66,7 @@ "eslint-config-prettier": "^6.10.1", "eslint-loader": "^3.0.3", "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jsdoc": "^22.1.0", + "eslint-plugin-jsdoc": "^25.0.0", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.19.0", diff --git a/src/css/admin.css b/src/css/admin.css index dd4d548..cd07f0b 100644 --- a/src/css/admin.css +++ b/src/css/admin.css @@ -1,6 +1,10 @@ :root { --c-green: #228b22; --c-red: #950e0d; + --c-black: #0c0c0c; + --c-gray: #c6c7c8; + --c-cyan-light: #47a2c8; + --c-cyan-dark: #2e6fa5; } .insecure-warnings-panel { @@ -50,3 +54,30 @@ margin: 5px 0; padding: 5px; } + +#icw-fix-content-btn[disabled] { + pointer-events: none; +} + +.icw-fix-log { + align-content: start; + background: var(--c-black); + color: var(--c-gray); + font-family: monospace; + grid-auto-flow: column; + grid-template-columns: min-content auto; + height: 21em; + line-height: 2; + margin-top: 20px; + overflow-y: auto; + padding: 10px; + white-space: pre-wrap; + + & .warning { + color: var(--c-cyan-light); + } + + & .info { + color: var(--c-cyan-dark); + } +} diff --git a/src/js/admin.js b/src/js/admin.js new file mode 100644 index 0000000..d469a7f --- /dev/null +++ b/src/js/admin.js @@ -0,0 +1,132 @@ +import apiFetch from '@wordpress/api-fetch'; +import { __ } from '@wordpress/i18n'; + +const hideElement = (hide, element) => { + if (hide) { + element.classList.add('hidden'); + element.setAttribute('aria-hidden', 'true'); + } else { + element.classList.remove('hidden'); + element.setAttribute('aria-hidden', 'false'); + } +}; + +document.querySelector('#icw-fix-content-btn').addEventListener('click', async (event) => { + event.preventDefault(); + + const postSelection = document.querySelector('#icw-post-selection').value; + const postIds = document.querySelector('#icw-post-ids').value; + const postType = document.querySelector('#icw-post-type').value; + const batchSize = document.querySelector('#icw-batch-size').value; + const dryRun = document.querySelector('#icw-dry-run').checked; + const log = document.querySelector('#icw-fix-log'); + const loadingSpinner = document.querySelector('#icw-loading-spinner'); + const btn = document.querySelector('#icw-fix-content-btn'); + + const toggleSpinnerVisibility = () => { + loadingSpinner.classList.toggle('hidden'); + loadingSpinner.setAttribute('aria-hidden', 'true'); + }; + + const disableButton = (disable) => { + if (disable) { + btn.setAttribute('disabled', ''); + } else { + btn.removeAttribute('disabled'); + } + }; + + const setLogHTML = (html) => { + log.innerHTML = html; + + log.scrollTop = log.scrollHeight; + }; + + toggleSpinnerVisibility(); + disableButton(true); + setLogHTML(''); + hideElement(true, log); + + const count = await apiFetch({ + path: `/icw/v1/count-for-fix`, + method: 'POST', + data: { + postSelection, + postIds: postSelection === 'posts' ? postIds : false, + postType: postSelection === 'all_from_post_type' ? postType : '', + batchSize, + }, + }); + + const times = Math.ceil(count / batchSize); + + hideElement(false, log); + + const getProgressBar = (time, times, batchSize, count) => { + const progressBarMax = 50; + const percentage = time > 0 ? Math.ceil((100 / times) * time) : 0; + const value = (percentage * progressBarMax) / 100; + const completed = time !== times ? batchSize * time : count; + return `[${'#'.repeat(value)}${'-'.repeat( + progressBarMax - value, + )}] | ${percentage}% || ${completed}/${count} ${__( + 'posts', + 'insecure-content-warning', + )}`; + }; + + setLogHTML(getProgressBar(0, times, batchSize, count)); + let innerHTML = ''; + for (let i = 1; i <= times; i++) { + // eslint-disable-next-line no-await-in-loop + const data = await apiFetch({ + path: `/icw/v1/fix`, + method: 'POST', + data: { + postSelection, + postIds: postSelection === 'posts' ? postIds : false, + postType: postSelection === 'all_from_post_type' ? postType : '', + batchSize, + offset: (i - 1) * batchSize, + dryRun, + }, + }); + + innerHTML += data; + setLogHTML(innerHTML + getProgressBar(i, times, batchSize, count)); + } + + toggleSpinnerVisibility(); + disableButton(false); +}); + +document.querySelector('#icw-post-selection').addEventListener('change', () => { + const postSelection = document.querySelector('#icw-post-selection').value; + + const hidePostIdsRow = (hide) => { + const postIdsRow = document.querySelector('#icw-post-ids-row'); + hideElement(hide, postIdsRow); + }; + + const hidePostTypeRow = (hide) => { + const postTypeRow = document.querySelector('#icw-post-type-row'); + hideElement(hide, postTypeRow); + }; + + switch (postSelection) { + case 'all': + hidePostIdsRow(true); + hidePostTypeRow(true); + break; + case 'posts': + hidePostIdsRow(false); + hidePostTypeRow(true); + break; + case 'all_from_post_type': + hidePostTypeRow(false); + hidePostIdsRow(true); + break; + default: + break; + } +}); From 50b2b85bc9673c9763e68d0cbdba8d569be70bee Mon Sep 17 00:00:00 2001 From: Konstantinos Galanakis Date: Fri, 28 Apr 2023 17:56:28 +0300 Subject: [PATCH 02/37] Fix eslint errors --- dist/js/gutenberg.js | 2 +- src/js/classic-editor.js | 39 ++++++++++++++-------------- src/js/gutenberg.js | 26 +++++++++---------- src/js/utils/blur-insecure.js | 12 ++++----- src/js/utils/check-content.js | 46 ++++++++++++++++----------------- src/js/utils/find-elements.js | 14 +++++----- src/js/utils/gutenberg-check.js | 7 +++-- src/js/utils/gutenberg-scan.js | 3 +-- src/js/utils/replace.js | 6 ++--- 9 files changed, 72 insertions(+), 83 deletions(-) diff --git a/dist/js/gutenberg.js b/dist/js/gutenberg.js index 5119859..92ef13e 100644 --- a/dist/js/gutenberg.js +++ b/dist/js/gutenberg.js @@ -1 +1 @@ -!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=196)}([function(t,e){t.exports=window.jQuery},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(77),o=r.all;t.exports=r.IS_HTMLDDA?function(t){return"function"==typeof t||t===o}:function(t){return"function"==typeof t}},function(t,e,n){var r=n(35),o=Function.prototype,i=o.call,c=r&&o.bind.bind(i,i);t.exports=r?c:function(t){return function(){return i.apply(t,arguments)}}},function(t,e,n){var r=n(5),o=n(25),i=n(6),c=n(56),u=n(24),a=n(78),s=o("wks"),f=r.Symbol,l=f&&f.for,p=a?f:f&&f.withoutSetter||c;t.exports=function(t){if(!i(s,t)||!u&&"string"!=typeof s[t]){var e="Symbol."+t;u&&i(f,t)?s[t]=f[t]:s[t]=a&&l?l(e):p(e)}return s[t]}},function(t,e,n){(function(e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e&&e)||function(){return this}()||Function("return this")()}).call(this,n(118))},function(t,e,n){var r=n(3),o=n(26),i=r({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,e){return i(o(t),e)}},function(t,e,n){var r=n(5),o=n(45).f,i=n(27),c=n(14),u=n(55),a=n(81),s=n(68);t.exports=function(t,e){var n,f,l,p,v,d=t.target,h=t.global,y=t.stat;if(n=h?r:y?r[d]||u(d,{}):(r[d]||{}).prototype)for(f in e){if(p=e[f],l=t.dontCallGetSet?(v=o(n,f))&&v.value:n[f],!s(h?f:d+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;a(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),c(n,f,p,t)}}},function(t,e,n){var r=n(35),o=Function.prototype.call;t.exports=r?o.bind(o):function(){return o.apply(o,arguments)}},function(t,e,n){var r=n(1);t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e,n){var r=n(9),o=n(79),i=n(80),c=n(11),u=n(38),a=TypeError,s=Object.defineProperty,f=Object.getOwnPropertyDescriptor;e.f=r?i?function(t,e,n){if(c(t),e=u(e),c(n),"function"===typeof t&&"prototype"===e&&"value"in n&&"writable"in n&&!n.writable){var r=f(t,e);r&&r.writable&&(t[e]=n.value,n={configurable:"configurable"in n?n.configurable:r.configurable,enumerable:"enumerable"in n?n.enumerable:r.enumerable,writable:!1})}return s(t,e,n)}:s:function(t,e,n){if(c(t),e=u(e),c(n),o)try{return s(t,e,n)}catch(t){}if("get"in n||"set"in n)throw a("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(12),o=String,i=TypeError;t.exports=function(t){if(r(t))return t;throw i(o(t)+" is not an object")}},function(t,e,n){var r=n(2),o=n(77),i=o.all;t.exports=o.IS_HTMLDDA?function(t){return"object"==typeof t?null!==t:r(t)||t===i}:function(t){return"object"==typeof t?null!==t:r(t)}},function(t,e,n){var r=n(5),o=n(2),i=function(t){return o(t)?t:void 0};t.exports=function(t,e){return arguments.length<2?i(r[t]):r[t]&&r[t][e]}},function(t,e,n){var r=n(2),o=n(10),i=n(121),c=n(55);t.exports=function(t,e,n,u){u||(u={});var a=u.enumerable,s=void 0!==u.name?u.name:e;if(r(n)&&i(n,s,u),u.global)a?t[e]=n:c(e,n);else{try{u.unsafe?t[e]&&(a=!0):delete t[e]}catch(t){}a?t[e]=n:o.f(t,e,{value:n,enumerable:!1,configurable:!u.nonConfigurable,writable:!u.nonWritable})}return t}},function(t,e,n){var r=n(50),o=String;t.exports=function(t){if("Symbol"===r(t))throw TypeError("Cannot convert a Symbol value to a string");return o(t)}},function(t,e){t.exports=window.wp.data},function(t,e){t.exports=window.wp.i18n},function(t,e,n){var r=n(76),o=n(36);t.exports=function(t){return r(o(t))}},function(t,e,n){var r=n(3),o=r({}.toString),i=r("".slice);t.exports=function(t){return i(o(t),8,-1)}},function(t,e){t.exports=!1},function(t,e,n){"use strict";n(71),n(34);var r=n(0),o=n.n(r);e.a=function(){var t=o()(document.getElementById("wp-content-wrap"));(t.hasClass("tmce-active")||t.hasClass("tinymce-active")?o()("#content_ifr").contents().find(".js-icw-is-insecure"):o()("
").append(o.a.parseHTML(o()("#content").val())).find(".js-icw-is-insecure")).removeClass("js-icw-is-insecure"),o()(".js-icw-is-insecure").removeClass("js-icw-is-insecure")}},function(t,e,n){var r,o,i,c=n(122),u=n(5),a=n(12),s=n(27),f=n(6),l=n(54),p=n(40),v=n(41),d=u.TypeError,h=u.WeakMap;if(c||l.state){var y=l.state||(l.state=new h);y.get=y.get,y.has=y.has,y.set=y.set,r=function(t,e){if(y.has(t))throw d("Object already initialized");return e.facade=t,y.set(t,e),e},o=function(t){return y.get(t)||{}},i=function(t){return y.has(t)}}else{var g=p("state");v[g]=!0,r=function(t,e){if(f(t,g))throw d("Object already initialized");return e.facade=t,s(t,g,e),e},o=function(t){return f(t,g)?t[g]:{}},i=function(t){return f(t,g)}}t.exports={set:r,get:o,has:i,enforce:function(t){return i(t)?o(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!a(e)||(n=o(e)).type!==t)throw d("Incompatible receiver, "+t+" required");return n}}}},function(t,e,n){var r=n(3);t.exports=r({}.isPrototypeOf)},function(t,e,n){var r=n(52),o=n(1);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},function(t,e,n){var r=n(20),o=n(54);(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.26.1",mode:r?"pure":"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.26.1/LICENSE",source:"https://github.com/zloirock/core-js"})},function(t,e,n){var r=n(36),o=Object;t.exports=function(t){return o(r(t))}},function(t,e,n){var r=n(9),o=n(10),i=n(29);t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){t.exports=window.wp.element},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(83);t.exports=function(t){return r(t.length)}},function(t,e,n){var r,o=n(11),i=n(86),c=n(57),u=n(41),a=n(114),s=n(47),f=n(40),l=f("IE_PROTO"),p=function(){},v=function(t){return"