From cf52280c33642ad47ecd37cc67fa070c844a1e95 Mon Sep 17 00:00:00 2001 From: Rida Abou-Haidar Date: Wed, 20 May 2020 13:39:05 -0400 Subject: [PATCH] Fix data in fields where HTML characters were double escaped on 22 (#6477) This PR fixes the issue where special characters entered in instrument forms were double escaped on saving but improperly decoded on instrument loading causing each save to prepend an & escaping string to the already escaped characters. the problematic chars are &, <, > and ". The escaping is done in the deleted lines as well as the insert and update functions of the database class making this first instance unnecessary. A script has also been added to help projects identify if the bug has affected their data. Amending the data in the database will remain the responsibility of each project since it is highly specific to each project. --- php/libraries/LorisForm.class.inc | 4 - .../instrument_double_escape_report.php | 225 ++++++++++++++++++ 2 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 tools/single_use/instrument_double_escape_report.php diff --git a/php/libraries/LorisForm.class.inc b/php/libraries/LorisForm.class.inc index 6f1f0d76f27..b2b0ecc677a 100644 --- a/php/libraries/LorisForm.class.inc +++ b/php/libraries/LorisForm.class.inc @@ -693,10 +693,6 @@ class LorisForm } } } - // Always sanitize user-controlled input - if (!is_array($newValue)) { - $newValue = htmlspecialchars($newValue); - } return $newValue; } diff --git a/tools/single_use/instrument_double_escape_report.php b/tools/single_use/instrument_double_escape_report.php new file mode 100644 index 00000000000..d584f60fc2d --- /dev/null +++ b/tools/single_use/instrument_double_escape_report.php @@ -0,0 +1,225 @@ +pselectCol("SELECT Test_name FROM test_names", array()); +// Array of all fields containing any escaped characters +$escapedEntries = array(); +// Array of database tables and columns containing escaped characters. +$escapedFields = array(); +// Array of confirmed truncations based on size of fields and content +$confirmedTruncations = array(); +// Boolean flag for identify non-impacted databases and terminating. +$errorsDetected = false; +// Array of CHARACTER_MAXIMUM_LENGTH for each affected field. +$maxFieldLengths = array(); + +$databaseName = $config->getSetting('database')['database']; + +// FIRST loop just reporting all potential problematic fields +foreach($instrumentNames as $instrumentName) { + printOut("Checking $instrumentName"); + + //default value for table name + $tableName=$instrumentName; + + $instrumentCIDs = $DB->pselectCol( + "SELECT CommentID FROM flag WHERE Test_name=:tn", + array("tn" => $instrumentName) + ); + + $instrumentData=array(); + if ($useObjects) { + try { + $instrument = \NDB_BVL_Instrument::factory($instrumentName); + } catch (Exception $e) { + printError( + "There was an error instantiating instrument $instrumentName. + This instrument will be skipped." + ); + printError($e->getMessage()); + continue; + } + foreach ($instrumentCIDs as $cid) { + $instrumentInstance = \NDB_BVL_Instrument::factory($instrumentName, $cid); + $instrumentCandData = \NDB_BVL_Instrument::loadInstanceData($instrumentInstance); + + // instrument name and table name might differ + $tableName = $instrumentInstance->table; + $instrumentData[$cid] = $instrumentCandData; + } + } else if ($useDatabase) { + //Check if table by that name exists + if(!$DB->tableExists($instrumentName)) { + printError("No table by the name `$instrumentName` was found in the + database. This instrument will be skipped"); + }; + $instrumentData = $DB->pselectWithIndexKey( + "SELECT * FROM $instrumentName", + array(), + "CommentID" + ); + } + // Go through all fields and identify which have any escaped characters + foreach ($instrumentData as $cid => $instrumentCandData) { + foreach ($instrumentCandData as $field => $value) { + // regex detecting any escaped character in the database + if (!empty($value) && preg_match('/&(amp;)+(gt;|lt;|quot;|amp;)/', $value)) { + $escapedEntries[$tableName][$cid][$field] = $value; + $escapedFields[$tableName][] = $field; + $errorsDetected = true; + } + } + } +} + +//SECOND loop, depending on flags, report or fix values. +if ($errorsDetected) { + foreach ($escapedFields as $tableName => $fieldsArray) { + $fieldsList = "'" . implode($fieldsArray, "','") . "'"; + + $maxFieldLengths[$tableName] = $DB->pselectWithIndexKey( + "SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_MAXIMUM_LENGTH + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA=:dbn AND TABLE_NAME=:tbl AND COLUMN_NAME IN ($fieldsList)", + array('dbn'=>$databaseName,'tbl' => $tableName), + 'COLUMN_NAME' + ); + } + + // Start comparing the value length of the escaped entry to the character maximum for that field + // In order to begin identifying cases of truncation + foreach ($escapedEntries as $tableName => $rows) { + foreach ($rows as $cid => $entries) { + foreach ($entries as $field => $value) { + if (strlen($value) == $maxFieldLengths[$tableName][$field]['CHARACTER_MAXIMUM_LENGTH']) { + $confirmedTruncations[$tableName][$cid][$field] = $value; + } + } + } + } + + if(!empty($escapedEntries)) { + printOut( + "Below is a list of all entries in the database instruemnts which " . + "contain escaped characters" + ); + print_r($escapedEntries); + print_r("\n\n"); + } + if (!empty($confirmedTruncations)) { + printOut( + "Below is the list of truncations automatically detected. This " . + "list might not be exhaustive, truncation can occur without being " . + "automatically detected by this script." + ); + print_r($confirmedTruncations); + } +} else { + printOut("No errors have been detected. End !"); +} + +fclose($logfp); + + +/* + * Prints to log file + */ +function logMessage($message) +{ + global $logfp; + if (!$logfp) { + //The log file could not be instantiated + //use print instead + print_r($message); + } + $now_string = strftime("%Y-%m-%d %H:%M:%S"); + fwrite($logfp, "[$now_string] $message\n"); + +} + +/* + * Prints to STDERR + */ +function printError($message) +{ + logMessage($message); + fwrite(STDERR, "$message \n"); +} + +/* + * Prints to STDOUT + */ +function printOut($message) +{ + logMessage($message); + print_r("$message\n"); +} + +function showHelp() +{ + echo "\n\n*** Fix Double Escaped Fields ***\n\n"; + + echo "Usage: + instrument_double_escape_report.php [help | -h] -> displays this message + instrument_double_escape_report.php use-database -> Runs the reporter using only the database instrument names and tables + instrument_double_escape_report.php use-objects -> Runs the reporter using the database and instantiating Instrument objects. + + Note: in the event where use-objects fails, try use-database. + \n\n"; + + die(); +}