Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use WordPress\Plugin_Check\Traits\Amend_Check_Result;
use WordPress\Plugin_Check\Traits\License_Utils;
use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Traits\URL_Utils;
use WordPress\Plugin_Check\Traits\Version_Utils;

/**
Expand All @@ -26,6 +27,7 @@
use Amend_Check_Result;
use License_Utils;
use Stable_Check;
use URL_Utils;
use Version_Utils;

/**
Expand Down Expand Up @@ -135,22 +137,25 @@
'https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields',
7
);
} elseif ( preg_match( '/\/\/(example\.com|example\.net|example\.org)\//', $plugin_header['PluginURI'], $matches ) ) {
$this->add_result_error_for_file(
$result,
sprintf(
/* translators: 1: plugin header field, 2: domain */
__( 'The "%1$s" header in the plugin file is not valid. Discouraged domain "%2$s" found. This is the homepage of the plugin, which should be a unique URL, preferably on your own website. ', 'plugin-check' ),
esc_html( $labels['PluginURI'] ),
esc_html( $matches[1] ),
),
'plugin_header_invalid_plugin_uri_domain',
$plugin_main_file,
0,
0,
'https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields',
7
);
} else {
$matched_domain = $this->find_discouraged_domain( $plugin_header['PluginURI'] );
if ( $matched_domain ) {
$this->add_result_error_for_file(
$result,
sprintf(
/* translators: 1: plugin header field, 2: domain */
__( 'The "%1$s" header in the plugin file is not valid. Discouraged domain "%2$s" found. This is the homepage of the plugin, which should be a unique URL, preferably on your own website.', 'plugin-check' ),
esc_html( $labels['PluginURI'] ),
esc_html( $matched_domain )
),
'plugin_header_invalid_plugin_uri_domain',
$plugin_main_file,
0,
0,
'https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields',
7
);
}
}
}

Expand Down Expand Up @@ -242,6 +247,25 @@
'https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields',
7
);
} else {
$matched_domain = $this->find_discouraged_domain( $plugin_header['AuthorURI'] );
if ( $matched_domain ) {
$this->add_result_error_for_file(
$result,
sprintf(

Check warning on line 255 in includes/Checker/Checks/Plugin_Repo/Plugin_Header_Fields_Check.php

View check run for this annotation

Codecov / codecov/patch

includes/Checker/Checks/Plugin_Repo/Plugin_Header_Fields_Check.php#L253-L255

Added lines #L253 - L255 were not covered by tests
/* translators: 1: plugin header field, 2: domain */
__( 'The "%1$s" header in the plugin file is not valid. Discouraged domain "%2$s" found. This is the author\'s website or profile on another website.', 'plugin-check' ),
esc_html( $labels['AuthorURI'] ),
esc_html( $matched_domain )
),
'plugin_header_invalid_author_uri_domain',
$plugin_main_file,
0,
0,
'https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields',
7
);

Check warning on line 267 in includes/Checker/Checks/Plugin_Repo/Plugin_Header_Fields_Check.php

View check run for this annotation

Codecov / codecov/patch

includes/Checker/Checks/Plugin_Repo/Plugin_Header_Fields_Check.php#L257-L267

Added lines #L257 - L267 were not covered by tests
}
}
}

Expand Down Expand Up @@ -494,28 +518,6 @@
}
}

/**
* Checks if URL is valid.
*
* @since 1.2.0
*
* @param string $url URL.
* @return bool true if the URL is valid, otherwise false.
*/
private function is_valid_url( $url ) {
if ( filter_var( $url, FILTER_VALIDATE_URL ) !== $url || ! str_starts_with( $url, 'http' ) ) {
return false;
}

// Detect duplicated protocol (e.g., "https://http://example.com/").
$parsed_url = wp_parse_url( $url );
if ( isset( $parsed_url['scheme'] ) && str_contains( substr( $url, strlen( $parsed_url['scheme'] ) + 3 ), '://' ) ) {
return false;
}

return true;
}

/**
* Parses the plugin contents to retrieve plugin's metadata.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use WordPress\Plugin_Check\Traits\Find_Readme;
use WordPress\Plugin_Check\Traits\License_Utils;
use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Traits\URL_Utils;
use WordPress\Plugin_Check\Traits\Version_Utils;
use WordPressdotorg\Plugin_Directory\Readme\Parser as DotorgParser;

Expand All @@ -31,6 +32,7 @@ class Plugin_Readme_Check extends Abstract_File_Check {
use Find_Readme;
use Stable_Check;
use License_Utils;
use URL_Utils;
use Version_Utils;

/**
Expand Down Expand Up @@ -678,7 +680,7 @@ private function check_for_donate_link( Check_Result $result, string $readme_fil
return;
}

if ( ! ( filter_var( $donate_link, FILTER_VALIDATE_URL ) === $donate_link && str_starts_with( $donate_link, 'http' ) ) ) {
if ( ! $this->is_valid_url( $donate_link ) ) {
$this->add_result_warning_for_file(
$result,
sprintf(
Expand Down
115 changes: 115 additions & 0 deletions includes/Traits/URL_Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* Trait WordPress\Plugin_Check\Traits\URL_Utils
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Traits;

/**
* Trait for URL utilities.
*
* @since 1.6.0
*/
trait URL_Utils {

/**
* Checks if URL is valid.
*
* @since 1.6.0
*
* @param string $url URL.
* @return bool true if the URL is valid, otherwise false.
*/
protected function is_valid_url( string $url ): bool {
if ( filter_var( $url, FILTER_VALIDATE_URL ) !== $url || ! str_starts_with( $url, 'http' ) ) {
return false;
}

// Detect duplicated protocol (e.g., "https://http://example.com/").
$parsed_url = wp_parse_url( $url );

if ( isset( $parsed_url['scheme'] ) && str_contains( substr( $url, strlen( $parsed_url['scheme'] ) + 3 ), '://' ) ) {
return false;
}

return true;
}

/**
* Finds and returns the discouraged domain matched in the URL's host, or null if none.
*
* @since 1.6.0
*
* @param string $url The URL to check.
* @return string|null The matched discouraged domain, or null if none matched.
*/
protected function find_discouraged_domain( string $url ) {
$discouraged_domains = $this->get_discouraged_domains();

if ( empty( $discouraged_domains ) ) {
return null;

Check warning on line 52 in includes/Traits/URL_Utils.php

View check run for this annotation

Codecov / codecov/patch

includes/Traits/URL_Utils.php#L52

Added line #L52 was not covered by tests
}

$parsed_url = wp_parse_url( $url );

if ( empty( $parsed_url['host'] ) ) {
return null;

Check warning on line 58 in includes/Traits/URL_Utils.php

View check run for this annotation

Codecov / codecov/patch

includes/Traits/URL_Utils.php#L58

Added line #L58 was not covered by tests
}

$host = strtolower( rtrim( $parsed_url['host'], '.' ) );

foreach ( $discouraged_domains as $domain ) {
$domain = strtolower( rtrim( $domain, '.' ) );
if (
$host === $domain ||
( strlen( $host ) > strlen( $domain ) && substr( $host, -strlen( $domain ) - 1 ) === '.' . $domain )
) {
return $domain;
}
}

return null;
}

/**
* Checks if URL has discouraged domain.
*
* @since 1.6.0
*
* @param string $url The URL to check.
* @return bool True if the URL has a discouraged domain, false otherwise.
*/
protected function has_discouraged_domain( $url ) {
return null !== $this->find_discouraged_domain( $url );
}

/**
* Returns discouraged domains.
*
* @since 1.6.0
*
* @return array Discouraged domains.
*/
private function get_discouraged_domains() {
$discouraged_domains = array(
'example.com',
'example.net',
'example.org',
'yourdomain.com',
'yourwebsite.com',
);

/**
* Filter the list of discouraged domains.
*
* @since 1.6.0
*
* @param array $discouraged_domains Array of discouraged domains.
*/
$discouraged_domains = (array) apply_filters( 'wp_plugin_check_discouraged_domains', $discouraged_domains );

return $discouraged_domains;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -168,21 +168,4 @@ public function test_run_without_errors_requires_at_least_latest_version() {

$this->assertEmpty( $errors );
}

public function test_run_with_errors_duplicated_protocol_is_valid_url() {
$check = new Plugin_Header_Fields_Check();
$check_context = new Check_Context( UNIT_TESTS_PLUGIN_DIR . 'test-plugin-header-fields-duplicated-protocol-with-errors/load.php' );
$check_result = new Check_Result( $check_context );

$check->run( $check_result );

$errors = $check_result->get_errors();

$filtered_items = wp_list_filter( $errors['load.php'][0][0], array( 'code' => 'plugin_header_invalid_author_uri' ) );
$filtered_items = array_values( $filtered_items );

$this->assertCount( 1, $filtered_items );
$this->assertStringContainsString( 'Author URI', $filtered_items[0]['message'] );
$this->assertStringContainsString( 'is not valid', $filtered_items[0]['message'] );
}
}
59 changes: 59 additions & 0 deletions tests/phpunit/tests/Traits/URL_Utils_Tests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* Tests for the URL_Utils trait.
*
* @package plugin-check
*/

use WordPress\Plugin_Check\Traits\URL_Utils;

class URL_Utils_Tests extends WP_UnitTestCase {

use URL_Utils;

/**
* @dataProvider data_url_items
*/
public function test_url_validation( $url, $is_valid ) {
$result = $this->is_valid_url( $url );
$this->assertSame( $is_valid, $result );
}

public function data_url_items() {
return array(
array( 'http://example.com/', true ),
array( 'https://www.example.com', true ),
array( 'https://example.com/page.html', true ),
array( 'https://http://example.com/', false ),
array( 'ftp://example.com/file.txt', false ),
);
}

/**
* @dataProvider data_discouraged_domain_urls
*/
public function test_has_discouraged_domain( $url, $expected ) {
$result = $this->has_discouraged_domain( $url );
$this->assertSame( $expected, $result );
}

public function data_discouraged_domain_urls() {
return array(
array( 'http://example.com/', true ),
array( 'https://example.com', true ),
array( 'https://example.org', true ),
array( 'http://example.net/page', true ),
array( 'http://yourwebsite.com', true ),
array( 'http://sub.example.com/', true ),
array( 'http://www.example.com/', true ),
array( 'https://www.example.com/', true ),
array( 'https://www.example.org', true ),
array( 'http://www.example.net/page', true ),
array( 'http://www.yourwebsite.com', true ),
array( 'http://notexample.com/', false ),
array( 'http://example.co.uk/', false ),
array( 'http://myexample.com/', false ),
array( 'http://sub.notexample.com/', false ),
);
}
}
Loading