Skip to content

Commit

Permalink
Merge pull request #5523 from wp-cli/fix/require-insecure-flag-for-ht…
Browse files Browse the repository at this point in the history
…tp-retry

Disable automatic retry by default on certificate validation error
  • Loading branch information
schlessera committed May 10, 2021
2 parents e067300 + d3da414 commit 6f4e5dd
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 77 deletions.
18 changes: 17 additions & 1 deletion php/class-wp-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,10 @@ public static function line( $message = '' ) {
* @param string $message Message to write to STDOUT.
*/
public static function log( $message ) {
if ( null === self::$logger ) {
return;
}

self::$logger->info( $message );
}

Expand Down Expand Up @@ -728,6 +732,10 @@ public static function log( $message ) {
* @return null
*/
public static function success( $message ) {
if ( null === self::$logger ) {
return;
}

self::$logger->success( $message );
}

Expand Down Expand Up @@ -805,6 +813,10 @@ public static function debug( $message, $group = false ) {
* @return null
*/
public static function warning( $message ) {
if ( null === self::$logger ) {
return;
}

self::$logger->warning( self::error_to_string( $message ) );
}

Expand Down Expand Up @@ -832,7 +844,7 @@ public static function warning( $message ) {
* @return null
*/
public static function error( $message, $exit = true ) {
if ( ! isset( self::get_runner()->assoc_args['completions'] ) ) {
if ( null !== self::$logger && ! isset( self::get_runner()->assoc_args['completions'] ) ) {
self::$logger->error( self::error_to_string( $message ) );
}

Expand Down Expand Up @@ -879,6 +891,10 @@ public static function halt( $return_code ) {
* @param array $message Multi-line error message to be displayed.
*/
public static function error_multi_line( $message_lines ) {
if ( null === self::$logger ) {
return;
}

if ( ! isset( self::get_runner()->assoc_args['completions'] ) && is_array( $message_lines ) ) {
self::$logger->error_multi_line( array_map( array( __CLASS__, 'error_to_string' ), $message_lines ) );
}
Expand Down
18 changes: 12 additions & 6 deletions php/commands/src/CLI_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ public function check_update( $_, $assoc_args ) {
* [--yes]
* : Do not prompt for confirmation.
*
* [--insecure]
* : Retry without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
*
* ## EXAMPLES
*
* # Update CLI.
Expand Down Expand Up @@ -331,11 +334,13 @@ public function update( $_, $assoc_args ) {
$options = [
'timeout' => 600, // 10 minutes ought to be enough for everybody.
'filename' => $temp,
'insecure' => (bool) Utils\get_flag_value( $assoc_args, 'insecure', false ),
];

Utils\http_request( 'GET', $download_url, null, $headers, $options );

$md5_response = Utils\http_request( 'GET', $md5_url );
unset( $options['filename'] );
$md5_response = Utils\http_request( 'GET', $md5_url, null, $headers, $options );
if ( '20' !== substr( $md5_response->status_code, 0, 2 ) ) {
WP_CLI::error( "Couldn't access md5 hash for release (HTTP code {$md5_response->status_code})." );
}
Expand Down Expand Up @@ -371,9 +376,9 @@ class_exists( '\cli\Colors' ); // This autoloads \cli\Colors - after we move the
WP_CLI::error( sprintf( 'Cannot move %s to %s', $temp, $old_phar ) );
}

if ( Utils\get_flag_value( $assoc_args, 'nightly' ) ) {
if ( Utils\get_flag_value( $assoc_args, 'nightly', false ) ) {
$updated_version = 'the latest nightly release';
} elseif ( Utils\get_flag_value( $assoc_args, 'stable' ) ) {
} elseif ( Utils\get_flag_value( $assoc_args, 'stable', false ) ) {
$updated_version = 'the latest stable release';
} else {
$updated_version = $newest['version'];
Expand All @@ -388,7 +393,8 @@ private function get_updates( $assoc_args ) {
$url = 'https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100';

$options = [
'timeout' => 30,
'timeout' => 30,
'insecure' => (bool) Utils\get_flag_value( $assoc_args, 'insecure', false ),
];

$headers = [
Expand Down Expand Up @@ -448,14 +454,14 @@ private function get_updates( $assoc_args ) {
}

foreach ( [ 'major', 'minor', 'patch' ] as $type ) {
if ( true === \WP_CLI\Utils\get_flag_value( $assoc_args, $type ) ) {
if ( true === Utils\get_flag_value( $assoc_args, $type ) ) {
return ! empty( $updates[ $type ] ) ? [ $updates[ $type ] ] : false;
}
}

if ( empty( $updates ) && preg_match( '#-alpha-(.+)$#', WP_CLI_VERSION, $matches ) ) {
$version_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/NIGHTLY_VERSION';
$response = Utils\http_request( 'GET', $version_url );
$response = Utils\http_request( 'GET', $version_url, null, [], $options );
if ( ! $response->success || 200 !== $response->status_code ) {
WP_CLI::error( sprintf( 'Failed to get current nightly version (HTTP code %d)', $response->status_code ) );
}
Expand Down
48 changes: 37 additions & 11 deletions php/utils-wp.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

namespace WP_CLI\Utils;

use ReflectionClass;
use ReflectionParameter;
use WP_CLI;
use WP_CLI\UpgraderSkin;

function wp_not_installed() {
global $wpdb, $table_prefix;
if ( ! is_blog_installed() && ! defined( 'WP_INSTALLING' ) ) {
Expand All @@ -20,13 +25,13 @@ function wp_not_installed() {
if ( count( $found_prefixes ) ) {
$prefix_list = implode( ', ', $found_prefixes );
$install_label = count( $found_prefixes ) > 1 ? 'installations' : 'installation';
\WP_CLI::error(
WP_CLI::error(
"The site you have requested is not installed.\n" .
"Your table prefix is '{$table_prefix}'. Found {$install_label} with table prefix: {$prefix_list}.\n" .
'Or, run `wp core install` to create database tables.'
);
} else {
\WP_CLI::error(
WP_CLI::error(
"The site you have requested is not installed.\n" .
'Run `wp core install` to create database tables.'
);
Expand All @@ -36,7 +41,7 @@ function wp_not_installed() {

// phpcs:disable WordPress.PHP.IniSet -- Intentional & correct usage.
function wp_debug_mode() {
if ( \WP_CLI::get_config( 'debug' ) ) {
if ( WP_CLI::get_config( 'debug' ) ) {
if ( ! defined( 'WP_DEBUG' ) ) {
define( 'WP_DEBUG', true );
}
Expand Down Expand Up @@ -87,7 +92,7 @@ function wp_die_handler( $message ) {

$message = wp_clean_error_message( $message );

\WP_CLI::error( $message );
WP_CLI::error( $message );
}

/**
Expand Down Expand Up @@ -115,7 +120,7 @@ function wp_clean_error_message( $message ) {
}

function wp_redirect_handler( $url ) {
\WP_CLI::warning( 'Some code is trying to do a URL redirect. Backtrace:' );
WP_CLI::warning( 'Some code is trying to do a URL redirect. Backtrace:' );

ob_start();
debug_print_backtrace();
Expand All @@ -130,12 +135,33 @@ function maybe_require( $since, $path ) {
}
}

function get_upgrader( $class ) {
function get_upgrader( $class, $insecure = false ) {
if ( ! class_exists( '\WP_Upgrader' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
}

return new $class( new \WP_CLI\UpgraderSkin() );
$uses_insecure_flag = false;

$reflection = new ReflectionClass( $class );
if ( $reflection ) {
$constructor = $reflection->getConstructor();
if ( $constructor ) {
$arguments = $constructor->getParameters();
/** @var ReflectionParameter $argument */
foreach ( $arguments as $argument ) {
if ( 'insecure' === $argument->name ) {
$uses_insecure_flag = true;
break;
}
}
}
}

if ( $uses_insecure_flag ) {
return new $class( new UpgraderSkin(), $insecure );
} else {
return new $class( new UpgraderSkin() );
}
}

/**
Expand All @@ -154,7 +180,7 @@ function get_plugin_name( $basename ) {
function is_plugin_skipped( $file ) {
$name = get_plugin_name( str_replace( WP_PLUGIN_DIR . '/', '', $file ) );

$skipped_plugins = \WP_CLI::get_runner()->config['skip-plugins'];
$skipped_plugins = WP_CLI::get_runner()->config['skip-plugins'];
if ( true === $skipped_plugins ) {
return true;
}
Expand All @@ -173,7 +199,7 @@ function get_theme_name( $path ) {
function is_theme_skipped( $path ) {
$name = get_theme_name( $path );

$skipped_themes = \WP_CLI::get_runner()->config['skip-themes'];
$skipped_themes = WP_CLI::get_runner()->config['skip-themes'];
if ( true === $skipped_themes ) {
return true;
}
Expand Down Expand Up @@ -328,7 +354,7 @@ function wp_get_table_names( $args, $assoc_args = array() ) {

// Abort if incompatible args supplied.
if ( get_flag_value( $assoc_args, 'base-tables-only' ) && get_flag_value( $assoc_args, 'views-only' ) ) {
\WP_CLI::error( 'You cannot supply --base-tables-only and --views-only at the same time.' );
WP_CLI::error( 'You cannot supply --base-tables-only and --views-only at the same time.' );
}

// Pre-load tables SQL query with Views restriction if needed.
Expand Down Expand Up @@ -414,7 +440,7 @@ function ( $v ) use ( $arg ) {
$args_tables = array_values( array_unique( $args_tables ) );
$tables = array_values( array_intersect( $tables, $args_tables ) );
if ( empty( $tables ) ) {
\WP_CLI::error( sprintf( "Couldn't find any tables matching: %s", implode( ' ', $args ) ) );
WP_CLI::error( sprintf( "Couldn't find any tables matching: %s", implode( ' ', $args ) ) );
}
}

Expand Down
25 changes: 21 additions & 4 deletions php/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ static function ( $matches ) use ( $file, $dir ) {
* @type bool|string $verify A boolean to use enable/disable SSL verification
* or string absolute path to CA cert to use.
* Defaults to detected CA cert bundled with the Requests library.
* @type bool $insecure Whether to retry automatically without certificate validation.
* }
* @return object
* @throws RuntimeException If the request failed.
Expand All @@ -756,11 +757,13 @@ function http_request( $method, $url, $data = null, $headers = array(), $options
Requests::register_autoloader();
}

$insecure = isset( $options['insecure'] ) && (bool) $options['insecure'];
$halt_on_error = ! isset( $options['halt_on_error'] ) || (bool) $options['halt_on_error'];
unset( $options['halt_on_error'] );

if ( ! isset( $options['verify'] ) ) {
// 'curl.cainfo' enforces the CA file to use, otherwise fallback to system-wide defaults then use the embedded CA file.
$options['verify'] = ini_get( 'curl.cainfo' ) ? ini_get( 'curl.cainfo' ) : true;
$options['verify'] = ! empty( ini_get( 'curl.cainfo' ) ) ? ini_get( 'curl.cainfo' ) : true;
}

try {
Expand All @@ -777,16 +780,30 @@ function http_request( $method, $url, $data = null, $headers = array(), $options
}
} catch ( Requests_Exception $ex ) {
// CURLE_SSL_CACERT_BADFILE only defined for PHP >= 7.
if ( 'curlerror' !== $ex->getType() || ! in_array( curl_errno( $ex->getData() ), array( CURLE_SSL_CONNECT_ERROR, CURLE_SSL_CERTPROBLEM, 77 /*CURLE_SSL_CACERT_BADFILE*/ ), true ) ) {
if (
! $insecure
||
'curlerror' !== $ex->getType()
||
! in_array( curl_errno( $ex->getData() ), [ CURLE_SSL_CONNECT_ERROR, CURLE_SSL_CERTPROBLEM, 77 /*CURLE_SSL_CACERT_BADFILE*/ ], true )
) {
$error_msg = sprintf( "Failed to get url '%s': %s.", $url, $ex->getMessage() );
if ( $halt_on_error ) {
WP_CLI::error( $error_msg );
}
throw new RuntimeException( $error_msg, null, $ex );
}
// Handle SSL certificate issues gracefully
WP_CLI::warning( sprintf( "Re-trying without verify after failing to get verified url '%s' %s.", $url, $ex->getMessage() ) );

$warning = sprintf(
"Re-trying without verify after failing to get verified url '%s' %s.",
$url,
$ex->getMessage()
);
WP_CLI::warning( $warning );

// Disable certificate validation for the next try.
$options['verify'] = false;

try {
return Requests::request( $url, $headers, $data, $method, $options );
} catch ( Requests_Exception $ex ) {
Expand Down

0 comments on commit 6f4e5dd

Please sign in to comment.