MDL-38466 filters: ReDoS protection for text to URL conversion.
This commit is contained in:
committed by
Eloy Lafuente (stronk7)
parent
952bc6c5a6
commit
1b24951778
@@ -134,10 +134,9 @@ class filter_urltolink extends moodle_text_filter {
|
||||
|
||||
// Lookbehind assertions.
|
||||
// Is not HTML attribute or CSS URL property. Unfortunately legit text like "url(http://...)" will not be a link.
|
||||
$lookbehindstart = "(?<!=[\"']|\burl\([\"' ]|\burl\()";
|
||||
$lookbehindend = "(?<![]),.;])";
|
||||
|
||||
$regex = "$lookbehindstart$urlstart((?:$domainsegment\.)+$domainsegment|$numericip)" .
|
||||
$regex = "$urlstart((?:$domainsegment\.)+$domainsegment|$numericip)" .
|
||||
"($port?$path$querystring?$fragment?)$lookbehindend";
|
||||
if ($unicoderegexp) {
|
||||
$regex = '#' . $regex . '#ui';
|
||||
@@ -145,7 +144,39 @@ class filter_urltolink extends moodle_text_filter {
|
||||
$regex = '#' . preg_replace(array('\pLl', '\PL'), 'a-z', $regex) . '#i';
|
||||
}
|
||||
|
||||
$text = preg_replace($regex, '<a href="http$1://$2$3$4" class="_blanktarget">$0</a>', $text);
|
||||
// Locate any HTML tags.
|
||||
$matches = preg_split('/(<[^>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
$lookingforendstyle = false;
|
||||
|
||||
// Iterate through the tokenized text to handle chunks (html and content).
|
||||
foreach ($matches as $idx => $chunk) {
|
||||
// Nothing to do. We skip completely any html chunk.
|
||||
if (strpos($chunk, '<') !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nothing to do. We skip any content chunk having any of these attributes.
|
||||
if (preg_match('#(background=")|(action=")|(style="background)|(href=")|(src=")|(url [(])#', $chunk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Arrived here, we want to process every word in this chunk.
|
||||
$text = $chunk;
|
||||
$words = explode(' ', $text);
|
||||
|
||||
foreach ($words as $idx2 => $word) {
|
||||
// ReDoS protection. Stop processing if a word is too large.
|
||||
if (strlen($word) < 4096) {
|
||||
$words[$idx2] = preg_replace($regex, '<a href="http$1://$2$3$4" class="_blanktarget">$0</a>', $word);
|
||||
}
|
||||
}
|
||||
$text = implode(' ', $words);
|
||||
|
||||
// Copy the result back to the array.
|
||||
$matches[$idx] = $text;
|
||||
}
|
||||
|
||||
$text = implode('', $matches);
|
||||
|
||||
if (!empty($ignoretags)) {
|
||||
$ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems.
|
||||
|
||||
@@ -32,6 +32,10 @@ require_once($CFG->dirroot . '/filter/urltolink/filter.php'); // Include the cod
|
||||
class filter_urltolink_testcase extends basic_testcase {
|
||||
|
||||
function get_convert_urls_into_links_test_cases() {
|
||||
// Create a 4095 and 4096 long URLs.
|
||||
$superlong4095 = str_pad('http://www.superlong4095.com?this=something', 4095, 'a');
|
||||
$superlong4096 = str_pad('http://www.superlong4096.com?this=something', 4096, 'a');
|
||||
|
||||
$texts = array (
|
||||
//just a url
|
||||
'http://moodle.org - URL' => '<a href="http://moodle.org" class="_blanktarget">http://moodle.org</a> - URL',
|
||||
@@ -130,6 +134,7 @@ class filter_urltolink_testcase extends basic_testcase {
|
||||
'<td background="http://moodle.org"> </td>' => '<td background="http://moodle.org"> </td>',
|
||||
'<td background="www.moodle.org"> </td>' => '<td background="www.moodle.org"> </td>',
|
||||
'<form name="input" action="http://moodle.org/submit.asp" method="get">'=>'<form name="input" action="http://moodle.org/submit.asp" method="get">',
|
||||
'<input type="submit" value="Go to http://moodle.org">' => '<input type="submit" value="Go to http://moodle.org">',
|
||||
'<td background="https://www.moodle.org"> </td>' => '<td background="https://www.moodle.org"> </td>',
|
||||
// CSS URLs.
|
||||
'<table style="background-image: url(\'http://moodle.org/pic.jpg\');">' => '<table style="background-image: url(\'http://moodle.org/pic.jpg\');">',
|
||||
@@ -148,6 +153,12 @@ class filter_urltolink_testcase extends basic_testcase {
|
||||
//Encoded URLs in the query
|
||||
'URL: http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1' => 'URL: <a href="http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1" class="_blanktarget">http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1</a>',
|
||||
'URL: www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1' => 'URL: <a href="http://www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1" class="_blanktarget">www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1</a>',
|
||||
// Test URL less than 4096 characters in size is converted to link.
|
||||
'URL: ' . $superlong4095 => 'URL: <a href="' . $superlong4095 . '" class="_blanktarget">' . $superlong4095 . '</a>',
|
||||
// Test URL equal to or greater than 4096 characters in size is not converted to link.
|
||||
'URL: ' . $superlong4096 => 'URL: ' . $superlong4096,
|
||||
// Testing URL within a span tag.
|
||||
'URL: <span style="kasd"> my link to http://google.com </span>' => 'URL: <span style="kasd"> my link to <a href="http://google.com" class="_blanktarget">http://google.com</a> </span>',
|
||||
//URLs in Javascript. Commented out as part of MDL-21183
|
||||
//'var url="http://moodle.org";'=>'var url="http://moodle.org";',
|
||||
//'var url = "http://moodle.org";'=>'var url = "http://moodle.org";',
|
||||
|
||||
Reference in New Issue
Block a user