MDL-48546 filters: ReDoS protection for multimedia links.

This commit is contained in:
Zachary Durber 2015-01-05 10:43:40 +08:00 committed by David Monllao
parent ac667aa0b7
commit 75cb0ffffe
2 changed files with 66 additions and 10 deletions

View File

@ -68,18 +68,52 @@ class filter_mediaplugin extends moodle_text_filter {
// Check SWF permissions.
$this->trusted = !empty($options['noclean']) or !empty($CFG->allowobjectembed);
// Handle all links that contain any 'embeddable' marker text (it could
// do all links, but the embeddable markers thing should make it faster
// by meaning for most links it doesn't drop into PHP code).
$newtext = preg_replace_callback($re = '~<a\s[^>]*href="([^"]*(?:' .
$this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is',
array($this, 'callback'), $text);
// Looking for tags.
$matches = preg_split('/(<a\s[^>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
if (empty($newtext) or $newtext === $text) {
// error or not filtered
if (!$matches) {
return $text;
}
// Regex to find media extensions in an <a> tag.
$re = '~<a\s[^>]*href="([^"]*(?:' . $this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is';
$newtext = '';
$validtag = '';
$sizeofmatches = count($matches);
// We iterate through the given string to find valid <a> tags
// and build them so that the callback function can check it for
// embedded content. Then we rebuild the string.
foreach ($matches as $idx => $tag) {
if (preg_match('|</a>|', $tag) && !empty($validtag)) {
$validtag .= $tag;
// Given we now have a valid <a> tag to process it's time for
// ReDoS protection. Stop processing if a word is too large.
if (strlen($validtag) < 4096) {
$processed = preg_replace_callback($re, array($this, 'callback'), $validtag);
}
// Rebuilding the string with our new processed text.
$newtext .= !empty($processed) ? $processed : $validtag;
// Wipe it so we can catch any more instances to filter.
$validtag = '';
$processed = '';
} else if (preg_match('/<a\s[^>]*/', $tag) && $sizeofmatches > 1) {
// Looking for a starting <a> tag.
$validtag = $tag;
} else {
// If we have a validtag add to that to process later,
// else add straight onto our newtext string.
if (!empty($validtag)) {
$validtag .= $tag;
} else {
$newtext .= $tag;
}
}
}
// Return the same string except processed by the above.
return $newtext;
}

View File

@ -51,6 +51,15 @@ class filter_mediaplugin_testcase extends advanced_testcase {
$filterplugin = new filter_mediaplugin(null, array());
$longurl = '<a href="http://moodle/.mp4">my test file</a>';
$longhref = '';
do {
$longhref .= 'a';
} while(strlen($longhref) + strlen($longurl) < 4095);
$longurl = '<a href="http://moodle/' . $longhref . '.mp4">my test file</a>';
$validtexts = array (
'<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
'<a href="http://moodle.org/testfile/test.ogg">test ogg</a>',
@ -76,7 +85,9 @@ class filter_mediaplugin_testcase extends advanced_testcase {
href="http://moodle.org/testfile/test.avi">test mp3
</a>',
'<a href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200" >youtube\'s</a>'
'<a href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200" >youtube\'s</a>',
// Test a long URL under 4096 characters.
$longurl
);
//test for valid link
@ -86,6 +97,9 @@ class filter_mediaplugin_testcase extends advanced_testcase {
$this->assertNotEquals($text, $filter, $msg);
}
$insertpoint = strrpos($longurl, 'http://');
$longurl = substr_replace($longurl, 'http://pushover4096chars', $insertpoint, 0);
$invalidtexts = array(
'<a class="_blanktarget">href="http://moodle.org/testfile/test.mp3"</a>',
'<a>test test</a>',
@ -101,7 +115,9 @@ class filter_mediaplugin_testcase extends advanced_testcase {
'<href="http://moodle.org/testfile/test.avi">test</a>',
'<abbr href="http://moodle.org/testfile/test.mp3">test mp3</abbr>',
'<ahref="http://moodle.org/testfile/test.mp3">test mp3</a>',
'<aclass="content" href="http://moodle.org/testfile/test.mp3">test mp3</a>'
'<aclass="content" href="http://moodle.org/testfile/test.mp3">test mp3</a>',
// Test a long URL over 4096 characters.
$longurl
);
//test for invalid link
@ -110,5 +126,11 @@ class filter_mediaplugin_testcase extends advanced_testcase {
$filter = $filterplugin->filter($text);
$this->assertEquals($text, $filter, $msg);
}
// Valid mediaurl followed by a longurl.
$precededlongurl = '<a href="http://moodle.org/testfile/test.mp3">test.mp3</a>'. $longurl;
$filter = $filterplugin->filter($precededlongurl);
$this->assertEquals(1, substr_count($filter, 'M.util.add_audio_player'));
$this->assertContains($longurl, $filter);
}
}