diff --git a/question/format/xml/format.php b/question/format/xml/format.php index ed6420191c0..d854ce41d81 100644 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -1055,6 +1055,10 @@ class qformat_xml extends qformat_default { */ public function xml_escape($string) { if (!empty($string) && htmlspecialchars($string) != $string) { + // If the string contains something that looks like the end + // of a CDATA section, then we need to avoid errors by splitting + // the string between two CDATA sections. + $string = str_replace(']]>', ']]]]>', $string); return ""; } else { return $string; diff --git a/question/format/xml/tests/xmlformat_test.php b/question/format/xml/tests/xmlformat_test.php index 429f7876724..fbaa93d00c6 100644 --- a/question/format/xml/tests/xmlformat_test.php +++ b/question/format/xml/tests/xmlformat_test.php @@ -111,6 +111,40 @@ class qformat_xml_test extends question_testcase { return $newvar; } + public function test_xml_escape_simple_input_not_escaped() { + $exporter = new qformat_xml(); + $string = 'Nothing funny here. Even if we go to a café or to 日本.'; + $this->assertEquals($string, $exporter->xml_escape($string)); + } + + public function test_xml_escape_html_wrapped_in_cdata() { + $exporter = new qformat_xml(); + $string = '

Nothing funny here. Even if we go to a café or to 日本.

'; + $this->assertEquals('', $exporter->xml_escape($string)); + } + + public function test_xml_escape_script_tag_handled_ok() { + $exporter = new qformat_xml(); + $input = ''; + $expected = ']]>'; + $this->assertEquals($expected, $exporter->xml_escape($input)); + + // Check that parsing the expected result does give the input again. + $parsed = simplexml_load_string('
' . $expected . '
'); + $this->assertEquals($input, $parsed->xpath('//div')[0]); + } + + public function test_xml_escape_code_that_looks_like_cdata_end_ok() { + $exporter = new qformat_xml(); + $input = "if (x[[0]]>a) print('hah');"; + $expected = "a) print('hah');]]>"; + $this->assertEquals($expected, $exporter->xml_escape($input)); + + // Check that parsing the expected result does give the input again. + $parsed = simplexml_load_string('
' . $expected . '
'); + $this->assertEquals($input, $parsed->xpath('//div')[0]); + } + public function test_write_hint_basic() { $q = $this->make_test_question(); $q->name = 'Short answer question';