From a89aef0f6e772151698160a09112a9276a83da9c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 24 Oct 2016 20:06:06 +0000 Subject: [PATCH] Customize: Harden url matching to account for varying ports and ensuring matching base pathname for allowed urls Fixes #38409. git-svn-id: https://develop.svn.wordpress.org/trunk@38890 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/js/customize-controls.js | 41 ++++++++++++------------- src/wp-includes/js/customize-base.js | 3 +- src/wp-includes/js/customize-preview.js | 25 ++++++++------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 24ad043004..18862ae8d6 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -4040,7 +4040,7 @@ // ssl certs. previewer.add( 'previewUrl', params.previewUrl ).setter( function( to ) { - var result, urlParser, newPreviewUrl, schemeMatchingPreviewUrl, queryParams; + var result = null, urlParser, queryParams, parsedAllowedUrl, parsedCandidateUrls = []; urlParser = document.createElement( 'a' ); urlParser.href = to; @@ -4062,31 +4062,30 @@ } } - newPreviewUrl = urlParser.href; - urlParser.protocol = previewer.scheme.get() + ':'; - schemeMatchingPreviewUrl = urlParser.href; + parsedCandidateUrls.push( urlParser ); + + // Prepend list with URL that matches the scheme/protocol of the iframe. + if ( previewer.scheme.get() + ':' !== urlParser.protocol ) { + urlParser = document.createElement( 'a' ); + urlParser.href = parsedCandidateUrls[0].href; + urlParser.protocol = previewer.scheme.get() + ':'; + parsedCandidateUrls.unshift( urlParser ); + } // Attempt to match the URL to the control frame's scheme // and check if it's allowed. If not, try the original URL. - $.each( [ schemeMatchingPreviewUrl, newPreviewUrl ], function( i, url ) { - $.each( previewer.allowedUrls, function( i, allowed ) { - var path; - - allowed = allowed.replace( /\/+$/, '' ); - path = url.replace( allowed, '' ); - - if ( 0 === url.indexOf( allowed ) && /^([/#?]|$)/.test( path ) ) { - result = url; - return false; + parsedAllowedUrl = document.createElement( 'a' ); + _.find( parsedCandidateUrls, function( parsedCandidateUrl ) { + return ! _.isUndefined( _.find( previewer.allowedUrls, function( allowedUrl ) { + parsedAllowedUrl.href = allowedUrl; + if ( urlParser.protocol === parsedAllowedUrl.protocol && urlParser.host === parsedAllowedUrl.host && 0 === parsedAllowedUrl.pathname.indexOf( urlParser.pathname ) ) { + result = parsedCandidateUrl.href; + return true; } - }); - if ( result ) { - return false; - } - }); + } ) ); + } ); - // If we found a matching result, return it. If not, bail. - return result ? result : null; + return result; }); previewer.bind( 'ready', previewer.ready ); diff --git a/src/wp-includes/js/customize-base.js b/src/wp-includes/js/customize-base.js index 11b868c85e..a8fa41d0de 100644 --- a/src/wp-includes/js/customize-base.js +++ b/src/wp-includes/js/customize-base.js @@ -654,7 +654,8 @@ window.wp = window.wp || {}; this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) { var urlParser = document.createElement( 'a' ); urlParser.href = to; - return urlParser.protocol + '//' + urlParser.hostname; + // Port stripping needed by IE since it adds to host but not to event.origin. + return urlParser.protocol + '//' + urlParser.host.replace( /:80$/, '' ); }); // first add with no value diff --git a/src/wp-includes/js/customize-preview.js b/src/wp-includes/js/customize-preview.js index 52de00df6a..f1bd852d0f 100644 --- a/src/wp-includes/js/customize-preview.js +++ b/src/wp-includes/js/customize-preview.js @@ -275,13 +275,13 @@ * @param {HTMLAnchorElement|HTMLAreaElement} element Link element. * @param {string} element.search Query string. * @param {string} element.pathname Path. - * @param {string} element.hostname Hostname. + * @param {string} element.host Host. * @param {object} [options] * @param {object} [options.allowAdminAjax=false] Allow admin-ajax.php requests. * @returns {boolean} Is appropriate for changeset link. */ api.isLinkPreviewable = function isLinkPreviewable( element, options ) { - var hasMatchingHost, urlParser, args; + var matchesAllowedUrl, parsedAllowedUrl, args; args = _.extend( {}, { allowAdminAjax: false }, options || {} ); @@ -294,15 +294,12 @@ return false; } - urlParser = document.createElement( 'a' ); - hasMatchingHost = ! _.isUndefined( _.find( api.settings.url.allowed, function( allowedUrl ) { - urlParser.href = allowedUrl; - if ( urlParser.hostname === element.hostname && urlParser.protocol === element.protocol ) { - return true; - } - return false; + parsedAllowedUrl = document.createElement( 'a' ); + matchesAllowedUrl = ! _.isUndefined( _.find( api.settings.url.allowed, function( allowedUrl ) { + parsedAllowedUrl.href = allowedUrl; + return parsedAllowedUrl.protocol === element.protocol && parsedAllowedUrl.host === element.host && 0 === element.pathname.indexOf( parsedAllowedUrl.pathname ); } ) ); - if ( ! hasMatchingHost ) { + if ( ! matchesAllowedUrl ) { return false; } @@ -331,7 +328,9 @@ * @access protected * * @param {HTMLAnchorElement|HTMLAreaElement} element Link element. - * @param {object} element.search Query string. + * @param {string} element.search Query string. + * @param {string} element.host Host. + * @param {string} element.protocol Protocol. * @returns {void} */ api.prepareLinkPreview = function prepareLinkPreview( element ) { @@ -348,7 +347,7 @@ } // Make sure links in preview use HTTPS if parent frame uses HTTPS. - if ( 'https' === api.preview.scheme.get() && 'http:' === element.protocol && -1 !== api.settings.url.allowedHosts.indexOf( element.hostname ) ) { + if ( 'https' === api.preview.scheme.get() && 'http:' === element.protocol && -1 !== api.settings.url.allowedHosts.indexOf( element.host ) ) { element.protocol = 'https:'; } @@ -496,7 +495,7 @@ urlParser.href = form.action; // Make sure forms in preview use HTTPS if parent frame uses HTTPS. - if ( 'https' === api.preview.scheme.get() && 'http:' === urlParser.protocol && -1 !== api.settings.url.allowedHosts.indexOf( urlParser.hostname ) ) { + if ( 'https' === api.preview.scheme.get() && 'http:' === urlParser.protocol && -1 !== api.settings.url.allowedHosts.indexOf( urlParser.host ) ) { urlParser.protocol = 'https:'; form.action = urlParser.href; }