Differences Between: [Versions 310 and 403] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * HTTPS find and replace Tests 19 * 20 * @package tool_httpsreplace 21 * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace tool_httpsreplace; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Tests the httpsreplace tool. 31 * 32 * @package tool_httpsreplace 33 * @copyright Copyright (c) 2016 Blackboard Inc. (http://www.blackboard.com) 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class httpsreplace_test extends \advanced_testcase { 37 38 /** 39 * Data provider for test_upgrade_http_links 40 */ 41 public function upgrade_http_links_provider() { 42 global $CFG; 43 // Get the http url, since the default test wwwroot is https. 44 $wwwroothttp = preg_replace('/^https:/', 'http:', $CFG->wwwroot); 45 return [ 46 "Test image from another site should be replaced" => [ 47 "content" => '<img src="' . $this->getExternalTestFileUrl('/test.jpg', false) . '">', 48 "outputregex" => '/UPDATE/', 49 "expectedcontent" => '<img src="' . $this->get_converted_http_link('/test.jpg') . '">', 50 ], 51 "Test object from another site should be replaced" => [ 52 "content" => '<object data="' . $this->getExternalTestFileUrl('/test.swf', false) . '">', 53 "outputregex" => '/UPDATE/', 54 "expectedcontent" => '<object data="' . $this->get_converted_http_link('/test.swf') . '">', 55 ], 56 "Test image from a site with international name should be replaced" => [ 57 "content" => '<img src="http://中国互联网络信息中心.中国/logosy/201706/W01.png">', 58 "outputregex" => '/UPDATE/', 59 "expectedcontent" => '<img src="https://中国互联网络信息中心.中国/logosy/201706/W01.png">', 60 ], 61 "Link that is from this site should be replaced" => [ 62 "content" => '<img src="' . $wwwroothttp . '/logo.png">', 63 "outputregex" => '/UPDATE/', 64 "expectedcontent" => '<img src="' . $CFG->wwwroot . '/logo.png">', 65 ], 66 "Link that is from this site, https new so doesn't need replacing" => [ 67 "content" => '<img src="' . $CFG->wwwroot . '/logo.png">', 68 "outputregex" => '/^$/', 69 "expectedcontent" => '<img src="' . $CFG->wwwroot . '/logo.png">', 70 ], 71 "Unavailable image should be replaced" => [ 72 "content" => '<img src="http://intentionally.unavailable/link1.jpg">', 73 "outputregex" => '/UPDATE/', 74 "expectedcontent" => '<img src="https://intentionally.unavailable/link1.jpg">', 75 ], 76 "Https content that has an http url as a param should not be replaced" => [ 77 "content" => '<img src="https://anothersite.com?param=http://asdf.com">', 78 "outputregex" => '/^$/', 79 "expectedcontent" => '<img src="https://anothersite.com?param=http://asdf.com">', 80 ], 81 "Search for params should be case insensitive" => [ 82 "content" => '<object DATA="' . $this->getExternalTestFileUrl('/test.swf', false) . '">', 83 "outputregex" => '/UPDATE/', 84 "expectedcontent" => '<object DATA="' . $this->get_converted_http_link('/test.swf') . '">', 85 ], 86 "URL should be case insensitive" => [ 87 "content" => '<object data="HTTP://some.site/path?query">', 88 "outputregex" => '/UPDATE/', 89 "expectedcontent" => '<object data="https://some.site/path?query">', 90 ], 91 "More params should not interfere" => [ 92 "content" => '<img alt="A picture" src="' . $this->getExternalTestFileUrl('/test.png', false) . 93 '" width="1”><p style="font-size: \'20px\'"></p>', 94 "outputregex" => '/UPDATE/', 95 "expectedcontent" => '<img alt="A picture" src="' . $this->get_converted_http_link('/test.png') . 96 '" width="1”><p style="font-size: \'20px\'"></p>', 97 ], 98 "Broken URL should not be changed" => [ 99 "content" => '<img src="broken.' . $this->getExternalTestFileUrl('/test.png', false) . '">', 100 "outputregex" => '/^$/', 101 "expectedcontent" => '<img src="broken.' . $this->getExternalTestFileUrl('/test.png', false) . '">', 102 ], 103 "Link URL should not be changed" => [ 104 "content" => '<a href="' . $this->getExternalTestFileUrl('/test.png', false) . '">' . 105 $this->getExternalTestFileUrl('/test.png', false) . '</a>', 106 "outputregex" => '/^$/', 107 "expectedcontent" => '<a href="' . $this->getExternalTestFileUrl('/test.png', false) . '">' . 108 $this->getExternalTestFileUrl('/test.png', false) . '</a>', 109 ], 110 "Test image from another site should be replaced but link should not" => [ 111 "content" => '<a href="' . $this->getExternalTestFileUrl('/test.png', false) . '"><img src="' . 112 $this->getExternalTestFileUrl('/test.jpg', false) . '"></a>', 113 "outputregex" => '/UPDATE/', 114 "expectedcontent" => '<a href="' . $this->getExternalTestFileUrl('/test.png', false) . '"><img src="' . 115 $this->get_converted_http_link('/test.jpg') . '"></a>', 116 ], 117 ]; 118 } 119 120 /** 121 * Convert the HTTP external test file URL to use HTTPS. 122 * 123 * Note: We *must not* use getExternalTestFileUrl with the True option 124 * here, becase it is reasonable to have only one of these set due to 125 * issues with SSL certificates. 126 * 127 * @param string $path Path to be rewritten 128 * @return string 129 */ 130 protected function get_converted_http_link($path) { 131 return preg_replace('/^http:/', 'https:', $this->getExternalTestFileUrl($path, false)); 132 } 133 134 /** 135 * Test upgrade_http_links 136 * @param string $content Example content that we'll attempt to replace. 137 * @param string $ouputregex Regex for what output we expect. 138 * @param string $expectedcontent What content we are expecting afterwards. 139 * @dataProvider upgrade_http_links_provider 140 */ 141 public function test_upgrade_http_links($content, $ouputregex, $expectedcontent) { 142 global $DB; 143 144 $this->resetAfterTest(); 145 $this->expectOutputRegex($ouputregex); 146 147 $finder = new tool_httpreplace_url_finder_mock(); 148 149 $generator = $this->getDataGenerator(); 150 $course = $generator->create_course((object) [ 151 'summary' => $content, 152 ]); 153 154 $finder->upgrade_http_links(); 155 156 $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); 157 $this->assertStringContainsString($expectedcontent, $summary); 158 } 159 160 /** 161 * Data provider for test_http_link_stats 162 */ 163 public function http_link_stats_provider() { 164 global $CFG; 165 // Get the http url, since the default test wwwroot is https. 166 $wwwrootdomain = 'www.example.com'; 167 $wwwroothttp = preg_replace('/^https:/', 'http:', $CFG->wwwroot); 168 $testdomain = $this->get_converted_http_link(''); 169 return [ 170 "Test image from an available site so shouldn't be reported" => [ 171 "content" => '<img src="' . $this->getExternalTestFileUrl('/test.jpg', false) . '">', 172 "domain" => $testdomain, 173 "expectedcount" => 0, 174 ], 175 "Link that is from this site shouldn't be reported" => [ 176 "content" => '<img src="' . $wwwroothttp . '/logo.png">', 177 "domain" => $wwwrootdomain, 178 "expectedcount" => 0, 179 ], 180 "Unavailable, but https shouldn't be reported" => [ 181 "content" => '<img src="https://intentionally.unavailable/logo.png">', 182 "domain" => 'intentionally.unavailable', 183 "expectedcount" => 0, 184 ], 185 "Unavailable image should be reported" => [ 186 "content" => '<img src="http://intentionally.unavailable/link1.jpg">', 187 "domain" => 'intentionally.unavailable', 188 "expectedcount" => 1, 189 ], 190 "Unavailable object should be reported" => [ 191 "content" => '<object data="http://intentionally.unavailable/file.swf">', 192 "domain" => 'intentionally.unavailable', 193 "expectedcount" => 1, 194 ], 195 "Link should not be reported" => [ 196 "content" => '<a href="http://intentionally.unavailable/page.php">Link</a>', 197 "domain" => 'intentionally.unavailable', 198 "expectedcount" => 0, 199 ], 200 "Text should not be reported" => [ 201 "content" => 'http://intentionally.unavailable/page.php', 202 "domain" => 'intentionally.unavailable', 203 "expectedcount" => 0, 204 ], 205 ]; 206 } 207 208 /** 209 * Test http_link_stats 210 * @param string $content Example content that we'll attempt to replace. 211 * @param string $domain The domain we will check was replaced. 212 * @param string $expectedcount Number of urls from that domain that we expect to be replaced. 213 * @dataProvider http_link_stats_provider 214 */ 215 public function test_http_link_stats($content, $domain, $expectedcount) { 216 $this->resetAfterTest(); 217 218 $finder = new tool_httpreplace_url_finder_mock(); 219 220 $generator = $this->getDataGenerator(); 221 $course = $generator->create_course((object) [ 222 'summary' => $content, 223 ]); 224 225 $results = $finder->http_link_stats(); 226 227 $this->assertEquals($expectedcount, $results[$domain] ?? 0); 228 } 229 230 /** 231 * Test links and text are not changed 232 */ 233 public function test_links_and_text() { 234 global $DB; 235 236 $this->resetAfterTest(); 237 $this->expectOutputRegex('/^$/'); 238 239 $finder = new tool_httpreplace_url_finder_mock(); 240 241 $generator = $this->getDataGenerator(); 242 $course = $generator->create_course((object) [ 243 'summary' => '<a href="http://intentionally.unavailable/page.php">Link</a> http://other.unavailable/page.php', 244 ]); 245 246 $results = $finder->http_link_stats(); 247 $this->assertCount(0, $results); 248 249 $finder->upgrade_http_links(); 250 251 $results = $finder->http_link_stats(); 252 $this->assertCount(0, $results); 253 254 $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); 255 $this->assertStringContainsString('http://intentionally.unavailable/page.php', $summary); 256 $this->assertStringContainsString('http://other.unavailable/page.php', $summary); 257 $this->assertStringNotContainsString('https://intentionally.unavailable', $summary); 258 $this->assertStringNotContainsString('https://other.unavailable', $summary); 259 } 260 261 /** 262 * If we have an http wwwroot then we shouldn't report it. 263 */ 264 public function test_httpwwwroot() { 265 global $DB, $CFG; 266 267 $this->resetAfterTest(); 268 $CFG->wwwroot = preg_replace('/^https:/', 'http:', $CFG->wwwroot); 269 $this->expectOutputRegex('/^$/'); 270 271 $finder = new tool_httpreplace_url_finder_mock(); 272 273 $generator = $this->getDataGenerator(); 274 $course = $generator->create_course((object) [ 275 'summary' => '<img src="' . $CFG->wwwroot . '/image.png">', 276 ]); 277 278 $results = $finder->http_link_stats(); 279 $this->assertCount(0, $results); 280 281 $finder->upgrade_http_links(); 282 $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); 283 $this->assertStringContainsString($CFG->wwwroot, $summary); 284 } 285 286 /** 287 * Test that links in excluded tables are not replaced 288 */ 289 public function test_upgrade_http_links_excluded_tables() { 290 $this->resetAfterTest(); 291 292 set_config('test_upgrade_http_links', '<img src="http://somesite/someimage.png" />'); 293 294 $finder = new tool_httpreplace_url_finder_mock(); 295 ob_start(); 296 $results = $finder->upgrade_http_links(); 297 $output = ob_get_contents(); 298 ob_end_clean(); 299 $this->assertTrue($results); 300 $this->assertStringNotContainsString('https://somesite', $output); 301 $testconf = get_config('core', 'test_upgrade_http_links'); 302 $this->assertStringContainsString('http://somesite', $testconf); 303 $this->assertStringNotContainsString('https://somesite', $testconf); 304 } 305 306 /** 307 * Test renamed domains 308 */ 309 public function test_renames() { 310 global $DB, $CFG; 311 $this->resetAfterTest(); 312 $this->expectOutputRegex('/UPDATE/'); 313 314 $renames = [ 315 'example.com' => 'secure.example.com', 316 ]; 317 318 set_config('renames', json_encode($renames), 'tool_httpsreplace'); 319 320 $finder = new tool_httpreplace_url_finder_mock(); 321 322 $generator = $this->getDataGenerator(); 323 $course = $generator->create_course((object) [ 324 'summary' => '<script src="http://example.com/test.js"><img src="http://EXAMPLE.COM/someimage.png">', 325 ]); 326 327 $results = $finder->http_link_stats(); 328 $this->assertCount(0, $results); 329 330 $finder->upgrade_http_links(); 331 332 $summary = $DB->get_field('course', 'summary', ['id' => $course->id]); 333 $this->assertStringContainsString('https://secure.example.com', $summary); 334 $this->assertStringNotContainsString('http://example.com', $summary); 335 $this->assertEquals('<script src="https://secure.example.com/test.js">' . 336 '<img src="https://secure.example.com/someimage.png">', $summary); 337 } 338 339 /** 340 * When there are many different pieces of contents from the same site, we should only run replace once 341 */ 342 public function test_multiple() { 343 global $DB; 344 $this->resetAfterTest(); 345 $original1 = ''; 346 $expected1 = ''; 347 $original2 = ''; 348 $expected2 = ''; 349 for ($i = 0; $i < 15; $i++) { 350 $original1 .= '<img src="http://example.com/image' . $i . '.png">'; 351 $expected1 .= '<img src="https://example.com/image' . $i . '.png">'; 352 $original2 .= '<img src="http://example.com/image' . ($i + 15 ) . '.png">'; 353 $expected2 .= '<img src="https://example.com/image' . ($i + 15) . '.png">'; 354 } 355 $finder = new tool_httpreplace_url_finder_mock(); 356 357 $generator = $this->getDataGenerator(); 358 $course1 = $generator->create_course((object) ['summary' => $original1]); 359 $course2 = $generator->create_course((object) ['summary' => $original2]); 360 361 ob_start(); 362 $finder->upgrade_http_links(); 363 $output = ob_get_contents(); 364 ob_end_clean(); 365 366 // Make sure everything is replaced. 367 $summary1 = $DB->get_field('course', 'summary', ['id' => $course1->id]); 368 $this->assertEquals($expected1, $summary1); 369 $summary2 = $DB->get_field('course', 'summary', ['id' => $course2->id]); 370 $this->assertEquals($expected2, $summary2); 371 372 // Make sure only one UPDATE statment was called. 373 $this->assertEquals(1, preg_match_all('/UPDATE/', $output)); 374 } 375 376 /** 377 * Test the tool when the column name is a reserved word in SQL (in this case 'where') 378 */ 379 public function test_reserved_words() { 380 global $DB; 381 382 $this->resetAfterTest(); 383 $this->expectOutputRegex('/UPDATE/'); 384 385 // Create a table with a field that is a reserved SQL word. 386 $dbman = $DB->get_manager(); 387 $table = new \xmldb_table('reserved_words_temp'); 388 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 389 $table->add_field('where', XMLDB_TYPE_TEXT, null, null, null, null, null); 390 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 391 $dbman->create_table($table); 392 393 // Insert a record with an <img> in this table and run tool. 394 $content = '<img src="http://example.com/image.png">'; 395 $expectedcontent = '<img src="https://example.com/image.png">'; 396 $columnamequoted = $dbman->generator->getEncQuoted('where'); 397 $DB->execute("INSERT INTO {reserved_words_temp} ($columnamequoted) VALUES (?)", [$content]); 398 399 $finder = new tool_httpreplace_url_finder_mock(); 400 $finder->upgrade_http_links(); 401 402 $record = $DB->get_record('reserved_words_temp', []); 403 $this->assertStringContainsString($expectedcontent, $record->where); 404 405 $dbman->drop_table($table); 406 } 407 } 408 409 /** 410 * Class tool_httpreplace_url_finder_mock for testing replace tool without calling curl 411 * 412 * @package tool_httpsreplace 413 * @copyright 2017 Marina Glancy 414 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 415 */ 416 class tool_httpreplace_url_finder_mock extends \tool_httpsreplace\url_finder { 417 /** 418 * Check if url is available (check hardcoded for unittests) 419 * 420 * @param string $url 421 * @return bool 422 */ 423 protected function check_domain_availability($url) { 424 return !preg_match('|\.unavailable/$|', $url); 425 } 426 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body