diff --git a/src/php/cloud/class-cloud-search-list-table.php b/src/php/cloud/class-cloud-search-list-table.php index af1051c7..40cad9d4 100644 --- a/src/php/cloud/class-cloud-search-list-table.php +++ b/src/php/cloud/class-cloud-search-list-table.php @@ -101,15 +101,33 @@ public function process_actions() { ); // Check request is coming from the cloud search page. - if ( isset( $_REQUEST['type'] ) && 'cloud_search' === $_REQUEST['type'] ) { - if ( isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) { - cloud_lts_process_download_action( - sanitize_key( wp_unslash( $_REQUEST['action'] ) ), - sanitize_key( wp_unslash( $_REQUEST['source'] ) ), - sanitize_key( wp_unslash( $_REQUEST['snippet'] ) ), - ); - } + if ( ! isset( $_REQUEST['type'] ) || 'cloud_search' !== sanitize_key( wp_unslash( $_REQUEST['type'] ) ) ) { + return; + } + + if ( ! isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) { + return; + } + + $action = sanitize_key( wp_unslash( $_REQUEST['action'] ) ); + $source = sanitize_key( wp_unslash( $_REQUEST['source'] ) ); + $snippet_id = absint( wp_unslash( $_REQUEST['snippet'] ) ); + + if ( ! in_array( $action, [ 'download', 'update' ], true ) ) { + return; + } + + if ( ! $snippet_id ) { + return; } + + check_admin_referer( cloud_lts_get_snippet_action_nonce_action( $action, $snippet_id, $source ) ); + + cloud_lts_process_download_action( + $action, + $source, + (string) $snippet_id, + ); } /** diff --git a/src/php/cloud/list-table-shared-ops.php b/src/php/cloud/list-table-shared-ops.php index 7126f28e..0bae9a1b 100644 --- a/src/php/cloud/list-table-shared-ops.php +++ b/src/php/cloud/list-table-shared-ops.php @@ -9,6 +9,19 @@ use function Code_Snippets\code_snippets; +/** + * Build the nonce action string for cloud snippet state-changing operations. + * + * @param string $action Action - 'download' or 'update'. + * @param int $snippet_id Cloud snippet ID. + * @param string $source Source - 'search' or 'cloud'. + * + * @return string + */ +function cloud_lts_get_snippet_action_nonce_action( string $action, int $snippet_id, string $source ): string { + return sprintf( 'cloud-snippet-action|%s|%s|%d', $action, $source, $snippet_id ); +} + /** * Display a hidden input field for a certain column and snippet value. * @@ -82,15 +95,19 @@ function cloud_lts_build_action_links( Cloud_Snippet $cloud_snippet, string $sou $link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $cloud_snippet ); $is_licensed = code_snippets()->licensing->is_licensed(); $download = $is_licensed || ! in_array( $lang, [ 'css', 'js' ], true ); + $snippet_id = (int) $cloud_snippet->id; if ( $link ) { if ( $is_licensed && $link->update_available ) { - $update_url = add_query_arg( - [ - 'action' => 'update', - 'snippet' => $cloud_snippet->id, - 'source' => $source, - ] + $update_url = wp_nonce_url( + add_query_arg( + [ + 'action' => 'update', + 'snippet' => $snippet_id, + 'source' => $source, + ] + ), + cloud_lts_get_snippet_action_nonce_action( 'update', $snippet_id, $source ) ); return sprintf( '