Skip to content
2 changes: 1 addition & 1 deletion inc/traits/normalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ public function add_schema( $url ) {
$should_add_schema = substr( $schema_url, 0, strlen( '//' ) ) === '//';
}
if ( $should_add_schema ) {
$schema_url = is_ssl() ? 'https:' : 'http:' . $schema_url;
$schema_url = ( is_ssl() ? 'https:' : 'http:' ) . $schema_url;
}
return $schema_url;
}
Expand Down
8 changes: 7 additions & 1 deletion inc/url_replacer.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ public function build_url(
return $original_url;
}

if ( substr( $url, 0, 2 ) === '//' ) {
$url = ltrim( $url, '/' );
$url = sprintf( '%s://%s', is_ssl() ? 'https' : 'http', $url );
}

if ( ! $this->can_replace_url( $url ) ) {
return $original_url;
}
Expand All @@ -154,7 +159,8 @@ public function build_url(
$url = str_replace( array_keys( $this->site_mappings ), array_values( $this->site_mappings ), $url );
}
if ( substr( $url, 0, 2 ) === '//' ) {
$url = sprintf( '%s:%s', is_ssl() ? 'https' : 'http', $url );
$url = ltrim( $url, '/' );
$url = sprintf( '%s://%s', is_ssl() ? 'https' : 'http', $url );
}
$normalized_ext = strtolower( $ext );
if ( isset( Optml_Config::$image_extensions[ $normalized_ext ] ) ) {
Expand Down
37 changes: 37 additions & 0 deletions tests/test-generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,43 @@ function test_get_unoptimized_url_edge_cases() {
$this->assertEquals( $uploaded_url, $result );
}

/**
* Test add_schema() prepends http: for protocol-relative URLs on non-SSL.
*/
function test_add_schema_non_ssl() {
$this->assertEquals( 'http://example.org/image.jpg', $this->add_schema( '//example.org/image.jpg' ) );
}

/**
* Test add_schema() prepends https: for protocol-relative URLs on SSL.
* Regression: operator-precedence bug caused https: to be returned bare instead of https://example.org/...
*/
function test_add_schema_ssl() {
$had_https = array_key_exists( 'HTTPS', $_SERVER );
$previous_https = $had_https ? $_SERVER['HTTPS'] : null;

try {
$_SERVER['HTTPS'] = 'on';
$result = $this->add_schema( '//example.org/image.jpg' );

$this->assertEquals( 'https://example.org/image.jpg', $result );
} finally {
if ( $had_https ) {
$_SERVER['HTTPS'] = $previous_https;
} else {
unset( $_SERVER['HTTPS'] );
}
}
}

/**
* Test add_schema() leaves already-schemed URLs unchanged.
*/
function test_add_schema_already_schemed() {
$this->assertEquals( 'http://example.org/image.jpg', $this->add_schema( 'http://example.org/image.jpg' ) );
$this->assertEquals( 'https://example.org/image.jpg', $this->add_schema( 'https://example.org/image.jpg' ) );
}

/**
* Test get_unoptimized_url with custom domain configuration.
*/
Expand Down
22 changes: 22 additions & 0 deletions tests/test-replacer.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,28 @@ public function test_replacement_without_scheme() {
$this->assertStringContainsString( 'http://www.example.org', $replaced_content );
}

/**
* Regression test: build_url() must normalize protocol-relative and
* triple-slash URLs to http(s)://host/path — never produce https:///host/path.
*/
public function test_build_url_normalizes_triple_slash() {
// Protocol-relative URL (//host/path) should be normalized correctly.
$url = '//www.example.org/wp-content/uploads/2018/05/brands.png';
$result = Optml_Url_Replacer::instance()->build_url( $url );

$this->assertStringContainsString( 'i.optimole.com', $result );
$this->assertStringNotContainsString( ':///', $result );
$this->assertStringContainsString( 'http://www.example.org', $result );

// Triple-slash URL (///host/path) should also be normalized correctly.
$url = '///www.example.org/wp-content/uploads/2018/05/brands.png';
$result = Optml_Url_Replacer::instance()->build_url( $url );

$this->assertStringContainsString( 'i.optimole.com', $result );
$this->assertStringNotContainsString( ':///', $result );
$this->assertStringContainsString( 'http://www.example.org', $result );
}

public function test_non_allowed_extensions() {
$replaced_content = Optml_Manager::instance()->replace_content( ( self::CSS_STYLE . self::IMG_TAGS . self::WRONG_EXTENSION ) );
$this->assertStringContainsString( 'i.optimole.com', $replaced_content );
Expand Down
Loading