MDL-82576 qtype_calculated: Improve formula regexes
authorMichael Hawkins <michaelh@moodle.com>
Fri, 2 Aug 2024 09:34:39 +0000 (17:34 +0800)
committerHuong Nguyen <huongnv13@gmail.com>
Thu, 8 Aug 2024 04:11:02 +0000 (11:11 +0700)
question/type/calculated/questiontype.php

index 7bd48f6..bda3599 100644 (file)
@@ -39,9 +39,9 @@ require_once($CFG->dirroot . '/question/type/numerical/question.php');
  */
 class qtype_calculated extends question_type {
     /**
-     * @var string a placeholder is a letter, followed by almost any characters. (This should probably be restricted more.)
+     * @var string a placeholder is a letter, followed by zero or more alphanum chars (as well as space, - and _ for readability).
      */
-    const PLACEHOLDER_REGEX_PART = '[[:alpha:]][^>} <`{"\']*';
+    const PLACEHOLDER_REGEX_PART = '[[:alpha:]][[:alpha:][:digit:]\-_\s]*';
 
     /**
      * @var string REGEXP for a placeholder, wrapped in its {...} delimiters, with capturing brackets around the name.
@@ -1952,15 +1952,18 @@ function qtype_calculated_find_formula_errors($formula) {
     // Validates the formula submitted from the question edit page.
     // Returns false if everything is alright
     // otherwise it constructs an error message.
-    // Strip away dataset names. Use 1.0 to catch illegal concatenation like {a}{b}.
+    // Strip away dataset names. Use 1.0 to remove valid names, so illegal names can be identified later.
     $formula = preg_replace(qtype_calculated::PLACEHODLER_REGEX, '1.0', $formula);
 
     // Strip away empty space and lowercase it.
     $formula = strtolower(str_replace(' ', '', $formula));
 
-    $safeoperatorchar = '-+/*%>:^\~<?=&|!'; /* */
+    // Only mathematical operators are supported. Bitwise operators are not safe.
+    // Note: In this context, ^ is a bitwise operator (exponents are represented by **).
+    $safeoperatorchar = '-+/*%>:\~<?=!';
     $operatorornumber = "[{$safeoperatorchar}.0-9eE]";
 
+    // Validate mathematical functions in formula.
     while (preg_match("~(^|[{$safeoperatorchar},(])([a-z0-9_]*)" .
             "\\(({$operatorornumber}+(,{$operatorornumber}+((,{$operatorornumber}+)+)?)?)?\\)~",
             $formula, $regs)) {