Skip to content
Open
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
25 changes: 20 additions & 5 deletions src/wp-includes/http.php
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,28 @@ function get_allowed_http_origins() {
$admin_origin = parse_url( admin_url() );
$home_origin = parse_url( home_url() );

// @todo Preserve port?
/*
* Preserve a non-default port so that origins served on a custom port
* (for example `http://example.com:8080`) are matched against the browser's
* `Origin` request header. The default HTTP and HTTPS ports (80 and 443) are
* omitted because browsers leave them out of the `Origin` header.
*/
$admin_host = $admin_origin['host'];
if ( isset( $admin_origin['port'] ) && 80 !== $admin_origin['port'] && 443 !== $admin_origin['port'] ) {
$admin_host .= ':' . $admin_origin['port'];
}

$home_host = $home_origin['host'];
if ( isset( $home_origin['port'] ) && 80 !== $home_origin['port'] && 443 !== $home_origin['port'] ) {
$home_host .= ':' . $home_origin['port'];
}

$allowed_origins = array_unique(
array(
'http://' . $admin_origin['host'],
'https://' . $admin_origin['host'],
'http://' . $home_origin['host'],
'https://' . $home_origin['host'],
'http://' . $admin_host,
'https://' . $admin_host,
'http://' . $home_host,
'https://' . $home_host,
)
);

Expand Down
121 changes: 121 additions & 0 deletions tests/phpunit/tests/http/getAllowedHttpOrigins.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

/**
* Tests for get_allowed_http_origins().
*
* @group http
*
* @covers ::get_allowed_http_origins
*/
class Tests_HTTP_GetAllowedHttpOrigins extends WP_UnitTestCase {

/**
* Backup of the `home` option.
*
* @var string
*/
private $original_home;

/**
* Backup of the `siteurl` option.
*
* @var string
*/
private $original_siteurl;

public function set_up() {
parent::set_up();
$this->original_home = get_option( 'home' );
$this->original_siteurl = get_option( 'siteurl' );
}

public function tear_down() {
update_option( 'home', $this->original_home );
update_option( 'siteurl', $this->original_siteurl );
parent::tear_down();
}

/**
* A non-default port must be preserved so that an origin served on a custom
* port (for example a local or staging install) is matched.
*
* @ticket 65522
*/
public function test_non_default_port_is_preserved() {
update_option( 'home', 'http://example.com:8080' );
update_option( 'siteurl', 'http://example.com:8080' );

$origins = get_allowed_http_origins();

$this->assertContains( 'http://example.com:8080', $origins );
$this->assertContains( 'https://example.com:8080', $origins );
$this->assertNotContains( 'http://example.com', $origins );
}

/**
* A URL without a port must not gain a port suffix.
*
* @ticket 65522
*/
public function test_url_without_port_has_no_port_suffix() {
update_option( 'home', 'http://example.com' );
update_option( 'siteurl', 'http://example.com' );

$origins = get_allowed_http_origins();

$this->assertContains( 'http://example.com', $origins );
$this->assertContains( 'https://example.com', $origins );
$this->assertNotContains( 'http://example.com:80', $origins );
}

/**
* The default HTTP and HTTPS ports must be omitted, because browsers leave
* them out of the `Origin` request header. An explicit `:80` (or `:443`) in
* the site URL must therefore still produce a port-less origin so the check
* matches the value the browser actually sends.
*
* @ticket 65522
*
* @dataProvider data_default_ports
*
* @param string $url The site URL with an explicit default port.
*/
public function test_default_ports_are_omitted( $url ) {
update_option( 'home', $url );
update_option( 'siteurl', $url );

$origins = get_allowed_http_origins();

$this->assertContains( 'http://example.com', $origins, 'A port-less HTTP origin should be present.' );
$this->assertContains( 'https://example.com', $origins, 'A port-less HTTPS origin should be present.' );
$this->assertNotContains( 'http://example.com:80', $origins, 'The default HTTP port should be omitted.' );
$this->assertNotContains( 'https://example.com:443', $origins, 'The default HTTPS port should be omitted.' );
}

/**
* Data provider.
*
* @return array[]
*/
public function data_default_ports() {
return array(
'explicit default HTTP port' => array( 'http://example.com:80' ),
'explicit default HTTPS port' => array( 'https://example.com:443' ),
);
}

/**
* A custom port on the site URL must make a matching origin allowed.
*
* @ticket 65522
*
* @covers ::is_allowed_http_origin
*/
public function test_is_allowed_http_origin_matches_custom_port() {
update_option( 'home', 'http://example.com:8080' );
update_option( 'siteurl', 'http://example.com:8080' );

$this->assertSame( 'http://example.com:8080', is_allowed_http_origin( 'http://example.com:8080' ) );
$this->assertSame( '', is_allowed_http_origin( 'http://example.com' ), 'A port-less origin should not match a custom-port site.' );
}
}
Loading