From 91ff3c6323d1363cca43a82a11f770fbd9997d15 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 26 Jan 2017 14:41:03 +0900 Subject: [PATCH] Add array_escape() function to escape all keys and values of an array or object --- common/functions.php | 34 +++++++++++++++++++++++--- tests/unit/functions/FunctionsTest.php | 10 ++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/common/functions.php b/common/functions.php index cd55a352c..2884dddf1 100644 --- a/common/functions.php +++ b/common/functions.php @@ -96,6 +96,35 @@ function array_last_key(array $array) return key($array); } +/** + * Escape all keys and values in a multi-dimensional array. + * + * @param array $array The array to escape + * @param bool $double_escape Set this to false to skip symbols that are already escaped (default: true) + * @return array + */ +function array_escape(array $array, $double_escape = true) +{ + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + $result = array(); + foreach ($array as $key => $value) + { + if (is_array($value)) + { + $result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = array_escape($value, $double_escape, $flags); + } + elseif (is_object($value)) + { + $result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = (object)array_escape(get_object_vars($value), $double_escape, $flags); + } + else + { + $result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = htmlspecialchars($value, $flags, 'UTF-8', $double_escape); + } + } + return $result; +} + /** * Flatten a multi-dimensional array into a one-dimensional array. * Based on util.php @@ -155,7 +184,7 @@ function clean_path($path) */ function escape($str, $double_escape = true) { - $flags = defined('ENT_SUBSTITUTE') ? (ENT_QUOTES | ENT_SUBSTITUTE) : (ENT_QUOTES | ENT_IGNORE); + $flags = ENT_QUOTES | ENT_SUBSTITUTE; return htmlspecialchars($str, $flags, 'UTF-8', $double_escape); } @@ -178,8 +207,7 @@ function escape_css($str) */ function escape_js($str) { - $flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT; - if (defined('JSON_UNESCAPED_UNICODE')) $flags = $flags | JSON_UNESCAPED_UNICODE; + $flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE; $str = json_encode((string)$str, $flags); return substr($str, 1, strlen($str) - 2); } diff --git a/tests/unit/functions/FunctionsTest.php b/tests/unit/functions/FunctionsTest.php index e89a73977..db6a1f748 100644 --- a/tests/unit/functions/FunctionsTest.php +++ b/tests/unit/functions/FunctionsTest.php @@ -18,6 +18,14 @@ class FunctionsTest extends \Codeception\TestCase\Test $this->assertEquals($flattened2, array_flatten($array, false)); } + public function testArrayEscape() + { + $this->assertEquals(array('foo<' => 'bar>', 'baz"baz' => array('fuzz&amp;bazz' => '<rhymix>')), array_escape(array('foo<' => 'bar>', 'baz"baz' => array('fuzz&bazz' => '')))); + $this->assertEquals(array('invalid' => 'unicode' . "\xEF\xBF\xBD", 'other' => array('key&key')), array_escape(array('invalid' => 'unicode' . "\xE4\xA8", 'other' => array('key&key')), false)); + $this->assertEquals(array('object' => (object)array('foo>' => 'bar<', 'baz"' => '&amp;')), array_escape(array('object' => (object)array('foo>' => 'bar<', 'baz"' => '&')))); + $this->assertEquals(array('object' => (object)array('foo>' => array('bar<' => array('&')))), array_escape(array('object' => (object)array('foo>' => array('bar<' => array('&')))), false)); + } + public function testClassBasename() { $this->assertEquals('FunctionsTest', class_basename($this)); @@ -28,6 +36,8 @@ class FunctionsTest extends \Codeception\TestCase\Test { $this->assertEquals('<foo>&amp;</foo>', escape('&')); $this->assertEquals('<foo>&</foo>', escape('&', false)); + $this->assertEquals('<foo>invalid'. "\xEF\xBF\xBD" . 'unicode</foo>', escape('invalid' . "\xE4\xA8" . 'unicode')); + $this->assertEquals('<foo>invalid'. "\xEF\xBF\xBD" . 'unicode</foo>', escape('invalid' . "\xE4\xA8" . 'unicode</foo>', false)); $this->assertEquals('expressionalertXSS', escape_css('expression:alert("XSS")')); $this->assertEquals('#123456', escape_css('#123456'));