Skip to content

Commit ff87e4e

Browse files
authored
CONTRIB-9171 Safely decode score calculation field (#462)
* CONTRIB-9171 Safely deserialize scores. * CONTRIB-9171 Using safer unserialize_array().
1 parent d1e3fbe commit ff87e4e

File tree

4 files changed

+38
-11
lines changed

4 files changed

+38
-11
lines changed

backup/moodle2/restore_questionnaire_stepslib.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//
1414
// You should have received a copy of the GNU General Public License
1515
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
use mod_questionnaire\feedback\section;
1617

1718
/**
1819
* Define all the restore steps that will be used by the restore_questionnaire_activity_task.
@@ -189,7 +190,7 @@ protected function process_questionnaire_fb_sections($data) {
189190

190191
// If this questionnaire has separate sections feedbacks.
191192
if (isset($data->scorecalculation)) {
192-
$scorecalculation = unserialize($data->scorecalculation);
193+
$scorecalculation = section::decode_scorecalculation($data->scorecalculation);
193194
$newscorecalculation = array();
194195
foreach ($scorecalculation as $qid => $val) {
195196
$newqid = $this->get_mappingid('questionnaire_question', $qid);

classes/feedback/section.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public function load_section($params) {
143143
$this->id = $feedbackrec->id;
144144
$this->surveyid = $feedbackrec->surveyid;
145145
$this->section = $feedbackrec->section;
146-
$this->scorecalculation = $this->decode_scorecalculation($feedbackrec->scorecalculation);
146+
$this->scorecalculation = $this->get_valid_scorecalculation($feedbackrec->scorecalculation);
147147
$this->sectionlabel = $feedbackrec->sectionlabel;
148148
$this->sectionheading = $feedbackrec->sectionheading;
149149
$this->sectionheadingformat = $feedbackrec->sectionheadingformat;
@@ -253,20 +253,20 @@ public function update() {
253253

254254
$this->scorecalculation = $this->encode_scorecalculation($this->scorecalculation);
255255
$DB->update_record(self::TABLE, $this);
256-
$this->scorecalculation = $this->decode_scorecalculation($this->scorecalculation);
256+
$this->scorecalculation = $this->get_valid_scorecalculation($this->scorecalculation);
257257

258258
foreach ($this->sectionfeedback as $sectionfeedback) {
259259
$sectionfeedback->update();
260260
}
261261
}
262262

263263
/**
264-
* Return the decoded calculation array/
265-
* @param string $codedstring
266-
* @return mixed
264+
* Decode and ensure scorecalculation is what we expect.
265+
* @param string|null $codedstring
266+
* @return array
267267
* @throws coding_exception
268268
*/
269-
protected function decode_scorecalculation($codedstring) {
269+
public static function decode_scorecalculation(?string $codedstring): array {
270270
// Expect a serialized data string.
271271
if (($codedstring == null)) {
272272
$codedstring = '';
@@ -275,11 +275,33 @@ protected function decode_scorecalculation($codedstring) {
275275
throw new coding_exception('Invalid scorecalculation format.');
276276
}
277277
if (!empty($codedstring)) {
278-
$scorecalculation = unserialize($codedstring);
278+
$scorecalculation = unserialize_array($codedstring) ?: [];
279279
} else {
280280
$scorecalculation = [];
281281
}
282282

283+
if (!is_array($scorecalculation)) {
284+
throw new coding_exception('Invalid scorecalculation format.');
285+
}
286+
287+
foreach ($scorecalculation as $score) {
288+
if (!empty($score) && !is_numeric($score)) {
289+
throw new coding_exception('Invalid scorecalculation format.');
290+
}
291+
}
292+
293+
return $scorecalculation;
294+
}
295+
296+
/**
297+
* Return the decoded and validated calculation array.
298+
* @param string $codedstring
299+
* @return mixed
300+
* @throws coding_exception
301+
*/
302+
protected function get_valid_scorecalculation($codedstring) {
303+
$scorecalculation = static::decode_scorecalculation($codedstring);
304+
283305
// Check for deleted questions and questions that don't support scores.
284306
foreach ($scorecalculation as $qid => $score) {
285307
if (!isset($this->questions[$qid])) {

classes/question/sectiontext.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
namespace mod_questionnaire\question;
1818

19+
use mod_questionnaire\feedback\section;
20+
1921
/**
2022
* This file contains the parent class for sectiontext question types.
2123
*
@@ -101,7 +103,7 @@ protected function question_survey_display($response, $descendantsdata, $blankqu
101103

102104
// In which section(s) is this question?
103105
foreach ($fbsections as $key => $fbsection) {
104-
if ($scorecalculation = unserialize($fbsection->scorecalculation)) {
106+
if ($scorecalculation = section::decode_scorecalculation($fbsection->scorecalculation)) {
105107
if (array_key_exists($this->id, $scorecalculation)) {
106108
array_push($filteredsections, $fbsection->section);
107109
}

questionnaire.class.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1616

17+
use mod_questionnaire\feedback\section;
18+
1719
defined('MOODLE_INTERNAL') || die();
1820

1921
require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');
@@ -1795,7 +1797,7 @@ public function survey_copy($owner) {
17951797
$fbsections = $DB->get_records('questionnaire_fb_sections', ['surveyid' => $this->survey->id], 'id');
17961798
foreach ($fbsections as $fbsid => $fbsection) {
17971799
$fbsection->surveyid = $newsid;
1798-
$scorecalculation = unserialize($fbsection->scorecalculation);
1800+
$scorecalculation = section::decode_scorecalculation($fbsection->scorecalculation);
17991801
$newscorecalculation = [];
18001802
foreach ($scorecalculation as $qid => $val) {
18011803
$newscorecalculation[$qidarray[$qid]] = $val;
@@ -3880,7 +3882,7 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
38803882
foreach ($fbsections as $key => $fbsection) {
38813883
if ($fbsection->section == $section) {
38823884
$feedbacksectionid = $key;
3883-
$scorecalculation = unserialize($fbsection->scorecalculation);
3885+
$scorecalculation = section::decode_scorecalculation($fbsection->scorecalculation);
38843886
if (empty($scorecalculation) && !is_array($scorecalculation)) {
38853887
$scorecalculation = [];
38863888
}

0 commit comments

Comments
 (0)