Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
     */
11 efrain 141
    public function test_upgrade_http_links($content, $ouputregex, $expectedcontent): void {
1 efrain 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
     */
11 efrain 215
    public function test_http_link_stats($content, $domain, $expectedcount): void {
1 efrain 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
     */
11 efrain 233
    public function test_links_and_text(): void {
1 efrain 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
     */
11 efrain 264
    public function test_httpwwwroot(): void {
1 efrain 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
     */
11 efrain 289
    public function test_upgrade_http_links_excluded_tables(): void {
1 efrain 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
     */
11 efrain 309
    public function test_renames(): void {
1 efrain 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
     */
11 efrain 342
    public function test_multiple(): void {
1 efrain 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
     */
11 efrain 379
    public function test_reserved_words(): void {
1 efrain 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
}