From 21072195c00b3b58689ab943eebde82465e3c90b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 2 Jul 2017 00:36:19 +0900 Subject: [PATCH] Prevent overwriting jQuery and other common scripts - Block third-party programs trying to load their own version of jQuery - Block obsolete scripts such as xe.css and xe.js - Block minified versions of the above, too - Increase blocking index threshold from 1,500,000 to 1,500,000,000 - Add unit tests for script blocking --- classes/display/HTMLDisplayHandler.php | 38 +++++++++++++------ .../FrontEndFileHandler.class.php | 25 +++++++++--- .../unit/classes/FrontEndFileHandlerTest.php | 28 ++++++++++++++ 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index d8e40e934..228ea5ad1 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -3,19 +3,33 @@ class HTMLDisplayHandler { + /** + * jQuery versions + */ + const JQUERY_V1 = '1.11.3'; + const JQUERY_V2 = '2.1.4'; + /** * Reserved scripts */ public static $reservedCSS = '@\bcommon/css/(?:xe|rhymix|mobile)\.(?:min\.)?(?:s?css|less)$@'; public static $reservedJS = '@\bcommon/js/(?:jquery(?:-[123][0-9.x-]+)?|xe?|common|js_app|xml_handler|xml_js_filter)\.(?:min\.)?js$@'; - + + /** + * List of scripts to block loading + */ + public static $blockedScripts = array( + '@\bcommon/(?:css|js)/xe(?:\.min)?\.(?:css|js)$@', + '@\bj[Qq]uery(?:-[0-9]+(?:\.[0-9x]+)*)?(?:\.min)?\.js$@', + ); + /** * Replacement table for XE compatibility */ public static $replacements = array( '@\bcommon/xeicon/@' => 'common/css/xeicon/', ); - + /** * Produce HTML compliant content given a module object.\n * @param ModuleObject $oModule the module object @@ -546,7 +560,7 @@ class HTMLDisplayHandler private function _loadMobileJSCSS() { $this->_loadCommonJSCSS(); - Context::loadFile(array('./common/css/mobile.css', '', '', -1500000), true); + Context::loadFile(array('./common/css/mobile.css', '', '', -1500000000), true); } /** @@ -554,23 +568,23 @@ class HTMLDisplayHandler */ private function _loadCommonJSCSS() { - Context::loadFile(array('./common/css/rhymix.scss', '', '', -1600000), true); + Context::loadFile(array('./common/css/rhymix.scss', '', '', -1600000000), true); $original_file_list = array('x', 'common', 'js_app', 'xml_handler', 'xml_js_filter'); - $jquery_version = preg_match('/MSIE [5-8]\./', $_SERVER['HTTP_USER_AGENT']) ? '1.11.3' : '2.1.4'; + $jquery_version = preg_match('/MSIE [5-8]\./', $_SERVER['HTTP_USER_AGENT']) ? self::JQUERY_V1 : self::JQUERY_V2; if(config('view.minify_scripts') === 'none') { - Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.js', 'head', '', -1730000), true); - Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.js', 'head', '', -1720000), true); + Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.js', 'head', '', -1730000000), true); + Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.js', 'head', '', -1720000000), true); foreach($original_file_list as $filename) { - Context::loadFile(array('./common/js/' . $filename . '.js', 'head', '', -1700000), true); + Context::loadFile(array('./common/js/' . $filename . '.js', 'head', '', -1710000000), true); } } else { - Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.min.js', 'head', '', -1730000), true); - Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.min.js', 'head', '', -1720000), true); + Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.min.js', 'head', '', -1730000000), true); + Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.min.js', 'head', '', -1720000000), true); $concat_target_filename = 'files/cache/assets/minified/rhymix.min.js'; if(file_exists(\RX_BASEDIR . $concat_target_filename)) { @@ -582,14 +596,14 @@ class HTMLDisplayHandler } if($concat_target_mtime > $original_mtime) { - Context::loadFile(array('./' . $concat_target_filename, 'head', '', -100000), true); + Context::loadFile(array('./' . $concat_target_filename, 'head', '', -1500000000), true); return; } } Rhymix\Framework\Formatter::minifyJS(array_map(function($str) { return \RX_BASEDIR . 'common/js/' . $str . '.js'; }, $original_file_list), \RX_BASEDIR . $concat_target_filename); - Context::loadFile(array('./' . $concat_target_filename, 'head', '', -100000), true); + Context::loadFile(array('./' . $concat_target_filename, 'head', '', -1500000000), true); } } } diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 533a511cc..30bc68ab9 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -93,12 +93,27 @@ class FrontEndFileHandler extends Handler { $args = array($args); } - $args[0] = preg_replace(array_keys(HTMLDisplayHandler::$replacements), array_values(HTMLDisplayHandler::$replacements), $args[0]); - $isCommon = preg_match(HTMLDisplayHandler::$reservedCSS, $args[0]) || preg_match(HTMLDisplayHandler::$reservedJS, $args[0]); - if($args[3] > -1500000 && $isCommon) + + // Replace obsolete paths with current paths. + $args[0] = preg_replace(array_keys(HTMLDisplayHandler::$replacements), array_values(HTMLDisplayHandler::$replacements), $args[0]); + $isCommon = preg_match(HTMLDisplayHandler::$reservedCSS, $args[0]) || preg_match(HTMLDisplayHandler::$reservedJS, $args[0]); + + // Prevent overwriting common scripts. + if(intval($args[3]) > -1500000000) { - return; - } + if($isCommon) + { + return; + } + foreach(HTMLDisplayHandler::$blockedScripts as $regexp) + { + if(preg_match($regexp, $args[0])) + { + return; + } + } + } + $file = $this->getFileInfo($args[0], $args[2], $args[1], $args[4], $isCommon); $file->index = (int)$args[3]; diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index 6f5f9a80d..6a9a976e9 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -236,6 +236,34 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $this->assertEquals($expected, $handler->getCssFileList()); }); + $this->specify("path conversion", function() { + $handler = new FrontEndFileHandler(); + $handler->loadFile(array('./common/xeicon/xeicon.min.css')); + $result = $handler->getCssFileList(); + $this->assertEquals('/rhymix/common/css/xeicon/xeicon.min.css' . $this->_filemtime('common/css/xeicon/xeicon.min.css'), $result[0]['file']); + $this->assertEquals('all', $result[0]['media']); + $this->assertEmpty($result[0]['targetie']); + }); + $this->specify("blocked scripts", function() { + $handler = new FrontEndFileHandler(); + $handler->loadFile(array('./common/css/mobile.css')); + $handler->loadFile(array('./common/css/xe.min.css')); + $handler->loadFile(array('./common/js/common.js')); + $handler->loadFile(array('./common/js/xe.js')); + $handler->loadFile(array('./common/js/xe.min.js')); + $handler->loadFile(array('./common/js/xml2json.js')); + $handler->loadFile(array('./common/js/jquery.js')); + $handler->loadFile(array('./common/js/jquery-1.x.min.js')); + $handler->loadFile(array('./common/js/jquery-2.0.0.js')); + $handler->loadFile(array('./common/js/jQuery.min.js')); + $result = $handler->getCssFileList(); + $this->assertEquals(1, count($result)); + $this->assertEquals('/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), $result[0]['file']); + $result = $handler->getJsFileList(); + $this->assertEquals(2, count($result)); + $this->assertEquals('/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), $result[0]['file']); + $this->assertEquals('/rhymix/common/js/xml2json.js' . $this->_filemtime('common/js/xml2json.js'), $result[1]['file']); + }); } }