Skip to content
This repository has been archived by the owner on Apr 8, 2022. It is now read-only.

Commit

Permalink
MDL-48546 filters: ReDoS protection for multimedia links.
Browse files Browse the repository at this point in the history
  • Loading branch information
zbdd authored and David Monllao committed Jan 7, 2015
1 parent ac667aa commit 75cb0ff
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 11 deletions.
52 changes: 43 additions & 9 deletions filter/mediaplugin/filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,52 @@ public function filter($text, array $options = array()) {
// 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);

if (empty($newtext) or $newtext === $text) {
// error or not filtered
// Looking for tags.
$matches = preg_split('/(<a\s[^>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

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;
}

Expand Down
26 changes: 24 additions & 2 deletions filter/mediaplugin/tests/filter_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ function test_filter_mediaplugin_link() {

$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>',
Expand All @@ -76,7 +85,9 @@ function test_filter_mediaplugin_link() {
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
Expand All @@ -86,6 +97,9 @@ function test_filter_mediaplugin_link() {
$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>',
Expand All @@ -101,7 +115,9 @@ function test_filter_mediaplugin_link() {
'<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
Expand All @@ -110,5 +126,11 @@ function test_filter_mediaplugin_link() {
$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);
}
}

0 comments on commit 75cb0ff

Please sign in to comment.