From 7d8a3cb06ae45ccb4d39e60c563ff712cc1dd536 Mon Sep 17 00:00:00 2001 From: skodak Date: Sat, 26 Aug 2006 13:00:07 +0000 Subject: [PATCH] trusttext: * proposed by Martin Dougiamas * implemented by skodak Usage: 1/ change enabletrusttext to yes in site settings (it is off by default) or set it in config.php 2/ assign moodle/site:trustcontent capability to users whose text submitted in glossary entries, comments, forum posts etc. should not be cleaned == they can use javascript or any other forbidden tags in glossary and forums... done: * core * glossary (without proper upgrade) to do: * data cleaning in upgrades * forum, blocks and some other places (MD decides) --- admin/configvars.php | 3 + admin/settings/authenticationandsecurity.php | 1 + lib/db/access.php | 14 +++ lib/defaults.php | 1 + lib/weblib.php | 126 +++++++++++++++++-- mod/glossary/comment.html | 10 +- mod/glossary/comment.php | 2 +- mod/glossary/edit.html | 10 +- mod/glossary/edit.php | 5 +- mod/glossary/import.php | 2 +- mod/glossary/lib.php | 50 +++++++- version.php | 2 +- 12 files changed, 193 insertions(+), 33 deletions(-) diff --git a/admin/configvars.php b/admin/configvars.php index a1e0b87f75..c3188444cd 100644 --- a/admin/configvars.php +++ b/admin/configvars.php @@ -225,6 +225,9 @@ class configvarrss extends configvar { $permissions['allowobjectembed'] = new configvar (get_string('configallowobjectembed', 'admin'), choose_from_menu ($noyesoptions, 'allowobjectembed', $config->allowobjectembed, '', '', '', true) ); +/// enabletrusttext + $permissions['enabletrusttext'] = new configvar (get_string('configenabletrusttext', 'admin'), + choose_from_menu ($noyesoptions, 'enabletrusttext', $config->enabletrusttext, '', '', '', true) ); unset($options); $options['none'] = 'No courses'; diff --git a/admin/settings/authenticationandsecurity.php b/admin/settings/authenticationandsecurity.php index c500e5cfd4..31c9c9977d 100644 --- a/admin/settings/authenticationandsecurity.php +++ b/admin/settings/authenticationandsecurity.php @@ -77,6 +77,7 @@ $temp->add(new admin_setting_configcheckbox('opentogoogle', get_string('opentogo $temp->add(new admin_setting_configtext('maxbytes', get_string('maxbytes', 'admin'), get_string('configmaxbytes', 'admin'), PARAM_INT)); $temp->add(new admin_setting_configcheckbox('messaging', get_string('messaging', 'admin'), get_string('configmessaging','admin'))); $temp->add(new admin_setting_configcheckbox('allowobjectembed', get_string('allowobjectembed', 'admin'), get_string('configallowobjectembed', 'admin'))); +$temp->add(new admin_setting_configcheckbox('enabletrusttext', get_string('enabletrusttext', 'admin'), get_string('configenabletrusttext', 'admin'))); $temp->add(new admin_setting_configselect('maxeditingtime', get_string('maxeditingtime','admin'), get_string('configmaxeditingtime','admin'), array(60 => get_string('numminutes', '', 1), 300 => get_string('numminutes', '', 5), 900 => get_string('numminutes', '', 15), diff --git a/lib/db/access.php b/lib/db/access.php index c4d6d40e74..a533ec2847 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -186,6 +186,20 @@ $moodle_capabilities = array( ) ), + 'moodle/site:trustcontent' => array( + + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'guest' => CAP_PREVENT, + 'student' => CAP_PREVENT, + 'teacher' => CAP_PREVENT, + 'editingteacher' => CAP_ALLOW, + 'coursecreator' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'moodle/user:create' => array( 'captype' => 'write', diff --git a/lib/defaults.php b/lib/defaults.php index ceeda36d51..755ecf9829 100644 --- a/lib/defaults.php +++ b/lib/defaults.php @@ -34,6 +34,7 @@ 'enablecourserequests' => 0, 'enablerssfeeds' => 0, 'enablestats' => 0, + 'enabletrusttext' => 0, 'enrol' => 'internal', 'extendedusernamechars' => false, 'editorbackgroundcolor' => '#ffffff', diff --git a/lib/weblib.php b/lib/weblib.php index b104948c9f..8b4e363088 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -72,6 +72,11 @@ define('FORMAT_WIKI', '3'); // Wiki-formatted text */ define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/ +/** + * TRUSTTEXT marker - if present in text, text cleaning should be bypassed + */ +define('TRUSTTEXT', '#####TRUSTTEXT#####'); + /** * Allowed tags - string of html tags that can be tested against for safe html tags @@ -1234,10 +1239,14 @@ function format_text_menu() { * @return string * @todo Finish documenting this function */ -function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL ) { +function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) { global $CFG, $course; + if (!isset($options->trusttext)) { + $options->trusttext = false; + } + if (!isset($options->noclean)) { $options->noclean=false; } @@ -1262,7 +1271,7 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL if (!empty($CFG->cachetext)) { $time = time() - $CFG->cachetext; - $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines.$format.current_language().$courseid); + $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines.$format.current_language().$courseid.$options->trusttext); if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) { if ($oldcacheitem->timemodified >= $time) { return $oldcacheitem->formattedtext; @@ -1270,17 +1279,31 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL } } + // trusttext overrides the noclean option! + if ($options->trusttext) { + if (trusttext_present($text)) { + $text = trusttext_strip($text); + if (!empty($CFG->enabletrusttext)) { + $options->noclean = true; + } else { + $options->noclean = false; + } + } else { + $options->noclean = false; + } + } + $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter switch ($format) { case FORMAT_HTML: - if (!empty($options->smiley)) { + if ($options->smiley) { replace_smilies($text); } - if (empty($options->noclean)) { + if (!$options->noclean) { $text = clean_text($text, $format); } - if (!empty($options->filter)) { + if ($options->filter) { $text = filter_text($text, $courseid); } break; @@ -1302,25 +1325,25 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL case FORMAT_MARKDOWN: $text = markdown_to_html($text); - if (!empty($options->smiley)) { + if ($options->smiley) { replace_smilies($text); } - if (empty($options->noclean)) { + if (!$options->noclean) { $text = clean_text($text, $format); } - if (!empty($options->filter)) { + if ($options->filter) { $text = filter_text($text, $courseid); } break; default: // FORMAT_MOODLE or anything else $text = text_to_html($text, $options->smiley, $options->para, $options->newlines); - if (empty($options->noclean)) { + if (!$options->noclean) { $text = clean_text($text, $format); } - if (!empty($options->filter)) { + if ($options->filter) { $text = filter_text($text, $courseid); } break; @@ -1475,7 +1498,6 @@ function format_text_email($text, $format) { * @todo Finish documenting this function */ function filter_text($text, $courseid=NULL) { - global $CFG; require_once($CFG->libdir.'/filterlib.php'); @@ -1499,6 +1521,88 @@ function filter_text($text, $courseid=NULL) { return $text; } +/** + * Is the text marked as trusted? + * + * @param string $text text to be searched for TRUSTTEXT marker + * @return boolean + */ +function trusttext_present($text) { + if (strpos($text, TRUSTTEXT) !== FALSE) { + return true; + } else { + return false; + } +} + +/** + * This funtion MUST be called before the cleaning or any other + * function that modifies the data! We do not know the origin of trusttext + * in database, if it gets there in tweaked form we must not convert it + * to supported form!!! + * + * Please be carefull not to use stripslashes on data from database + * or twice stripslashes when processing data recieved from user. + * + * @param string $text text that may contain TRUSTTEXT marker + * @return text without any TRUSTTEXT marker + */ +function trusttext_strip($text) { + global $CFG; + + while (true) { //removing nested TRUSTTEXT + $orig = $text; + $text = str_replace(TRUSTTEXT, '', $text); + if (strcmp($orig, $text) === 0) { + return $text; + } + } +} + +/** + * Mark text as trusted, such text may contain any HTML tags because the + * normal text cleaning will be bypassed. + * Please make sure that the text comes from trusted user before storing + * it into database! + */ +function trusttext_mark($text) { + global $CFG; + if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) { + return TRUSTTEXT.$text; + } else { + return $text; + } +} +function trusttext_after_edit(&$text, $context) { + if (has_capability('moodle/site:trustcontent', $context)) { + $text = trusttext_mark($text); + } else { + $text = trusttext_strip($text); + } +} + +function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) { + global $CFG; + + $options = new object(); + $options->smiley = false; + $options->filter = false; + if (!empty($CFG->enabletrusttext) + and has_capability('moodle/site:trustcontent', $context) + and trusttext_present($text)) { + $options->noclean = true; + } else { + $options->noclean = false; + } + $text = trusttext_strip($text); + if ($usehtmleditor) { + $text = format_text($text, $format, $options); + $format = FORMAT_HTML; + } else if (!$options->noclean){ + $text = clean_text($text, $format); + } +} + /** * Given raw text (eg typed in by a user), this function cleans it up * and removes any nasty tags that could mess up Moodle pages. diff --git a/mod/glossary/comment.html b/mod/glossary/comment.html index fa2117140f..7d749ed400 100644 --- a/mod/glossary/comment.html +++ b/mod/glossary/comment.html @@ -2,13 +2,9 @@ if (!isset($form->format)) { $form->format = $defaultformat; } - if ($usehtmleditor) { //clean and convert before editing - $options = new object(); - $options->smiley = false; - $options->filter = false; - $form->text = format_text($form->text, $form->format, $options); - $form->format = FORMAT_HTML; - } + + trusttext_prepare_edit($form->text, $form->format, $usehtmleditor, $context) + ?>
diff --git a/mod/glossary/comment.php b/mod/glossary/comment.php index bc9f098318..879eaad396 100644 --- a/mod/glossary/comment.php +++ b/mod/glossary/comment.php @@ -134,7 +134,7 @@ } if ( $confirm and $form = data_submitted() ) { - //$form->text = clean_text($form->text, $form->format); + trusttext_after_edit($form->text, $context); $newentry->entryid = $entry->id; $newentry->comment = $form->text; diff --git a/mod/glossary/edit.html b/mod/glossary/edit.html index c1e2896d48..455ed276f9 100644 --- a/mod/glossary/edit.html +++ b/mod/glossary/edit.html @@ -2,13 +2,9 @@ if (!isset($newentry->format)) { $newentry->format = $defaultformat; } - if ($usehtmleditor) { //clean and convert before editing - $options = new object(); - $options->smiley = false; - $options->filter = false; - $newentry->definition = format_text($newentry->definition, $newentry->format, $options); - $newentry->format = FORMAT_HTML; - } + + trusttext_prepare_edit($newentry->definition, $newentry->format, $usehtmleditor, $context) + ?>
diff --git a/mod/glossary/edit.php b/mod/glossary/edit.php index 69a82219df..accc444489 100644 --- a/mod/glossary/edit.php +++ b/mod/glossary/edit.php @@ -43,6 +43,8 @@ if (!$glossary->studentcanpost && !has_capability('mod/glossary:manageentries', } if ( $confirm ) { $form = data_submitted(); + trusttext_after_edit($form->text, $context); + if ( !isset($form->usedynalink) ) { $form->usedynalink = 0; } @@ -245,6 +247,7 @@ if ( $confirm ) { $newentry->userid = $form->userid; $newentry->timecreated = $form->timecreated; + if ( $aliases = get_records("glossary_alias","entryid",$e) ) { foreach ($aliases as $alias) { $newentry->aliases .= $alias->alias . "\n"; @@ -332,7 +335,7 @@ $tab = GLOSSARY_ADDENTRY_VIEW; include("tabs.html"); if (!$e) { - require_capability('glossary_write', $context); + require_capability('mod/glossary:write', $context); } include("edit.html"); diff --git a/mod/glossary/import.php b/mod/glossary/import.php index 9ce562356e..f91bd6d846 100644 --- a/mod/glossary/import.php +++ b/mod/glossary/import.php @@ -212,7 +212,7 @@ $xmlentry = $xmlentries[$i]; unset($newentry); $newentry->concept = trim(addslashes($xmlentry['#']['CONCEPT'][0]['#'])); - $newentry->definition = addslashes($xmlentry['#']['DEFINITION'][0]['#']); + $newentry->definition = trusttext_strip(addslashes($xmlentry['#']['DEFINITION'][0]['#'])); if ( isset($xmlentry['#']['CASESENSITIVE'][0]['#']) ) { $newentry->casesensitive = addslashes($xmlentry['#']['CASESENSITIVE'][0]['#']); } else { diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index a70c91de91..b987daea92 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -588,13 +588,36 @@ function glossary_print_entry($course, $cm, $glossary, $entry, $mode='',$hook='' //Default (old) print format used if custom function doesn't exist in format function glossary_print_entry_default ($entry) { echo ''. strip_tags($entry->concept) . ': '; + + $definition = $entry->definition; + + // always detect and strip TRUSTTEXT marker before processing and add+strip it afterwards! + if (trusttext_present($definition)) { + $ttpresent = true; + $definition = trusttext_strip($definition); + } else { + $ttpresent = false; + } + + $definition = '' . strip_tags($definition) . ''; + + // reconstruct the TRUSTTEXT properly after processing + if ($ttpresent) { + $definition = trusttext_mark($definition); + } else { + $definition = trusttext_strip($definition); //make 100% sure TRUSTTEXT marker was not created + } + + $options = new object(); $options->para = false; - $definition = format_text('' . strip_tags($entry->definition) . '', $entry->format,$options); + $options->trusttext = true; + $definition = format_text($definition, $entry->format, $options); echo ($definition); echo '

'; } function glossary_print_entry_concept($entry) { + $options = new object(); $options->para = false; $text = format_text('' . $entry->concept . '', FORMAT_MOODLE, $options); if (!empty($entry->highlight)) { @@ -607,6 +630,14 @@ function glossary_print_entry_definition($entry) { $definition = $entry->definition; + // always detect and strip TRUSTTEXT marker before processing and add+strip it afterwards! + if (trusttext_present($definition)) { + $ttpresent = true; + $definition = trusttext_strip($definition); + } else { + $ttpresent = false; + } + $links = array(); $tags = array(); $urls = array(); @@ -702,9 +733,18 @@ function glossary_print_entry_definition($entry) { $definition = str_replace(array_keys($links),$links,$definition); } + $options = new object(); $options->para = false; + $options->trusttext = true; + + // reconstruct the TRUSTTEXT properly after processing + if ($ttpresent) { + $definition = trusttext_mark($definition); + } else { + $definition = trusttext_strip($definition); //make 100% sure TRUSTTEXT marker was not created + } - $text = format_text($definition, $entry->format,$options); + $text = format_text($definition, $entry->format, $options); if (!empty($entry->highlight)) { $text = highlight($entry->highlight, $text); } @@ -1537,7 +1577,9 @@ function glossary_print_comment($course, $cm, $glossary, $entry, $comment) { echo ' '; echo '
'; - echo format_text($comment->comment, $comment->format); + $options = new object(); + $options->trusttext = true; + echo format_text($comment->comment, $comment->format, $options); echo '
'; @@ -1692,7 +1734,7 @@ function glossary_generate_export_file($glossary, $hook = "", $hook = 0) { if ( $entry->approved and $permissiongranted ) { $co .= glossary_start_tag("ENTRY",3,true); $co .= glossary_full_tag("CONCEPT",4,false,trim($entry->concept)); - $co .= glossary_full_tag("DEFINITION",4,false,$entry->definition); + $co .= glossary_full_tag("DEFINITION",4,false,trusttext_strip($entry->definition)); $co .= glossary_full_tag("FORMAT",4,false,$entry->format); $co .= glossary_full_tag("USEDYNALINK",4,false,$entry->usedynalink); $co .= glossary_full_tag("CASESENSITIVE",4,false,$entry->casesensitive); diff --git a/version.php b/version.php index cbd51bd4d3..7a6f70b5eb 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2006082300; // YYYYMMDD = date + $version = 2006082600; // YYYYMMDD = date // XY = increments within a single day $release = '1.7 dev'; // Human-friendly version name -- 2.39.5