updated plugin Jetpack Protect
version 2.0.0
This commit is contained in:
@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.15.0] - 2024-01-18
|
||||
### Added
|
||||
- Adding support for IDC when site URL is an IP address. [#34753]
|
||||
|
||||
## [0.14.1] - 2024-01-04
|
||||
### Changed
|
||||
- Updated package dependencies. [#34815]
|
||||
|
||||
## [0.14.0] - 2023-12-06
|
||||
### Added
|
||||
- Send a verifcation secret when URL is IP. [#34436]
|
||||
|
||||
### Changed
|
||||
- Updated package dependencies. [#34416]
|
||||
|
||||
## [0.13.0] - 2023-12-03
|
||||
### Added
|
||||
- Store for persistent blog ID for multi-URL purposes. [#34262]
|
||||
|
||||
### Changed
|
||||
- Updated package dependencies. [#34411]
|
||||
|
||||
## [0.12.1] - 2023-11-24
|
||||
|
||||
## [0.12.0] - 2023-11-20
|
||||
### Added
|
||||
- Added idc query argument to url for tracking multisite idcs. [#34090]
|
||||
|
||||
### Changed
|
||||
- Replaced usage of strpos() with str_starts_with(). [#34135]
|
||||
- Updated required PHP version to >= 7.0. [#34192]
|
||||
|
||||
## [0.11.3] - 2023-11-14
|
||||
### Changed
|
||||
- Updated package dependencies. [#34093]
|
||||
|
||||
## [0.11.2] - 2023-11-03
|
||||
|
||||
## [0.11.1] - 2023-10-19
|
||||
### Changed
|
||||
- Updated package dependencies. [#33687]
|
||||
@ -435,6 +473,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Updated package dependencies.
|
||||
- Use Connection/Urls for home_url and site_url functions migrated from Sync.
|
||||
|
||||
[0.15.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.14.1...v0.15.0
|
||||
[0.14.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.14.0...v0.14.1
|
||||
[0.14.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.13.0...v0.14.0
|
||||
[0.13.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.12.1...v0.13.0
|
||||
[0.12.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.12.0...v0.12.1
|
||||
[0.12.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.11.3...v0.12.0
|
||||
[0.11.3]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.11.2...v0.11.3
|
||||
[0.11.2]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.11.1...v0.11.2
|
||||
[0.11.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.11.0...v0.11.1
|
||||
[0.11.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.10.7...v0.11.0
|
||||
[0.10.7]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.10.6...v0.10.7
|
||||
|
@ -1 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '40e79e96702db6f6da6d');
|
||||
<?php return array('dependencies' => array('react', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '2770a423a89e22f7aed3');
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,14 +4,15 @@
|
||||
"type": "jetpack-library",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"automattic/jetpack-connection": "^1.58.2",
|
||||
"automattic/jetpack-constants": "^1.6.23",
|
||||
"automattic/jetpack-status": "^1.18.5",
|
||||
"automattic/jetpack-logo": "^1.6.3",
|
||||
"automattic/jetpack-assets": "^1.18.13"
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-connection": "^2.2.0",
|
||||
"automattic/jetpack-constants": "^2.0.0",
|
||||
"automattic/jetpack-status": "^2.0.2",
|
||||
"automattic/jetpack-logo": "^2.0.0",
|
||||
"automattic/jetpack-assets": "^2.0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "^3.3.11",
|
||||
"automattic/jetpack-changelogger": "^4.0.5",
|
||||
"yoast/phpunit-polyfills": "1.1.0",
|
||||
"automattic/wordbless": "@dev"
|
||||
},
|
||||
@ -56,7 +57,7 @@
|
||||
"link-template": "https://github.com/Automattic/jetpack-identity-crisis/compare/v${old}...v${new}"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-trunk": "0.11.x-dev"
|
||||
"dev-trunk": "0.15.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
@ -27,7 +27,12 @@ class Identity_Crisis {
|
||||
/**
|
||||
* Package Version
|
||||
*/
|
||||
const PACKAGE_VERSION = '0.11.1';
|
||||
const PACKAGE_VERSION = '0.15.0';
|
||||
|
||||
/**
|
||||
* Persistent WPCOM blog ID that stays in the options after disconnect.
|
||||
*/
|
||||
const PERSISTENT_BLOG_ID_OPTION_NAME = 'jetpack_persistent_blog_id';
|
||||
|
||||
/**
|
||||
* Instance of the object.
|
||||
@ -87,9 +92,13 @@ class Identity_Crisis {
|
||||
add_filter( 'jetpack_remote_request_url', array( $this, 'add_idc_query_args_to_url' ) );
|
||||
|
||||
add_filter( 'jetpack_connection_validate_urls_for_idc_mitigation_response', array( static::class, 'add_secret_to_url_validation_response' ) );
|
||||
add_filter( 'jetpack_connection_validate_urls_for_idc_mitigation_response', array( static::class, 'add_ip_requester_to_url_validation_response' ) );
|
||||
|
||||
add_filter( 'jetpack_options', array( static::class, 'reverse_wpcom_urls_for_idc' ) );
|
||||
|
||||
add_filter( 'jetpack_register_request_body', array( static::class, 'register_request_body' ) );
|
||||
add_action( 'jetpack_site_registered', array( static::class, 'site_registered' ) );
|
||||
|
||||
$urls_in_crisis = self::check_identity_crisis();
|
||||
if ( false === $urls_in_crisis ) {
|
||||
return;
|
||||
@ -113,6 +122,8 @@ class Identity_Crisis {
|
||||
$connection->disconnect_site( false );
|
||||
}
|
||||
|
||||
delete_option( static::PERSISTENT_BLOG_ID_OPTION_NAME );
|
||||
|
||||
// Clear IDC options.
|
||||
self::clear_all_idc_options();
|
||||
}
|
||||
@ -199,10 +210,18 @@ class Identity_Crisis {
|
||||
|| self::validate_sync_error_idc_option() ) {
|
||||
return $url;
|
||||
}
|
||||
$home_url = Urls::home_url();
|
||||
$site_url = Urls::site_url();
|
||||
$hostname = wp_parse_url( $site_url, PHP_URL_HOST );
|
||||
|
||||
// If request is from an IP, make sure ip_requester option is set
|
||||
if ( self::url_is_ip( $hostname ) ) {
|
||||
self::maybe_update_ip_requester( $hostname );
|
||||
}
|
||||
|
||||
$query_args = array(
|
||||
'home' => Urls::home_url(),
|
||||
'siteurl' => Urls::site_url(),
|
||||
'home' => $home_url,
|
||||
'siteurl' => $site_url,
|
||||
);
|
||||
|
||||
if ( self::should_handle_idc() ) {
|
||||
@ -213,6 +232,10 @@ class Identity_Crisis {
|
||||
$query_args['migrate_for_idc'] = true;
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$query_args['multisite'] = true;
|
||||
}
|
||||
|
||||
return add_query_arg( $query_args, $url );
|
||||
}
|
||||
|
||||
@ -1337,4 +1360,153 @@ class Identity_Crisis {
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if URL is an IP.
|
||||
*
|
||||
* @param string $hostname The hostname to check.
|
||||
* @return bool
|
||||
*/
|
||||
public static function url_is_ip( $hostname = null ) {
|
||||
|
||||
if ( ! $hostname ) {
|
||||
$hostname = wp_parse_url( Urls::site_url(), PHP_URL_HOST );
|
||||
}
|
||||
|
||||
$is_ip = filter_var( $hostname, FILTER_VALIDATE_IP ) !== false ? $hostname : false;
|
||||
return $is_ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IDC-related data to the registration query.
|
||||
*
|
||||
* @param array $params The existing query params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function register_request_body( array $params ) {
|
||||
$persistent_blog_id = get_option( static::PERSISTENT_BLOG_ID_OPTION_NAME );
|
||||
if ( $persistent_blog_id ) {
|
||||
$params['persistent_blog_id'] = $persistent_blog_id;
|
||||
$params['url_secret'] = URL_Secret::create_secret( 'registration_request_url_secret_failed' );
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the necessary options when site gets registered.
|
||||
*
|
||||
* @param int $blog_id The blog ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function site_registered( $blog_id ) {
|
||||
update_option( static::PERSISTENT_BLOG_ID_OPTION_NAME, (int) $blog_id, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we need to update the ip_requester option.
|
||||
*
|
||||
* @param string $hostname The hostname to check.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_update_ip_requester( $hostname ) {
|
||||
// Check if transient exists
|
||||
$transient_key = ip2long( $hostname );
|
||||
if ( $transient_key && ! get_transient( 'jetpack_idc_ip_requester_' . $transient_key ) ) {
|
||||
self::set_ip_requester_for_idc( $hostname, $transient_key );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If URL is an IP, add the IP value to the ip_requester option with its expiry value.
|
||||
*
|
||||
* @param string $hostname The hostname to check.
|
||||
* @param int $transient_key The transient key.
|
||||
*/
|
||||
public static function set_ip_requester_for_idc( $hostname, $transient_key ) {
|
||||
// Check if option exists
|
||||
$data = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );
|
||||
|
||||
$ip_requester = array(
|
||||
'ip' => $hostname,
|
||||
'expires_at' => time() + 360,
|
||||
);
|
||||
|
||||
// If not set, initialize it
|
||||
if ( empty( $data ) ) {
|
||||
$data = array( $ip_requester );
|
||||
} else {
|
||||
$updated_data = array();
|
||||
$updated_value = false;
|
||||
|
||||
// Remove expired values and update existing IP
|
||||
foreach ( $data as $item ) {
|
||||
if ( time() > $item['expires_at'] ) {
|
||||
continue; // Skip expired IP
|
||||
}
|
||||
|
||||
if ( $item['ip'] === $hostname ) {
|
||||
$item['expires_at'] = time() + 360;
|
||||
$updated_value = true;
|
||||
}
|
||||
|
||||
$updated_data[] = $item;
|
||||
}
|
||||
|
||||
if ( ! $updated_value || empty( $updated_data ) ) {
|
||||
$updated_data[] = $ip_requester;
|
||||
}
|
||||
|
||||
$data = $updated_data;
|
||||
}
|
||||
|
||||
self::update_ip_requester( $data, $transient_key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ip_requester option and set a transient to expire in 5 minutes.
|
||||
*
|
||||
* @param array $data The data to be updated.
|
||||
* @param int $transient_key The transient key.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_ip_requester( $data, $transient_key ) {
|
||||
// Update the option
|
||||
$updated = Jetpack_Options::update_option( 'identity_crisis_ip_requester', $data );
|
||||
// Set a transient to expire in 5 minutes
|
||||
if ( $updated ) {
|
||||
$transient_name = 'jetpack_idc_ip_requester_' . $transient_key;
|
||||
set_transient( $transient_name, $data, 300 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds `ip_requester` to the `jetpack.idcUrlValidation` URL validation endpoint.
|
||||
*
|
||||
* @param array $response The enpoint response that we're modifying.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function add_ip_requester_to_url_validation_response( array $response ) {
|
||||
$requesters = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );
|
||||
if ( $requesters ) {
|
||||
// Loop through the requesters and add the IP to the response if it's not expired
|
||||
$i = 0;
|
||||
foreach ( $requesters as $ip ) {
|
||||
if ( $ip['expires_at'] > time() ) {
|
||||
$response['ip_requester'][] = $ip['ip'];
|
||||
}
|
||||
// Limit the response to five IPs
|
||||
$i = ++$i;
|
||||
if ( $i === 5 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,24 @@ class REST_Endpoints {
|
||||
'permission_callback' => array( static::class, 'url_secret_permission_check' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Fetch URL verification secret.
|
||||
register_rest_route(
|
||||
'jetpack/v4',
|
||||
'/identity-crisis/compare-url-secret',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( static::class, 'compare_url_secret' ),
|
||||
'permission_callback' => array( static::class, 'compare_url_secret_permission_check' ),
|
||||
'args' => array(
|
||||
'secret' => array(
|
||||
'description' => __( 'URL secret to compare to the ones stored in the database.', 'jetpack-idc' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,6 +237,31 @@ class REST_Endpoints {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint for comparing the existing secret.
|
||||
*
|
||||
* @param \WP_REST_Request $request The request sent to the WP REST API.
|
||||
*
|
||||
* @return WP_Error|\WP_REST_Response
|
||||
*/
|
||||
public static function compare_url_secret( $request ) {
|
||||
$match = false;
|
||||
|
||||
$storage = new URL_Secret();
|
||||
|
||||
if ( $storage->exists() ) {
|
||||
$remote_secret = $request->get_param( 'secret' );
|
||||
$match = $remote_secret && hash_equals( $storage->get_secret(), $remote_secret );
|
||||
}
|
||||
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'code' => 'success',
|
||||
'match' => $match,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify url_secret create/fetch permissions (valid blog token authentication).
|
||||
*
|
||||
@ -233,4 +276,20 @@ class REST_Endpoints {
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The endpoint is only available on non-connected sites.
|
||||
* use `/identity-crisis/url-secret` for connected sites.
|
||||
*
|
||||
* @return true|WP_Error
|
||||
*/
|
||||
public static function compare_url_secret_permission_check() {
|
||||
return ( new Connection_Manager() )->is_connected()
|
||||
? new WP_Error(
|
||||
'invalid_connection_status',
|
||||
esc_html__( 'The endpoint is not available on connected sites.', 'jetpack-idc' ),
|
||||
array( 'status' => 403 )
|
||||
)
|
||||
: true;
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ class UI {
|
||||
$priority1 = ( array_key_exists( 'priority', $c1 ) && (int) $c1['priority'] ) ? (int) $c1['priority'] : 10;
|
||||
$priority2 = ( array_key_exists( 'priority', $c2 ) && (int) $c2['priority'] ) ? (int) $c2['priority'] : 10;
|
||||
|
||||
return $priority1 > $priority2 ? 1 : -1;
|
||||
return $priority1 <=> $priority2;
|
||||
}
|
||||
);
|
||||
|
||||
@ -165,7 +165,7 @@ class UI {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) && 0 === strpos( filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ) ), $consumer['admin_page'] ) && strlen( $consumer['admin_page'] ) > $consumer_url_length ) {
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) && str_starts_with( filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ) ), $consumer['admin_page'] ) && strlen( $consumer['admin_page'] ) > $consumer_url_length ) {
|
||||
$consumer_chosen = $consumer;
|
||||
$consumer_url_length = strlen( $consumer['admin_page'] );
|
||||
}
|
||||
|
@ -131,4 +131,27 @@ class URL_Secret {
|
||||
private function generate_secret() {
|
||||
return wp_generate_password( 12, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate secret for response.
|
||||
*
|
||||
* @param string $flow used to tell which flow generated the exception.
|
||||
* @return string
|
||||
*/
|
||||
public static function create_secret( $flow = 'generating_secret_failed' ) {
|
||||
$secret = null;
|
||||
try {
|
||||
|
||||
$secret = new self();
|
||||
$secret->create();
|
||||
|
||||
if ( $secret->exists() ) {
|
||||
$secret = $secret->get_secret();
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
// Track the error and proceed.
|
||||
( new Tracking() )->record_user_event( $flow, array( 'current_url' => Urls::site_url() ) );
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user