From f744b3227b116d4f7ccd7c71f8b8498ebf86d85f Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Mon, 13 Apr 2015 22:35:27 +0200 Subject: Implemented autoescaping Still needs some cleanup and documentation. --- src/ste/STECore.php | 19 +++++++++++++++++++ src/ste/STEStandardLibrary.php | 18 ++++++++++++++++-- src/ste/Transcompiler.php | 10 +++++++--- tests/test_autoescape/.gitignore | 3 +++ tests/test_autoescape/code.php | 14 ++++++++++++++ tests/test_autoescape/test.tpl | 29 +++++++++++++++++++++++++++++ tests/test_autoescape/want | 25 +++++++++++++++++++++++++ 7 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 tests/test_autoescape/.gitignore create mode 100644 tests/test_autoescape/code.php create mode 100644 tests/test_autoescape/test.tpl create mode 100644 tests/test_autoescape/want diff --git a/src/ste/STECore.php b/src/ste/STECore.php index 35a92d5..4637aaa 100644 --- a/src/ste/STECore.php +++ b/src/ste/STECore.php @@ -10,9 +10,13 @@ namespace kch42\ste; * The Core of STE */ class STECore { + const ESCAPE_NONE = "none"; + const ESCAPE_HTML = "html"; + private $tags; private $storage_access; private $cur_tpl_dir; + public $escape_method = self::ESCAPE_NONE; public $scope; /* @@ -101,6 +105,21 @@ class STECore { } } + public function autoescape($content) { + if ($this->escape_method == self::ESCAPE_HTML) { + return htmlspecialchars($content); + } + return $content; + } + + public function eval_sub_with_escaping($sub, $method) { + $old_method = $this->escape_method; + $this->escape_method = $method; + $retval = $sub($this); + $this->escape_method = $old_method; + return $retval; + } + public function calc($expression) { return Calc::calc($expression); } diff --git a/src/ste/STEStandardLibrary.php b/src/ste/STEStandardLibrary.php index 915b699..68ba06a 100644 --- a/src/ste/STEStandardLibrary.php +++ b/src/ste/STEStandardLibrary.php @@ -12,13 +12,27 @@ class STEStandardLibrary { } static public function escape($ste, $params, $sub) { + $content = $ste->eval_sub_with_escaping($sub, STECore::ESCAPE_NONE); + if($ste->evalbool(@$params["lines"])) { - return nl2br(htmlspecialchars(str_replace("\r\n", "\n", $sub($ste)))); + return nl2br(htmlspecialchars(str_replace("\r\n", "\n", $content))); } else { - return htmlspecialchars($sub($ste)); + return htmlspecialchars($content); } } + static public function raw($ste, $params, $sub) { + return $ste->eval_sub_with_escaping($sub, STECore::ESCAPE_NONE); + } + + static public function autoescape($ste, $params, $sub) { + if(empty($params["mode"])) { + throw new RuntimeError("Missing mode parameter in ."); + } + + return $content = $ste->eval_sub_with_escaping($sub, $params['mode']); + } + static public function strlen($ste, $params, $sub) { return strlen($sub($ste)); } diff --git a/src/ste/Transcompiler.php b/src/ste/Transcompiler.php index 12fc59d..9961b15 100644 --- a/src/ste/Transcompiler.php +++ b/src/ste/Transcompiler.php @@ -401,7 +401,7 @@ class Transcompiler { return "try\n{\n" . self::indent_code($code) . "\n}\ncatch(\\kch42\\ste\\BreakException \$e) { break; }\ncatch(\\kch42\\ste\\ContinueException \$e) { continue; }\n"; } - private static function _transcompile($ast, $avoid_outputstack = false) { /* The real self::transcompile function, does not add boilerplate code. */ + private static function _transcompile($ast, $avoid_outputstack = false, $in_args = false) { /* The real self::transcompile function, does not add boilerplate code. */ $code = ""; $text_and_var_buffer = array(); @@ -410,7 +410,11 @@ class Transcompiler { if($node instanceof TextNode) { $text_and_var_buffer[] = '"' . Misc::escape_text($node->text) . '"'; } else if($node instanceof VariableNode) { - $text_and_var_buffer[] = $node->transcompile(); + if ($in_args) { + $text_and_var_buffer[] = $node->transcompile(); + } else { + $text_and_var_buffer[] = "\$ste->autoescape(".$node->transcompile().")"; + } } else if($node instanceof TagNode) { if(!empty($text_and_var_buffer)) { $code .= "\$outputstack[\$outputstack_i] .= " . implode (" . ", $text_and_var_buffer) . ";\n"; @@ -423,7 +427,7 @@ class Transcompiler { $code .= "\$$paramarray = array();\n"; foreach($node->params as $pname => $pcontent) { - list($pval, $pre) = self::_transcompile($pcontent, true); + list($pval, $pre) = self::_transcompile($pcontent, true, true); $code .= $pre . "\$${paramarray}['" . Misc::escape_text($pname) . "'] = " . $pval . ";\n"; } diff --git a/tests/test_autoescape/.gitignore b/tests/test_autoescape/.gitignore new file mode 100644 index 0000000..de2a41b --- /dev/null +++ b/tests/test_autoescape/.gitignore @@ -0,0 +1,3 @@ +have +*.ast +*.transc.php diff --git a/tests/test_autoescape/code.php b/tests/test_autoescape/code.php new file mode 100644 index 0000000..4f7c998 --- /dev/null +++ b/tests/test_autoescape/code.php @@ -0,0 +1,14 @@ +vars['test'] = 'foo"&'; + + $ste->register_tag('echoarg', function ($ste, $params, $sub) { + return $params['echo']; + }); + + // Autoescaping enabled by default + $ste->escape_method = STECore::ESCAPE_HTML; +} diff --git a/tests/test_autoescape/test.tpl b/tests/test_autoescape/test.tpl new file mode 100644 index 0000000..9452972 --- /dev/null +++ b/tests/test_autoescape/test.tpl @@ -0,0 +1,29 @@ +outer 1: $test +escaped outer 1: $test +double escaped outer 1: $test +raw outer 1: $test +arg outer 1: + +nested 1: $test +escaped nested 1: $test +double escaped nested 1: $test +raw nested 1: $test +arg nested 1: + +innermost: $test +escaped innermost: $test +double escaped innermost: $test +raw innermost: $test +arg innermost: + +nested 2: $test +escaped nested 2: $test +double escaped nested 2: $test +raw nested 2: $test +arg nested 2: + +outer 2: $test +escaped outer 2: $test +double escaped outer 2: $test +raw outer 2: $test +arg outer 2: diff --git a/tests/test_autoescape/want b/tests/test_autoescape/want new file mode 100644 index 0000000..0d06d44 --- /dev/null +++ b/tests/test_autoescape/want @@ -0,0 +1,25 @@ +outer 1: foo"&<bar> +escaped outer 1: foo"&<bar> +double escaped outer 1: foo&quot;&amp;&lt;bar&gt; +raw outer 1: foo"& +arg outer 1: foo"& +nested 1: foo"& +escaped nested 1: foo"&<bar> +double escaped nested 1: foo&quot;&amp;&lt;bar&gt; +raw nested 1: foo"& +arg nested 1: foo"& +innermost: foo"&<bar> +escaped innermost: foo"&<bar> +double escaped innermost: foo&quot;&amp;&lt;bar&gt; +raw innermost: foo"& +arg innermost: foo"& +nested 2: foo"& +escaped nested 2: foo"&<bar> +double escaped nested 2: foo&quot;&amp;&lt;bar&gt; +raw nested 2: foo"& +arg nested 2: foo"& +outer 2: foo"&<bar> +escaped outer 2: foo"&<bar> +double escaped outer 2: foo&quot;&amp;&lt;bar&gt; +raw outer 2: foo"& +arg outer 2: foo"& -- cgit v1.2.3-70-g09d2