From 020281d932c566476222e6c825ada3affff239a6 Mon Sep 17 00:00:00 2001 From: Andy Staudacher Date: Sat, 29 Aug 2009 10:45:47 -0700 Subject: Adding SafeString which is going to replace p::clean() and p::purify(). Refactoring of Xss_Security_Test. t() and t2() return a SafeString instance. TODO: - Update all code to use SafeString where appropriate. - Update golden fole of Xss_Security_Test - Stop reporting CLEAN vars in Xss_Security_Test --- modules/gallery/tests/SafeString_Test.php | 111 ++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 modules/gallery/tests/SafeString_Test.php (limited to 'modules/gallery/tests/SafeString_Test.php') diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php new file mode 100644 index 00000000..cdae3e99 --- /dev/null +++ b/modules/gallery/tests/SafeString_Test.php @@ -0,0 +1,111 @@ +world

"); + $this->assert_true($safe_string instanceof SafeString); + $this->assert_equal("hello

world

", + $safe_string->unescaped()); + } + + public function toString_escapes_for_html_test() { + $safe_string = new SafeString("hello

world

"); + $this->assert_equal("hello <p>world</p>", + $safe_string); + } + + public function toString_for_safe_string_test() { + $safe_string = new SafeString("hello

world

"); + $safe_string->mark_html_safe(); + $this->assert_equal("hello

world

", + $safe_string); + } + + public function for_html_test() { + $safe_string = new SafeString("hello

world

"); + $this->assert_equal("hello <p>world</p>", + $safe_string->for_html()); + } + + public function safestring_of_safestring_test() { + $safe_string = new SafeString("hello

world

"); + $safe_string_2 = new SafeString($safe_string); + $this->assert_true($safe_string_2 instanceof SafeString); + $raw_string = $safe_string_2->unescaped(); + $this->assert_false(is_object($raw_string)); + $this->assert_equal("hello

world

", $raw_string); + $this->assert_equal("hello <p>world</p>", $safe_string_2); + } + + public function for_js_test() { + $safe_string = new SafeString('"Foo\'s bar"'); + $js_string = $safe_string->for_js(); + $this->assert_equal('\\"Foo<\\/em>\\\'s bar\\"', + $js_string); + } + + public function string_safestring_equality_test() { + $safe_string = new SafeString("hello

world

"); + $this->assert_equal("hello

world

", + $safe_string->unescaped()); + $escaped_string = "hello <p>world</p>"; + $this->assert_equal($escaped_string, $safe_string); + + $this->assert_true($escaped_string == $safe_string); + $this->assert_false($escaped_string === $safe_string); + $this->assert_false("meow" == $safe_string); + } + + public function of_test() { + $safe_string = SafeString::of("hello

world

"); + $this->assert_equal("hello

world

", $safe_string->unescaped()); + } + + public function of_safe_html_test() { + $safe_string = SafeString::of("hello

world

")->mark_html_safe(); + $this->assert_equal("hello

world

", $safe_string->for_html()); + } + + public function of_fluid_api_test() { + $escaped_string = SafeString::of("Foo's bar")->for_js(); + $this->assert_equal("Foo\\'s bar", $escaped_string); + } + + public function safestring_of_safestring_preserves_safe_status_test() { + $safe_string = SafeString::of("hello's

world

")->mark_html_safe(); + $safe_string_2 = new SafeString($safe_string); + $this->assert_equal("hello's

world

", $safe_string_2); + $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); + } + + public function safestring_of_safestring_preserves_html_safe_status_test() { + $safe_string = SafeString::of("hello's

world

") + ->mark_html_safe(); + $safe_string_2 = new SafeString($safe_string); + $this->assert_equal("hello's

world

", $safe_string_2); + $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); + } + + public function safestring_of_safestring_safe_status_override_test() { + $safe_string = new SafeString("hello

world

"); + $safe_string_2 = SafeString::of($safe_string)->mark_html_safe(); + $this->assert_equal("hello

world

", $safe_string_2); + } +} -- cgit v1.2.3 From 7adb9ea2e3a42e1c5472024a1699912ae26eacb3 Mon Sep 17 00:00:00 2001 From: Andy Staudacher Date: Sat, 29 Aug 2009 11:48:55 -0700 Subject: Adding SafeString::for_html_attr() --- modules/gallery/libraries/SafeString.php | 19 +++++++++++++++++++ modules/gallery/tests/SafeString_Test.php | 21 ++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) (limited to 'modules/gallery/tests/SafeString_Test.php') diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php index 53bcb27a..709ab5f6 100644 --- a/modules/gallery/libraries/SafeString.php +++ b/modules/gallery/libraries/SafeString.php @@ -89,6 +89,25 @@ class SafeString_Core { return self::_escape_for_js($this->_raw_string); } + /** + * Safe for use in HTML element attributes. + * + * Assumes that the HTML element attribute is already + * delimited by single or double quotes + * + * Example:
+   *     ;
+   *   
+   * 
+ * @return the string escaped for use in HTML attributes. + */ + function for_html_attr() { + $string = (string) $this->for_html(); + return strtr($string, + array("'"=>"'", + '"'=>'"')); + } + /** * Safe for use HTML (purified HTML) * diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php index cdae3e99..73d82c34 100644 --- a/modules/gallery/tests/SafeString_Test.php +++ b/modules/gallery/tests/SafeString_Test.php @@ -18,13 +18,6 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class SafeString_Test extends Unit_Test_Case { - public function p_clean_returns_safestring_instance_test() { - $safe_string = p::clean("hello

world

"); - $this->assert_true($safe_string instanceof SafeString); - $this->assert_equal("hello

world

", - $safe_string->unescaped()); - } - public function toString_escapes_for_html_test() { $safe_string = new SafeString("hello

world

"); $this->assert_equal("hello <p>world</p>", @@ -61,6 +54,20 @@ class SafeString_Test extends Unit_Test_Case { $js_string); } + public function for_html_attr_test() { + $safe_string = new SafeString('"Foo\'s bar"'); + $attr_string = $safe_string->for_html_attr(); + $this->assert_equal('"<em>Foo</em>'s bar"', + $attr_string); + } + + public function for_html_attr_with_safe_html_test() { + $safe_string = SafeString::of('"Foo\'s bar"')->mark_html_safe(); + $attr_string = $safe_string->for_html_attr(); + $this->assert_equal('"Foo's bar"', + $attr_string); + } + public function string_safestring_equality_test() { $safe_string = new SafeString("hello

world

"); $this->assert_equal("hello

world

", -- cgit v1.2.3 From a10063ff68cf5988297dcad889384ab2080c3850 Mon Sep 17 00:00:00 2001 From: Andy Staudacher Date: Sat, 29 Aug 2009 12:34:09 -0700 Subject: Add more factory methods for convenience: SafeString::purify() and SafeString::of_safe_html(). Removing SafeString::mark_html_safe() since it's no longer needed. --- modules/gallery/helpers/MY_url.php | 10 +++++----- modules/gallery/libraries/I18n.php | 2 +- modules/gallery/libraries/SafeString.php | 27 +++++++++++++++++++++------ modules/gallery/tests/SafeString_Test.php | 19 +++++++++++-------- modules/gallery/tests/Xss_Security_Test.php | 7 +++++-- 5 files changed, 43 insertions(+), 22 deletions(-) (limited to 'modules/gallery/tests/SafeString_Test.php') diff --git a/modules/gallery/helpers/MY_url.php b/modules/gallery/helpers/MY_url.php index b4b7f352..6092a9d8 100644 --- a/modules/gallery/helpers/MY_url.php +++ b/modules/gallery/helpers/MY_url.php @@ -31,7 +31,7 @@ class url extends url_Core { $uri = model_cache::get("item", $parts[1])->relative_path(); } $url = parent::site($uri . $query, $protocol); - return SafeString::of($url)->mark_html_safe(); + return SafeString::of_safe_html($url); } static function parse_url() { @@ -103,22 +103,22 @@ class url extends url_Core { public static function base($index=false, $protocol=false) { $url = parent::base($index, $protocol); - return SafeString::of($url)->mark_html_safe(); + return SafeString::of_safe_html($url); } public static function current($qs=false) { $url = parent::current($qs); - return SafeString::of($url)->mark_html_safe(); + return SafeString::of_safe_html($url); } public static function file($file, $index=false) { $url = parent::file($file, $index); - return SafeString::of($url)->mark_html_safe(); + return SafeString::of_safe_html($url); } public static function merge(array $arguments) { $url = parent::merge($arguments); - return SafeString::of($url)->mark_html_safe(); + return SafeString::of_safe_html($url); } } diff --git a/modules/gallery/libraries/I18n.php b/modules/gallery/libraries/I18n.php index 8dc42e04..a53d5ae9 100644 --- a/modules/gallery/libraries/I18n.php +++ b/modules/gallery/libraries/I18n.php @@ -116,7 +116,7 @@ class I18n_Core { $entry = $this->interpolate($locale, $entry, $values); - return SafeString::of($entry)->mark_html_safe(); + return SafeString::of_safe_html($entry); } private function lookup($locale, $message) { diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php index 709ab5f6..9a269ed4 100644 --- a/modules/gallery/libraries/SafeString.php +++ b/modules/gallery/libraries/SafeString.php @@ -24,6 +24,7 @@ class SafeString_Core { private $_raw_string; protected $_is_safe_html = false; + protected $_is_purified_html = false; private static $_purifier = null; @@ -44,11 +45,25 @@ class SafeString_Core { } /** - * Marks this string as safe to be used in HTML without any escaping. + * Factory method returning a new SafeString instance after HTML purifying + * the given string. */ - function mark_html_safe() { - $this->_is_safe_html = true; - return $this; + static function purify($string) { + if ($string instanceof SafeString) { + $string = $string->unescaped(); + } + $safe_string = self::of_safe_html(self::_purify_for_html($string)); + $safe_string->_is_purified_html = true; + return $safe_string; + } + + /** + * Factory method returning a new SafeString instance which won't HTML escape. + */ + static function of_safe_html($string) { + $safe_string = new SafeString($string); + $safe_string->_is_safe_html = true; + return $safe_string; } /** @@ -117,10 +132,10 @@ class SafeString_Core { * @return the string escaped for use in HTML. */ function purified_html() { - if ($this->_is_safe_html) { + if ($this->_is_purified_html) { return $this; } else { - return SafeString::of(self::_purify_for_html($this->_raw_string), true); + return self::purify($this); } } diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php index 73d82c34..0fc7f6f3 100644 --- a/modules/gallery/tests/SafeString_Test.php +++ b/modules/gallery/tests/SafeString_Test.php @@ -25,8 +25,7 @@ class SafeString_Test extends Unit_Test_Case { } public function toString_for_safe_string_test() { - $safe_string = new SafeString("hello

world

"); - $safe_string->mark_html_safe(); + $safe_string = SafeString::of_safe_html("hello

world

"); $this->assert_equal("hello

world

", $safe_string); } @@ -62,7 +61,7 @@ class SafeString_Test extends Unit_Test_Case { } public function for_html_attr_with_safe_html_test() { - $safe_string = SafeString::of('"Foo\'s bar"')->mark_html_safe(); + $safe_string = SafeString::of_safe_html('"Foo\'s bar"'); $attr_string = $safe_string->for_html_attr(); $this->assert_equal('"Foo's bar"', $attr_string); @@ -86,25 +85,29 @@ class SafeString_Test extends Unit_Test_Case { } public function of_safe_html_test() { - $safe_string = SafeString::of("hello

world

")->mark_html_safe(); + $safe_string = SafeString::of_safe_html("hello

world

"); $this->assert_equal("hello

world

", $safe_string->for_html()); } + public function purify_test() { + $safe_string = SafeString::purify("hello

world

"); + $this->assert_equal("hello

world

", $safe_string); + } + public function of_fluid_api_test() { $escaped_string = SafeString::of("Foo's bar")->for_js(); $this->assert_equal("Foo\\'s bar", $escaped_string); } public function safestring_of_safestring_preserves_safe_status_test() { - $safe_string = SafeString::of("hello's

world

")->mark_html_safe(); + $safe_string = SafeString::of_safe_html("hello's

world

"); $safe_string_2 = new SafeString($safe_string); $this->assert_equal("hello's

world

", $safe_string_2); $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); } public function safestring_of_safestring_preserves_html_safe_status_test() { - $safe_string = SafeString::of("hello's

world

") - ->mark_html_safe(); + $safe_string = SafeString::of_safe_html("hello's

world

"); $safe_string_2 = new SafeString($safe_string); $this->assert_equal("hello's

world

", $safe_string_2); $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); @@ -112,7 +115,7 @@ class SafeString_Test extends Unit_Test_Case { public function safestring_of_safestring_safe_status_override_test() { $safe_string = new SafeString("hello

world

"); - $safe_string_2 = SafeString::of($safe_string)->mark_html_safe(); + $safe_string_2 = SafeString::of_safe_html($safe_string); $this->assert_equal("hello

world

", $safe_string_2); } } diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php index e0e5bb86..fd596c69 100644 --- a/modules/gallery/tests/Xss_Security_Test.php +++ b/modules/gallery/tests/Xss_Security_Test.php @@ -110,10 +110,13 @@ class Xss_Security_Test extends Unit_Test_Case { } else if ($token[1] == "SafeString") { // Looking for SafeString::of(... if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING, "of"), $tokens, $token_number + 2) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], array("of", "of_safe_html", "purify")) && self::_token_matches("(", $tokens, $token_number + 3)) { $frame->is_safestring(true); - $frame->expr_append("::of("); + + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("::$method("); $token_number += 3; $token = $tokens[$token_number]; -- cgit v1.2.3 From beb711d6a0fedac0d4ca3b9bae162a6ce9d6cdeb Mon Sep 17 00:00:00 2001 From: Andy Staudacher Date: Sun, 30 Aug 2009 15:21:02 -0700 Subject: Rename clean_js to js_string and have it return a complete JS string (with delimiters) instead of just the string contents. Benefits: Using json_encode(), which is very robust. And as a user, it's clearer how to use this API compared to what it was before. --- modules/gallery/helpers/MY_html.php | 4 ++-- modules/gallery/libraries/SafeString.php | 14 +++----------- modules/gallery/tests/Html_Helper_Test.php | 6 +++--- modules/gallery/tests/SafeString_Test.php | 8 ++++---- modules/gallery/tests/Xss_Security_Test.php | 4 ++-- 5 files changed, 14 insertions(+), 22 deletions(-) (limited to 'modules/gallery/tests/SafeString_Test.php') diff --git a/modules/gallery/helpers/MY_html.php b/modules/gallery/helpers/MY_html.php index 75114898..4522d01c 100644 --- a/modules/gallery/helpers/MY_html.php +++ b/modules/gallery/helpers/MY_html.php @@ -65,11 +65,11 @@ class html extends html_Core { * * Example:
    *   
    * 
*/ - static function clean_js($string) { + static function js_string($string) { return SafeString::of($string)->for_js(); } diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php index 9614a213..0767a665 100644 --- a/modules/gallery/libraries/SafeString.php +++ b/modules/gallery/libraries/SafeString.php @@ -92,17 +92,17 @@ class SafeString_Core { } /** - * Safe for use in JavaScript. + * Safe for use as JavaScript string. * * Example:
    *   
    * 
* @return the string escaped for use in JavaScript. */ function for_js() { - return self::_escape_for_js($this->_raw_string); + return json_encode((string) $this->_raw_string); } /** @@ -152,14 +152,6 @@ class SafeString_Core { return html::specialchars($dirty_html); } - // Escapes special chars (quotes, backslash, etc.) with a backslash sequence. - private static function _escape_for_js($string) { - // From Smarty plugins/modifier.escape.php - // Might want to be stricter here. - return strtr($string, - array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n',''<\/')); - } - // Purifies the string, removing any potentially malicious or unsafe HTML / JavaScript. private static function _purify_for_html($dirty_html) { if (empty(self::$_purifier)) { diff --git a/modules/gallery/tests/Html_Helper_Test.php b/modules/gallery/tests/Html_Helper_Test.php index a9903256..f5ce7fa4 100644 --- a/modules/gallery/tests/Html_Helper_Test.php +++ b/modules/gallery/tests/Html_Helper_Test.php @@ -40,9 +40,9 @@ class Html_Helper_Test extends Unit_Test_Case { $safe_string_2); } - public function clean_js_test() { - $string = html::clean_js("hello's

world

"); - $this->assert_equal("hello\\'s

world<\\/p>", + public function js_string_test() { + $string = html::js_string("hello's

world

"); + $this->assert_equal('"hello\'s

world<\\/p>"', $string); } diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php index 0fc7f6f3..ede55240 100644 --- a/modules/gallery/tests/SafeString_Test.php +++ b/modules/gallery/tests/SafeString_Test.php @@ -49,7 +49,7 @@ class SafeString_Test extends Unit_Test_Case { public function for_js_test() { $safe_string = new SafeString('"Foo\'s bar"'); $js_string = $safe_string->for_js(); - $this->assert_equal('\\"Foo<\\/em>\\\'s bar\\"', + $this->assert_equal('"\\"Foo<\\/em>\'s bar\\""', $js_string); } @@ -96,21 +96,21 @@ class SafeString_Test extends Unit_Test_Case { public function of_fluid_api_test() { $escaped_string = SafeString::of("Foo's bar")->for_js(); - $this->assert_equal("Foo\\'s bar", $escaped_string); + $this->assert_equal('"Foo\'s bar"', $escaped_string); } public function safestring_of_safestring_preserves_safe_status_test() { $safe_string = SafeString::of_safe_html("hello's

world

"); $safe_string_2 = new SafeString($safe_string); $this->assert_equal("hello's

world

", $safe_string_2); - $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); + $this->assert_equal('"hello\'s

world<\\/p>"', $safe_string_2->for_js()); } public function safestring_of_safestring_preserves_html_safe_status_test() { $safe_string = SafeString::of_safe_html("hello's

world

"); $safe_string_2 = new SafeString($safe_string); $this->assert_equal("hello's

world

", $safe_string_2); - $this->assert_equal("hello\\'s

world<\\/p>", $safe_string_2->for_js()); + $this->assert_equal('"hello\'s

world<\\/p>"', $safe_string_2->for_js()); } public function safestring_of_safestring_safe_status_override_test() { diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php index b385580d..3a22afc1 100644 --- a/modules/gallery/tests/Xss_Security_Test.php +++ b/modules/gallery/tests/Xss_Security_Test.php @@ -188,7 +188,7 @@ class Xss_Security_Test extends Unit_Test_Case { if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && in_array($tokens[$token_number + 2][1], - array("clean", "purify", "clean_js", "clean_attribute")) && + array("clean", "purify", "js_string", "clean_attribute")) && self::_token_matches("(", $tokens, $token_number + 3)) { // Not checking for mark_safe(). We want such calls to be marked dirty (thus reviewed). @@ -198,7 +198,7 @@ class Xss_Security_Test extends Unit_Test_Case { $token_number += 3; $token = $tokens[$token_number]; - if ("clean_js" == $method) { + if ("js_string" == $method) { $frame->is_safe_js(true); } else { $frame->is_safe_html(true); -- cgit v1.2.3 From df38a890a64dd33eafe3aed51ce8fde732cf8b8b Mon Sep 17 00:00:00 2001 From: Andy Staudacher Date: Sun, 30 Aug 2009 18:07:13 -0700 Subject: Tabs to spaces cleanup --- modules/gallery/controllers/admin_languages.php | 32 +- modules/gallery/controllers/l10n_client.php | 8 +- modules/gallery/helpers/gallery.php | 6 +- modules/gallery/libraries/SafeString.php | 4 +- modules/gallery/tests/Html_Helper_Test.php | 10 +- modules/gallery/tests/SafeString_Test.php | 14 +- modules/gallery/tests/Xss_Security_Test.php | 380 ++++++++++++------------ modules/server_add/helpers/server_add_event.php | 2 +- 8 files changed, 228 insertions(+), 228 deletions(-) (limited to 'modules/gallery/tests/SafeString_Test.php') diff --git a/modules/gallery/controllers/admin_languages.php b/modules/gallery/controllers/admin_languages.php index b1bc4cff..d85c47f9 100644 --- a/modules/gallery/controllers/admin_languages.php +++ b/modules/gallery/controllers/admin_languages.php @@ -21,10 +21,10 @@ class Admin_Languages_Controller extends Admin_Controller { public function index($share_translations_form=null) { $v = new Admin_View("admin.html"); $v->content = new View("admin_languages.html"); - $v->content->available_locales = locales::available(); + $v->content->available_locales = locales::available(); $v->content->installed_locales = locales::installed(); $v->content->default_locale = module::get_var("gallery", "default_locale"); - + if (empty($share_translations_form)) { $share_translations_form = $this->_share_translations_form(); } @@ -35,21 +35,21 @@ class Admin_Languages_Controller extends Admin_Controller { public function save() { access::verify_csrf(); - - locales::update_installed($this->input->post("installed_locales")); - - $installed_locales = array_keys(locales::installed()); + + locales::update_installed($this->input->post("installed_locales")); + + $installed_locales = array_keys(locales::installed()); $new_default_locale = $this->input->post("default_locale"); - if (!in_array($new_default_locale, $installed_locales)) { - if (!empty($installed_locales)) { - $new_default_locale = $installed_locales[0]; - } else { - $new_default_locale = "en_US"; - } - } - module::set_var("gallery", "default_locale", $new_default_locale); - - print json_encode(array("result" => "success")); + if (!in_array($new_default_locale, $installed_locales)) { + if (!empty($installed_locales)) { + $new_default_locale = $installed_locales[0]; + } else { + $new_default_locale = "en_US"; + } + } + module::set_var("gallery", "default_locale", $new_default_locale); + + print json_encode(array("result" => "success")); } public function share() { diff --git a/modules/gallery/controllers/l10n_client.php b/modules/gallery/controllers/l10n_client.php index 0775791e..16d39024 100644 --- a/modules/gallery/controllers/l10n_client.php +++ b/modules/gallery/controllers/l10n_client.php @@ -90,13 +90,13 @@ class L10n_Client_Controller extends Controller { } $session = Session::instance(); - $l10n_mode = $session->get("l10n_mode", false); + $l10n_mode = $session->get("l10n_mode", false); $session->set("l10n_mode", !$l10n_mode); $redirect_url = "admin/languages"; - if (!$l10n_mode) { - $redirect_url .= "#l10n-client"; - } + if (!$l10n_mode) { + $redirect_url .= "#l10n-client"; + } url::redirect($redirect_url); } diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 122227fc..035ed1da 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -92,7 +92,7 @@ class gallery_Core { $can_add = $item && access::can("add", $item); if ($can_add) { - $menu->append($add_menu = Menu::factory("submenu") + $menu->append($add_menu = Menu::factory("submenu") ->id("add_menu") ->label(t("Add"))); $add_menu->append(Menu::factory("dialog") @@ -100,11 +100,11 @@ class gallery_Core { ->label(t("Add photos")) ->url(url::site("simple_uploader/app/$item->id"))); if ($item->is_album()) { - $add_menu->append(Menu::factory("dialog") + $add_menu->append(Menu::factory("dialog") ->id("add_album_item") ->label(t("Add an album")) ->url(url::site("form/add/albums/$item->id?type=album"))); - } + } } $menu->append($options_menu = Menu::factory("submenu") diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php index 0767a665..cc542e01 100644 --- a/modules/gallery/libraries/SafeString.php +++ b/modules/gallery/libraries/SafeString.php @@ -120,8 +120,8 @@ class SafeString_Core { function for_html_attr() { $string = (string) $this->for_html(); return strtr($string, - array("'"=>"'", - '"'=>'"')); + array("'"=>"'", + '"'=>'"')); } /** diff --git a/modules/gallery/tests/Html_Helper_Test.php b/modules/gallery/tests/Html_Helper_Test.php index f5ce7fa4..3623705e 100644 --- a/modules/gallery/tests/Html_Helper_Test.php +++ b/modules/gallery/tests/Html_Helper_Test.php @@ -21,14 +21,14 @@ class Html_Helper_Test extends Unit_Test_Case { public function clean_test() { $safe_string = html::clean("hello

world

"); $this->assert_equal("hello <p >world</p>", - $safe_string); + $safe_string); $this->assert_true($safe_string instanceof SafeString); } public function purify_test() { $safe_string = html::purify("hello

world

"); $this->assert_equal("hello

world

", - $safe_string); + $safe_string); $this->assert_true($safe_string instanceof SafeString); } @@ -37,19 +37,19 @@ class Html_Helper_Test extends Unit_Test_Case { $this->assert_true($safe_string instanceof SafeString); $safe_string_2 = html::clean($safe_string); $this->assert_equal("hello

world

", - $safe_string_2); + $safe_string_2); } public function js_string_test() { $string = html::js_string("hello's

world

"); $this->assert_equal('"hello\'s

world<\\/p>"', - $string); + $string); } public function clean_attribute_test() { $safe_string = SafeString::of_safe_html("hello's

world

"); $safe_string = html::clean_attribute($safe_string); $this->assert_equal("hello's

world

", - $safe_string); + $safe_string); } } \ No newline at end of file diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php index ede55240..0895b7dd 100644 --- a/modules/gallery/tests/SafeString_Test.php +++ b/modules/gallery/tests/SafeString_Test.php @@ -21,19 +21,19 @@ class SafeString_Test extends Unit_Test_Case { public function toString_escapes_for_html_test() { $safe_string = new SafeString("hello

world

"); $this->assert_equal("hello <p>world</p>", - $safe_string); + $safe_string); } public function toString_for_safe_string_test() { $safe_string = SafeString::of_safe_html("hello

world

"); $this->assert_equal("hello

world

", - $safe_string); + $safe_string); } public function for_html_test() { $safe_string = new SafeString("hello

world

"); $this->assert_equal("hello <p>world</p>", - $safe_string->for_html()); + $safe_string->for_html()); } public function safestring_of_safestring_test() { @@ -50,27 +50,27 @@ class SafeString_Test extends Unit_Test_Case { $safe_string = new SafeString('"Foo\'s bar"'); $js_string = $safe_string->for_js(); $this->assert_equal('"\\"Foo<\\/em>\'s bar\\""', - $js_string); + $js_string); } public function for_html_attr_test() { $safe_string = new SafeString('"Foo\'s bar"'); $attr_string = $safe_string->for_html_attr(); $this->assert_equal('"<em>Foo</em>'s bar"', - $attr_string); + $attr_string); } public function for_html_attr_with_safe_html_test() { $safe_string = SafeString::of_safe_html('"Foo\'s bar"'); $attr_string = $safe_string->for_html_attr(); $this->assert_equal('"Foo's bar"', - $attr_string); + $attr_string); } public function string_safestring_equality_test() { $safe_string = new SafeString("hello

world

"); $this->assert_equal("hello

world

", - $safe_string->unescaped()); + $safe_string->unescaped()); $escaped_string = "hello <p>world</p>"; $this->assert_equal($escaped_string, $safe_string); diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php index 3a22afc1..6c141c52 100644 --- a/modules/gallery/tests/Xss_Security_Test.php +++ b/modules/gallery/tests/Xss_Security_Test.php @@ -24,9 +24,9 @@ class Xss_Security_Test extends Unit_Test_Case { // List of all tokens without whitespace, simplifying parsing. $tokens = array(); foreach (token_get_all(file_get_contents($view)) as $token) { - if (!is_array($token) || ($token[0] != T_WHITESPACE)) { - $tokens[] = $token; - } + if (!is_array($token) || ($token[0] != T_WHITESPACE)) { + $tokens[] = $token; + } } $frame = null; @@ -34,199 +34,199 @@ class Xss_Security_Test extends Unit_Test_Case { $in_script_block = false; for ($token_number = 0; $token_number < count($tokens); $token_number++) { - $token = $tokens[$token_number]; - - // Are we in a block? - if (is_array($token) && $token[0] == T_INLINE_HTML) { - $inline_html = $token[1]; - // T_INLINE_HTML blocks can be split. Need to handle the case - // where one token has "expr_append($inline_html); - } - - // Note: This approach won't catch }i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) { - $last_match = array_pop($matches[0]); - if (is_array($last_match)) { - $closing_script_pos = $last_match[1]; - } else { - $closing_script_pos = $last_match; - } - } - if (preg_match('{]*>}i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) { - $last_match = array_pop($matches[0]); - if (is_array($last_match)) { - $opening_script_pos = $last_match[1]; - } else { - $opening_script_pos = $last_match; - } - } - if ($opening_script_pos != $closing_script_pos) { - $in_script_block = $opening_script_pos > $closing_script_pos; - } - } - - // Look and report each instance of < ? = ... ? > - if (!is_array($token)) { - // A single char token, e.g: ; ( ) - if ($frame) { - $frame->expr_append($token); - } - } else if ($token[0] == T_OPEN_TAG_WITH_ECHO) { - // No need for a stack here - assume < ? = cannot be nested. - $frame = self::_create_frame($token, $in_script_block); + $token = $tokens[$token_number]; + + // Are we in a block? + if (is_array($token) && $token[0] == T_INLINE_HTML) { + $inline_html = $token[1]; + // T_INLINE_HTML blocks can be split. Need to handle the case + // where one token has "expr_append($inline_html); + } + + // Note: This approach won't catch }i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) { + $last_match = array_pop($matches[0]); + if (is_array($last_match)) { + $closing_script_pos = $last_match[1]; + } else { + $closing_script_pos = $last_match; + } + } + if (preg_match('{]*>}i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) { + $last_match = array_pop($matches[0]); + if (is_array($last_match)) { + $opening_script_pos = $last_match[1]; + } else { + $opening_script_pos = $last_match; + } + } + if ($opening_script_pos != $closing_script_pos) { + $in_script_block = $opening_script_pos > $closing_script_pos; + } + } + + // Look and report each instance of < ? = ... ? > + if (!is_array($token)) { + // A single char token, e.g: ; ( ) + if ($frame) { + $frame->expr_append($token); + } + } else if ($token[0] == T_OPEN_TAG_WITH_ECHO) { + // No need for a stack here - assume < ? = cannot be nested. + $frame = self::_create_frame($token, $in_script_block); } else if ($frame && $token[0] == T_CLOSE_TAG) { - // Store the < ? = ... ? > block that just ended here. - $found[$view][] = $frame; - $frame = null; + // Store the < ? = ... ? > block that just ended here. + $found[$view][] = $frame; + $frame = null; } else if ($frame && $token[0] == T_VARIABLE) { - $frame->expr_append($token[1]); + $frame->expr_append($token[1]); if ($token[1] == '$theme') { - if (self::_token_matches(array(T_OBJECT_OPERATOR, "->"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && - in_array($tokens[$token_number + 2][1], - array("thumb_proportion", "site_menu", "album_menu", "tag_menu", "photo_menu", + if (self::_token_matches(array(T_OBJECT_OPERATOR, "->"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], + array("thumb_proportion", "site_menu", "album_menu", "tag_menu", "photo_menu", "context_menu", "pager", "site_status", "messages", "album_blocks", "album_bottom", "album_top", "body_attributes", "credits", "dynamic_bottom", "dynamic_top", "footer", "head", "header_bottom", "header_top", "page_bottom", "page_top", "photo_blocks", "photo_bottom", "photo_top", "resize_bottom", "resize_top", "sidebar_blocks", "sidebar_bottom", "sidebar_top", "thumb_bottom", "thumb_info", "thumb_top")) && - self::_token_matches("(", $tokens, $token_number + 3)) { + self::_token_matches("(", $tokens, $token_number + 3)) { - $method = $tokens[$token_number + 2][1]; - $frame->expr_append("->$method("); + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("->$method("); - $token_number += 3; - $token = $tokens[$token_number]; + $token_number += 3; + $token = $tokens[$token_number]; $frame->is_safe_html(true); - } else if (self::_token_matches(array(T_OBJECT_OPERATOR, "->"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && - in_array($tokens[$token_number + 2][1], - array("css", "script", "url")) && - self::_token_matches("(", $tokens, $token_number + 3) && - // Only allow constant strings here - self::_token_matches(array(T_CONSTANT_ENCAPSED_STRING), $tokens, $token_number + 4)) { + } else if (self::_token_matches(array(T_OBJECT_OPERATOR, "->"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], + array("css", "script", "url")) && + self::_token_matches("(", $tokens, $token_number + 3) && + // Only allow constant strings here + self::_token_matches(array(T_CONSTANT_ENCAPSED_STRING), $tokens, $token_number + 4)) { - $method = $tokens[$token_number + 2][1]; - $frame->expr_append("->$method("); + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("->$method("); - $token_number += 4; - $token = $tokens[$token_number]; + $token_number += 4; + $token = $tokens[$token_number]; $frame->is_safe_html(true); - } + } } - } else if ($frame && $token[0] == T_STRING) { - $frame->expr_append($token[1]); - // t() and t2() are special in that they're guaranteed to return a SafeString(). - if (in_array($token[1], array("t", "t2"))) { - if (self::_token_matches("(", $tokens, $token_number + 1)) { - $frame->is_safe_html(true); - $frame->expr_append("("); - - $token_number++; - $token = $tokens[$token_number]; - } - } else if ($token[1] == "SafeString") { - // Looking for SafeString::of(... - if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && - in_array($tokens[$token_number + 2][1], array("of", "purify")) && - self::_token_matches("(", $tokens, $token_number + 3)) { + } else if ($frame && $token[0] == T_STRING) { + $frame->expr_append($token[1]); + // t() and t2() are special in that they're guaranteed to return a SafeString(). + if (in_array($token[1], array("t", "t2"))) { + if (self::_token_matches("(", $tokens, $token_number + 1)) { + $frame->is_safe_html(true); + $frame->expr_append("("); + + $token_number++; + $token = $tokens[$token_number]; + } + } else if ($token[1] == "SafeString") { + // Looking for SafeString::of(... + if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], array("of", "purify")) && + self::_token_matches("(", $tokens, $token_number + 3)) { // Not checking for of_safe_html(). We want such calls to be marked dirty (thus reviewed). - $frame->is_safe_html(true); - - $method = $tokens[$token_number + 2][1]; - $frame->expr_append("::$method("); - - $token_number += 3; - $token = $tokens[$token_number]; - } - } else if ($token[1] == "json_encode") { - if (self::_token_matches("(", $tokens, $token_number + 1)) { - $frame->is_safe_js(true); - $frame->expr_append("("); - - $token_number++; - $token = $tokens[$token_number]; - } - } else if ($token[1] == "url") { - // url methods return safe HTML - if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && - in_array($tokens[$token_number + 2][1], - array("site", "current", "base", "file", "abs_site", "abs_current", - "abs_file", "merge")) && - self::_token_matches("(", $tokens, $token_number + 3)) { - $frame->is_safe_html(true); - - $method = $tokens[$token_number + 2][1]; - $frame->expr_append("::$method("); - - $token_number += 3; - $token = $tokens[$token_number]; - } - } else if ($token[1] == "html") { - if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && - self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && - in_array($tokens[$token_number + 2][1], - array("clean", "purify", "js_string", "clean_attribute")) && - self::_token_matches("(", $tokens, $token_number + 3)) { + $frame->is_safe_html(true); + + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("::$method("); + + $token_number += 3; + $token = $tokens[$token_number]; + } + } else if ($token[1] == "json_encode") { + if (self::_token_matches("(", $tokens, $token_number + 1)) { + $frame->is_safe_js(true); + $frame->expr_append("("); + + $token_number++; + $token = $tokens[$token_number]; + } + } else if ($token[1] == "url") { + // url methods return safe HTML + if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], + array("site", "current", "base", "file", "abs_site", "abs_current", + "abs_file", "merge")) && + self::_token_matches("(", $tokens, $token_number + 3)) { + $frame->is_safe_html(true); + + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("::$method("); + + $token_number += 3; + $token = $tokens[$token_number]; + } + } else if ($token[1] == "html") { + if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], + array("clean", "purify", "js_string", "clean_attribute")) && + self::_token_matches("(", $tokens, $token_number + 3)) { // Not checking for mark_safe(). We want such calls to be marked dirty (thus reviewed). - $method = $tokens[$token_number + 2][1]; - $frame->expr_append("::$method("); + $method = $tokens[$token_number + 2][1]; + $frame->expr_append("::$method("); - $token_number += 3; - $token = $tokens[$token_number]; + $token_number += 3; + $token = $tokens[$token_number]; if ("js_string" == $method) { $frame->is_safe_js(true); } else { $frame->is_safe_html(true); } - } - } - } else if ($frame && $token[0] == T_OBJECT_OPERATOR) { - $frame->expr_append($token[1]); - - if (self::_token_matches(array(T_STRING), $tokens, $token_number + 1) && - in_array($tokens[$token_number + 1][1], - array("for_js", "for_html", "purified_html", "for_html_attr")) && - self::_token_matches("(", $tokens, $token_number + 2)) { - $method = $tokens[$token_number + 1][1]; - $frame->expr_append("$method("); - - $token_number += 2; - $token = $tokens[$token_number]; - - if ("for_js" == $method) { - $frame->is_safe_js(true); - } else { - $frame->is_safe_html(true); - } - } + } + } + } else if ($frame && $token[0] == T_OBJECT_OPERATOR) { + $frame->expr_append($token[1]); + + if (self::_token_matches(array(T_STRING), $tokens, $token_number + 1) && + in_array($tokens[$token_number + 1][1], + array("for_js", "for_html", "purified_html", "for_html_attr")) && + self::_token_matches("(", $tokens, $token_number + 2)) { + $method = $tokens[$token_number + 1][1]; + $frame->expr_append("$method("); + + $token_number += 2; + $token = $tokens[$token_number]; + + if ("for_js" == $method) { + $frame->is_safe_js(true); + } else { + $frame->is_safe_html(true); + } + } } else if ($frame) { - $frame->expr_append($token[1]); - } + $frame->expr_append($token[1]); + } } } @@ -252,26 +252,26 @@ class Xss_Security_Test extends Unit_Test_Case { ksort($found); foreach ($found as $view => $frames) { foreach ($frames as $frame) { - $state = "DIRTY"; - if ($frame->in_script_block()) { - $state = "DIRTY_JS"; - if ($frame->is_safe_js()) { - $state = "CLEAN"; - } - } else { - if ($frame->is_safe_html()) { - $state = "CLEAN"; - } - } - - if ("CLEAN" == $state) { - // Don't print CLEAN instances - No need to update the golden - // file when adding / moving clean instances. - continue; - } - - fprintf($fd, "%-60s %-3s %-8s %s\n", - $view, $frame->line(), $state, $frame->expr()); + $state = "DIRTY"; + if ($frame->in_script_block()) { + $state = "DIRTY_JS"; + if ($frame->is_safe_js()) { + $state = "CLEAN"; + } + } else { + if ($frame->is_safe_html()) { + $state = "CLEAN"; + } + } + + if ("CLEAN" == $state) { + // Don't print CLEAN instances - No need to update the golden + // file when adding / moving clean instances. + continue; + } + + fprintf($fd, "%-60s %-3s %-8s %s\n", + $view, $frame->line(), $state, $frame->expr()); } } fclose($fd); @@ -280,7 +280,7 @@ class Xss_Security_Test extends Unit_Test_Case { $canonical = MODPATH . "gallery/tests/xss_data.txt"; exec("diff $canonical $new", $output, $return_value); $this->assert_false( - $return_value, "XSS golden file mismatch. Output:\n" . implode("\n", $output) ); + $return_value, "XSS golden file mismatch. Output:\n" . implode("\n", $output) ); } private static function _create_frame($token, $in_script_block) { @@ -296,9 +296,9 @@ class Xss_Security_Test extends Unit_Test_Case { if (is_array($expected_token)) { for ($i = 0; $i < count($expected_token); $i++) { - if ($expected_token[$i] != $token[$i]) { - return false; - } + if ($expected_token[$i] != $token[$i]) { + return false; + } } return true; } else { diff --git a/modules/server_add/helpers/server_add_event.php b/modules/server_add/helpers/server_add_event.php index 6b21ec2e..b2d55153 100644 --- a/modules/server_add/helpers/server_add_event.php +++ b/modules/server_add/helpers/server_add_event.php @@ -35,7 +35,7 @@ class server_add_event_Core { // turn that into a dropdown if there are two different ways to add things. Do that in a // portable way for now. If we find ourselves duplicating this pattern, we should make an // API method for this. - $add_menu = $menu->get("add_menu"); + $add_menu = $menu->get("add_menu"); $add_menu->append(Menu::factory("dialog") ->id("server_add") ->label(t("Server add")) -- cgit v1.2.3