diff --git a/Puc/v5p0/DebugBar/Extension.php b/Puc/v5p0/DebugBar/Extension.php
index d085068..b7b27d1 100644
--- a/Puc/v5p0/DebugBar/Extension.php
+++ b/Puc/v5p0/DebugBar/Extension.php
@@ -66,14 +66,16 @@ if ( !class_exists(Extension::class, false) ):
* the update checking process works as expected.
*/
public function ajaxCheckNow() {
- if ( $_POST['uid'] !== $this->updateChecker->getUniqueName('uid') ) {
+ //phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is checked in preAjaxRequest().
+ if ( !isset($_POST['uid']) || ($_POST['uid'] !== $this->updateChecker->getUniqueName('uid')) ) {
return;
}
$this->preAjaxRequest();
$update = $this->updateChecker->checkForUpdates();
if ( $update !== null ) {
echo "An update is available:";
- echo '
', htmlentities(print_r($update, true)), '
';
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output.
+ echo '', esc_html(print_r($update, true)), '
';
} else {
echo 'No updates found.';
}
@@ -85,7 +87,7 @@ if ( !class_exists(Extension::class, false) ):
foreach (array_values($errors) as $num => $item) {
$wpError = $item['error'];
/** @var \WP_Error $wpError */
- printf('%d) %s
', $num + 1, esc_html($wpError->get_error_message()));
+ printf('%d) %s
', intval($num + 1), esc_html($wpError->get_error_message()));
echo '';
printf('- Error code:
%s ', esc_html($wpError->get_error_code()));
@@ -107,8 +109,8 @@ if ( !class_exists(Extension::class, false) ):
//Status code.
printf(
'- HTTP status:
%d %s ',
- wp_remote_retrieve_response_code($item['httpResponse']),
- wp_remote_retrieve_response_message($item['httpResponse'])
+ esc_html(wp_remote_retrieve_response_code($item['httpResponse'])),
+ esc_html(wp_remote_retrieve_response_message($item['httpResponse']))
);
//Headers.
@@ -147,7 +149,9 @@ if ( !class_exists(Extension::class, false) ):
}
check_ajax_referer('puc-ajax');
+ //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting -- Part of a debugging feature.
error_reporting(E_ALL);
+ //phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted
@ini_set('display_errors', 'On');
}
diff --git a/Puc/v5p0/DebugBar/PluginExtension.php b/Puc/v5p0/DebugBar/PluginExtension.php
index 975a02f..feaf2ff 100644
--- a/Puc/v5p0/DebugBar/PluginExtension.php
+++ b/Puc/v5p0/DebugBar/PluginExtension.php
@@ -20,14 +20,16 @@ if ( !class_exists(PluginExtension::class, false) ):
* Request plugin info and output it.
*/
public function ajaxRequestInfo() {
- if ( $_POST['uid'] !== $this->updateChecker->getUniqueName('uid') ) {
+ //phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is checked in preAjaxRequest().
+ if ( !isset($_POST['uid']) || ($_POST['uid'] !== $this->updateChecker->getUniqueName('uid')) ) {
return;
}
$this->preAjaxRequest();
$info = $this->updateChecker->requestInfo();
if ( $info !== null ) {
echo 'Successfully retrieved plugin info from the metadata URL:';
- echo '', htmlentities(print_r($info, true)), '
';
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output.
+ echo '', esc_html(print_r($info, true)), '
';
} else {
echo 'Failed to retrieve plugin info from the metadata URL.';
}
diff --git a/Puc/v5p0/Metadata.php b/Puc/v5p0/Metadata.php
index eab684b..92563fb 100644
--- a/Puc/v5p0/Metadata.php
+++ b/Puc/v5p0/Metadata.php
@@ -36,16 +36,18 @@ if ( !class_exists(Metadata::class, false) ):
/** @var \StdClass $apiResponse */
$apiResponse = json_decode($json);
if ( empty($apiResponse) || !is_object($apiResponse) ){
- $errorMessage = "Failed to parse update metadata. Try validating your .json file with http://jsonlint.com/";
+ $errorMessage = "Failed to parse update metadata. Try validating your .json file with https://jsonlint.com/";
do_action('puc_api_error', new WP_Error('puc-invalid-json', $errorMessage));
- trigger_error($errorMessage, E_USER_NOTICE);
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers.
+ trigger_error(esc_html($errorMessage), E_USER_NOTICE);
return false;
}
$valid = $target->validateMetadata($apiResponse);
if ( is_wp_error($valid) ){
do_action('puc_api_error', $valid);
- trigger_error($valid->get_error_message(), E_USER_NOTICE);
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers.
+ trigger_error(esc_html($valid->get_error_message()), E_USER_NOTICE);
return false;
}
diff --git a/Puc/v5p0/OAuthSignature.php b/Puc/v5p0/OAuthSignature.php
index 79b0d1e..9942709 100644
--- a/Puc/v5p0/OAuthSignature.php
+++ b/Puc/v5p0/OAuthSignature.php
@@ -26,10 +26,10 @@ if ( !class_exists(OAuthSignature::class, false) ):
$parameters = array();
//Parse query parameters.
- $query = parse_url($url, PHP_URL_QUERY);
+ $query = wp_parse_url($url, PHP_URL_QUERY);
if ( !empty($query) ) {
parse_str($query, $parsedParams);
- if ( is_array($parameters) ) {
+ if ( is_array($parsedParams) ) {
$parameters = $parsedParams;
}
//Remove the query string from the URL. We'll replace it later.
@@ -91,7 +91,8 @@ if ( !class_exists(OAuthSignature::class, false) ):
}
}
if ( $rand === null ) {
- $rand = mt_rand();
+ //phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand
+ $rand = function_exists('wp_rand') ? wp_rand() : mt_rand();
}
return md5($mt . '_' . $rand);
diff --git a/Puc/v5p0/Plugin/Ui.php b/Puc/v5p0/Plugin/Ui.php
index fce1750..880e31b 100644
--- a/Puc/v5p0/Plugin/Ui.php
+++ b/Puc/v5p0/Plugin/Ui.php
@@ -206,8 +206,9 @@ if ( !class_exists('Ui', false) ):
* You can change the result message by using the "puc_manual_check_message-$slug" filter.
*/
public function displayManualCheckResult() {
+ //phpcs:disable WordPress.Security.NonceVerification.Recommended -- Just displaying a message.
if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->updateChecker->slug) ) {
- $status = strval($_GET['puc_update_check_result']);
+ $status = sanitize_key($_GET['puc_update_check_result']);
$title = $this->updateChecker->getInstalledPackage()->getPluginTitle();
$noticeClass = 'updated notice-success';
$details = '';
@@ -223,16 +224,29 @@ if ( !class_exists('Ui', false) ):
$details = $this->formatManualCheckErrors(get_site_transient($this->manualCheckErrorTransient));
delete_site_transient($this->manualCheckErrorTransient);
} else {
- $message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status));
+ $message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), $status);
$noticeClass = 'error notice-error';
}
+
+ $message = esc_html($message);
+
+ //Plugins can replace the message with their own, including adding HTML.
+ $message = apply_filters(
+ $this->updateChecker->getUniqueName('manual_check_message'),
+ $message,
+ $status
+ );
+
printf(
'',
- $noticeClass,
- apply_filters($this->updateChecker->getUniqueName('manual_check_message'), $message, $status),
+ esc_attr($noticeClass),
+ //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Was escaped above, and plugins can add HTML.
+ $message,
+ //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Contains HTML. Content should already be escaped.
$details
);
}
+ //phpcs:enable
}
/**
@@ -259,8 +273,8 @@ if ( !class_exists('Ui', false) ):
/** @var \WP_Error $wpError */
$output .= sprintf(
$formatString,
- $wpError->get_error_message(),
- $wpError->get_error_code()
+ esc_html($wpError->get_error_message()),
+ esc_html($wpError->get_error_code())
);
}
if ( $showAsList ) {
diff --git a/Puc/v5p0/Plugin/UpdateChecker.php b/Puc/v5p0/Plugin/UpdateChecker.php
index 5f61f25..568f091 100644
--- a/Puc/v5p0/Plugin/UpdateChecker.php
+++ b/Puc/v5p0/Plugin/UpdateChecker.php
@@ -57,8 +57,8 @@ if ( !class_exists(UpdateChecker::class, false) ):
if ( $slugUsedBy ) {
$this->triggerError(sprintf(
'Plugin slug "%s" is already in use by %s. Slugs must be unique.',
- htmlentities($slug),
- htmlentities($slugUsedBy)
+ $slug,
+ $slugUsedBy
), E_USER_ERROR);
}
add_filter($slugCheckFilter, array($this, 'getAbsolutePath'));
diff --git a/Puc/v5p0/PucFactory.php b/Puc/v5p0/PucFactory.php
index 4b52a95..a02cc1f 100644
--- a/Puc/v5p0/PucFactory.php
+++ b/Puc/v5p0/PucFactory.php
@@ -105,13 +105,14 @@ if ( !class_exists(PucFactory::class, false) ):
$checkerClass = self::getCompatibleClassVersion($checkerClass);
if ( $checkerClass === null ) {
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error(
- sprintf(
+ esc_html(sprintf(
'PUC %s does not support updates for %ss %s',
- htmlentities(self::$latestCompatibleVersion),
+ self::$latestCompatibleVersion,
strtolower($type),
- $service ? ('hosted on ' . htmlentities($service)) : 'using JSON metadata'
- ),
+ $service ? ('hosted on ' . $service) : 'using JSON metadata'
+ )),
E_USER_ERROR
);
}
@@ -123,11 +124,12 @@ if ( !class_exists(PucFactory::class, false) ):
//VCS checker + an API client.
$apiClass = self::getCompatibleClassVersion($apiClass);
if ( $apiClass === null ) {
- trigger_error(sprintf(
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+ trigger_error(esc_html(sprintf(
'PUC %s does not support %s',
- htmlentities(self::$latestCompatibleVersion),
- htmlentities($service)
- ), E_USER_ERROR);
+ self::$latestCompatibleVersion,
+ $service
+ )), E_USER_ERROR);
}
return new $checkerClass(
@@ -251,8 +253,8 @@ if ( !class_exists(PucFactory::class, false) ):
$service = null;
//Which hosting service does the URL point to?
- $host = (string)(parse_url($metadataUrl, PHP_URL_HOST));
- $path = (string)(parse_url($metadataUrl, PHP_URL_PATH));
+ $host = (string)(wp_parse_url($metadataUrl, PHP_URL_HOST));
+ $path = (string)(wp_parse_url($metadataUrl, PHP_URL_PATH));
//Check if the path looks like "/user-name/repository".
//For GitLab.com it can also be "/user/group1/group2/.../repository".
diff --git a/Puc/v5p0/Scheduler.php b/Puc/v5p0/Scheduler.php
index 58c43ae..aef5914 100644
--- a/Puc/v5p0/Scheduler.php
+++ b/Puc/v5p0/Scheduler.php
@@ -57,7 +57,15 @@ if ( !class_exists(Scheduler::class, false) ):
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
//Randomly offset the schedule to help prevent update server traffic spikes. Without this
//most checks may happen during times of day when people are most likely to install new plugins.
- $firstCheckTime = time() - rand(0, max($this->checkPeriod * 3600 - 15 * 60, 1));
+ $upperLimit = max($this->checkPeriod * 3600 - 15 * 60, 1);
+ if ( function_exists('wp_rand') ) {
+ $randomOffset = wp_rand(0, $upperLimit);
+ } else {
+ //This constructor may be called before wp_rand() is available.
+ //phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand
+ $randomOffset = rand(0, $upperLimit);
+ }
+ $firstCheckTime = time() - $randomOffset;
$firstCheckTime = apply_filters(
$this->updateChecker->getUniqueName('first_check_time'),
$firstCheckTime
diff --git a/Puc/v5p0/UpdateChecker.php b/Puc/v5p0/UpdateChecker.php
index 3be594b..0da5db5 100644
--- a/Puc/v5p0/UpdateChecker.php
+++ b/Puc/v5p0/UpdateChecker.php
@@ -210,7 +210,7 @@ if ( !class_exists(UpdateChecker::class, false) ):
*/
public function allowMetadataHost($allow, $host) {
if ( $this->cachedMetadataHost === 0 ) {
- $this->cachedMetadataHost = parse_url($this->metadataUrl, PHP_URL_HOST);
+ $this->cachedMetadataHost = wp_parse_url($this->metadataUrl, PHP_URL_HOST);
}
if ( is_string($this->cachedMetadataHost) && (strtolower($host) === strtolower($this->cachedMetadataHost)) ) {
@@ -432,7 +432,8 @@ if ( !class_exists(UpdateChecker::class, false) ):
*/
public function triggerError($message, $errorType) {
if ( $this->isDebugModeEnabled() ) {
- trigger_error($message, $errorType);
+ //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Only happens in debug mode.
+ trigger_error(esc_html($message), $errorType);
}
}
diff --git a/Puc/v5p0/Vcs/BitBucketApi.php b/Puc/v5p0/Vcs/BitBucketApi.php
index e73ccf3..13677db 100644
--- a/Puc/v5p0/Vcs/BitBucketApi.php
+++ b/Puc/v5p0/Vcs/BitBucketApi.php
@@ -24,7 +24,7 @@ if ( !class_exists(BitBucketApi::class, false) ):
private $repository;
public function __construct($repositoryUrl, $credentials = array()) {
- $path = parse_url($repositoryUrl, PHP_URL_PATH);
+ $path = wp_parse_url($repositoryUrl, PHP_URL_PATH);
if ( preg_match('@^/?(?P[^/]+?)/(?P[^/#?&]+?)/?$@', $path, $matches) ) {
$this->username = $matches['username'];
$this->repository = $matches['repository'];
diff --git a/Puc/v5p0/Vcs/GitHubApi.php b/Puc/v5p0/Vcs/GitHubApi.php
index fc6208a..8682c8b 100644
--- a/Puc/v5p0/Vcs/GitHubApi.php
+++ b/Puc/v5p0/Vcs/GitHubApi.php
@@ -46,7 +46,7 @@ if ( !class_exists(GitHubApi::class, false) ):
private $downloadFilterAdded = false;
public function __construct($repositoryUrl, $accessToken = null) {
- $path = parse_url($repositoryUrl, PHP_URL_PATH);
+ $path = wp_parse_url($repositoryUrl, PHP_URL_PATH);
if ( preg_match('@^/?(?P[^/]+?)/(?P[^/#?&]+?)/?$@', $path, $matches) ) {
$this->userName = $matches['username'];
$this->repositoryName = $matches['repository'];
@@ -371,6 +371,7 @@ if ( !class_exists(GitHubApi::class, false) ):
*/
public function addHttpRequestFilter($result) {
if ( !$this->downloadFilterAdded && $this->isAuthenticationEnabled() ) {
+ //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args -- The callback doesn't change the timeout.
add_filter('http_request_args', array($this, 'setUpdateDownloadHeaders'), 10, 2);
add_action('requests-requests.before_redirect', array($this, 'removeAuthHeaderFromRedirects'), 10, 4);
$this->downloadFilterAdded = true;
diff --git a/Puc/v5p0/Vcs/GitLabApi.php b/Puc/v5p0/Vcs/GitLabApi.php
index 5de56a5..b43b4ce 100644
--- a/Puc/v5p0/Vcs/GitLabApi.php
+++ b/Puc/v5p0/Vcs/GitLabApi.php
@@ -41,18 +41,18 @@ if ( !class_exists(GitLabApi::class, false) ):
public function __construct($repositoryUrl, $accessToken = null, $subgroup = null) {
//Parse the repository host to support custom hosts.
- $port = parse_url($repositoryUrl, PHP_URL_PORT);
+ $port = wp_parse_url($repositoryUrl, PHP_URL_PORT);
if ( !empty($port) ) {
$port = ':' . $port;
}
- $this->repositoryHost = parse_url($repositoryUrl, PHP_URL_HOST) . $port;
+ $this->repositoryHost = wp_parse_url($repositoryUrl, PHP_URL_HOST) . $port;
if ( $this->repositoryHost !== 'gitlab.com' ) {
- $this->repositoryProtocol = parse_url($repositoryUrl, PHP_URL_SCHEME);
+ $this->repositoryProtocol = wp_parse_url($repositoryUrl, PHP_URL_SCHEME);
}
//Find the repository information
- $path = parse_url($repositoryUrl, PHP_URL_PATH);
+ $path = wp_parse_url($repositoryUrl, PHP_URL_PATH);
if ( preg_match('@^/?(?P[^/]+?)/(?P[^/#?&]+?)/?$@', $path, $matches) ) {
$this->userName = $matches['username'];
$this->repositoryName = $matches['repository'];