From be33cfe5f19a3b7d51ddfb5ffc35e22f602a1f1e Mon Sep 17 00:00:00 2001 From: Georgy Kutsurua Date: Fri, 9 Mar 2012 03:05:28 +0400 Subject: [PATCH 1/2] #73 Add unit tests for JsonP & JsonXss --- main/UI/View/JsonPView.class.php | 70 +++++++++++++++++++ main/UI/View/JsonView.class.php | 2 +- main/UI/View/JsonXssView.class.php | 57 +++++++++++++++ test/main/JsonPViewTest.class.php | 105 ++++++++++++++++++++++++++++ test/main/JsonXssViewTest.class.php | 50 +++++++++++++ 5 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 main/UI/View/JsonPView.class.php create mode 100644 main/UI/View/JsonXssView.class.php create mode 100644 test/main/JsonPViewTest.class.php create mode 100644 test/main/JsonXssViewTest.class.php diff --git a/main/UI/View/JsonPView.class.php b/main/UI/View/JsonPView.class.php new file mode 100644 index 0000000000..ce8e66f454 --- /dev/null +++ b/main/UI/View/JsonPView.class.php @@ -0,0 +1,70 @@ +callback = $callback; + + return $this; + } + + /** + * @param Model $model + * @return string + */ + public function toString(/* Model */ $model = null) + { + $callback = null; + + if(is_scalar($this->callback)) + $callback = $this->callback; + elseif($this->callback instanceof Stringable) + $callback = $this->callback->toString(); + else + throw new WrongArgumentException('undefined type of callback, gived "'.gettype($this->callback).'"'); + + Assert::isNotEmpty($callback, 'callback can not be empty!'); + + if(!preg_match('/^[\$A-Z_][0-9A-Z_\$]*$/i', $callback)) + throw new WrongArgumentException('invalid function name, you should set valid javascript function name! gived "'.$callback.'"'); + + $json = parent::toString($model); + + return $callback.'('.$json.');'; + } + + } diff --git a/main/UI/View/JsonView.class.php b/main/UI/View/JsonView.class.php index 3df47ca817..83a8d6f788 100644 --- a/main/UI/View/JsonView.class.php +++ b/main/UI/View/JsonView.class.php @@ -12,7 +12,7 @@ /** * @ingroup Flow **/ - final class JsonView implements View, Stringable + class JsonView implements View, Stringable { protected $options = 0; diff --git a/main/UI/View/JsonXssView.class.php b/main/UI/View/JsonXssView.class.php new file mode 100644 index 0000000000..43eb3cafb3 --- /dev/null +++ b/main/UI/View/JsonXssView.class.php @@ -0,0 +1,57 @@ +setHexAmp(true); + $this->setHexApos(true); + $this->setHexQuot(true); + $this->setHexTag(true); + + $jsonp = parent::toString($model); + + $jsonp = str_ireplace( + array('u0022', 'u0027'), + array('\u0022', '\u0027'), + $jsonp + ); + + $result = ''."\n"; + + return $result; + } + + } diff --git a/test/main/JsonPViewTest.class.php b/test/main/JsonPViewTest.class.php new file mode 100644 index 0000000000..984a2308ab --- /dev/null +++ b/test/main/JsonPViewTest.class.php @@ -0,0 +1,105 @@ +',"'bar'",'"baz"','&blong&'); + + + public function testMain() + { + $this->execCallback('myCallback'); + + try{ + $this->execCallback(''); // empty js callback function name + + $this->fail('empty callback javascript function name expected!'); + } catch(WrongArgumentException $e) {} + + try{ + $this->execCallback('34_callback'); // invalid javascript function name + + $this->fail('invalid javascript function name expected!'); + } catch(WrongArgumentException $e) {} + + } + + protected function execCallback($callback) + { + Assert::isScalar($callback); + + $model = Model::create()->set('array', $this->array); + $data = array('array' => $this->array); + + //setup + $view = JsonPView::create()->setCallback($callback); + + //execution and check + $this->assertEquals( + $callback.'('.json_encode( + $data + ).');', + $view->toString($model) + ); + + //setup from stringable object + $view = JsonPView::create()->setCallback( + SimpleStringableObject::create()->setString($callback) + ); + + //execution and check + $this->assertEquals( + $callback.'('.json_encode( + $data + ).');', + $view->toString($model) + ); + + } + + } + + class SimpleStringableObject implements Stringable + { + protected $string = null; + + + /** + * @static + * @return SimpleStringableObject + */ + public static function create() + { + return new self(); + } + + /** + * @param $value + * @return SimpleStringableObject + */ + public function setString($value) + { + Assert::isString($value); + + $this->string = $value; + + return $this; + } + + /** + * @return str + */ + public function toString() + { + return $this->string; + } + } +?> \ No newline at end of file diff --git a/test/main/JsonXssViewTest.class.php b/test/main/JsonXssViewTest.class.php new file mode 100644 index 0000000000..11a9d2de7d --- /dev/null +++ b/test/main/JsonXssViewTest.class.php @@ -0,0 +1,50 @@ +',"'bar'",'"baz"','&blong&'); + + public function testMain() + { + $callback = 'myCallback'; + + $model = Model::create()->set('array', $this->array); + $data = array('array' => $this->array); + + //setup + $view = JsonXssView::create()->setCallback($callback); + + //execution and check + $this->assertEquals( + ''."\n", + $view->toString($model) + ); + + } + + } + +?> \ No newline at end of file From d0c2d121dec15b9e1015184ad63c884ddec7f3c5 Mon Sep 17 00:00:00 2001 From: Georgy Kutsurua Date: Fri, 16 Mar 2012 17:18:55 +0400 Subject: [PATCH 2/2] #73 Refactor & fix --- main/UI/View/JsonPView.class.php | 35 ++++++++++-------- main/UI/View/JsonXssView.class.php | 39 +++++++++++++++++--- test/main/JsonPViewTest.class.php | 56 +++++++++++++---------------- test/main/JsonXssViewTest.class.php | 41 +++++++++++++++------ 4 files changed, 110 insertions(+), 61 deletions(-) diff --git a/main/UI/View/JsonPView.class.php b/main/UI/View/JsonPView.class.php index ce8e66f454..c6144ccc04 100644 --- a/main/UI/View/JsonPView.class.php +++ b/main/UI/View/JsonPView.class.php @@ -15,6 +15,11 @@ class JsonPView extends JsonView { + /** + * Javascript valid function name pattern + */ + const CALLBACK_PATTERN = '/^[\$A-Z_][0-9A-Z_\$]*$/i'; + /** * @static * @return JsonPView @@ -37,7 +42,19 @@ public static function create() */ public function setCallback($callback) { - $this->callback = $callback; + $realCallbackName = null; + + if(is_scalar($callback)) + $realCallbackName = $callback; + elseif($callback instanceof Stringable) + $realCallbackName = $callback->toString(); + else + throw new WrongArgumentException('undefined type of callback, gived "'.gettype($callback).'"'); + + if(!preg_match(static::CALLBACK_PATTERN, $realCallbackName)) + throw new WrongArgumentException('invalid function name, you should set valid javascript function name! gived "'.$realCallbackName.'"'); + + $this->callback = $realCallbackName; return $this; } @@ -48,23 +65,11 @@ public function setCallback($callback) */ public function toString(/* Model */ $model = null) { - $callback = null; - - if(is_scalar($this->callback)) - $callback = $this->callback; - elseif($this->callback instanceof Stringable) - $callback = $this->callback->toString(); - else - throw new WrongArgumentException('undefined type of callback, gived "'.gettype($this->callback).'"'); - - Assert::isNotEmpty($callback, 'callback can not be empty!'); - - if(!preg_match('/^[\$A-Z_][0-9A-Z_\$]*$/i', $callback)) - throw new WrongArgumentException('invalid function name, you should set valid javascript function name! gived "'.$callback.'"'); + Assert::isNotEmpty($this->callback, 'callback can not be empty!'); $json = parent::toString($model); - return $callback.'('.$json.');'; + return $this->callback.'('.$json.');'; } } diff --git a/main/UI/View/JsonXssView.class.php b/main/UI/View/JsonXssView.class.php index 43eb3cafb3..f0f7e59304 100644 --- a/main/UI/View/JsonXssView.class.php +++ b/main/UI/View/JsonXssView.class.php @@ -15,6 +15,22 @@ class JsonXssView extends JsonPView { + /** + * Javascript valid function name pattern + */ + const CALLBACK_PATTERN = '/^[\$A-Z_][0-9A-Z_\$\.]*$/i'; + + /** + * Default prefix + * @var string + */ + protected $prefix = 'window.'; + + /** + * Default callback + * @var string + */ + protected $callback = 'name'; /** * @static @@ -25,6 +41,21 @@ public static function create() return new self(); } + /** + * @param $value + * @return JsonXssView + * @throws WrongArgumentException + */ + public function setPrefix($value) + { + if(!preg_match(static::CALLBACK_PATTERN, $value)) + throw new WrongArgumentException('invalid prefix name, you should set valid javascript function name! gived "'.$value.'"'); + + $this->prefix = $value; + + return $this; + } + /** * @param Model $model * @return string @@ -39,16 +70,16 @@ public function toString(/* Model */ $model = null) $this->setHexQuot(true); $this->setHexTag(true); - $jsonp = parent::toString($model); + $json = JsonView::toString($model); - $jsonp = str_ireplace( + $json = str_ireplace( array('u0022', 'u0027'), array('\u0022', '\u0027'), - $jsonp + $json ); $result = ''."\n"; return $result; diff --git a/test/main/JsonPViewTest.class.php b/test/main/JsonPViewTest.class.php index 984a2308ab..bee79ceb3d 100644 --- a/test/main/JsonPViewTest.class.php +++ b/test/main/JsonPViewTest.class.php @@ -16,53 +16,47 @@ final class JsonPViewTest extends TestCase public function testMain() { - $this->execCallback('myCallback'); + $model = Model::create()->set('array', $this->array); + $data = array('array' => $this->array); + $callback = 'myFunc'; + + //setup + $view = JsonPView::create(); try{ - $this->execCallback(''); // empty js callback function name + // empty js callback function name + $view->toString($model); $this->fail('empty callback javascript function name expected!'); } catch(WrongArgumentException $e) {} try{ - $this->execCallback('34_callback'); // invalid javascript function name + $view->setCallback('34_callback'); // invalid javascript function name $this->fail('invalid javascript function name expected!'); } catch(WrongArgumentException $e) {} - } + $view->setCallback($callback); - protected function execCallback($callback) - { - Assert::isScalar($callback); + $this->assertEquals($this->makeString($callback, $data), $view->toString($model) ); - $model = Model::create()->set('array', $this->array); - $data = array('array' => $this->array); + $simpleStringableObject = SimpleStringableObject::create()->setString($callback); - //setup - $view = JsonPView::create()->setCallback($callback); + $view->setCallback($simpleStringableObject); - //execution and check - $this->assertEquals( - $callback.'('.json_encode( - $data - ).');', - $view->toString($model) - ); - - //setup from stringable object - $view = JsonPView::create()->setCallback( - SimpleStringableObject::create()->setString($callback) - ); - - //execution and check - $this->assertEquals( - $callback.'('.json_encode( - $data - ).');', - $view->toString($model) - ); + $this->assertEquals($this->makeString($callback, $data), $view->toString($model) ); + } + /** + * @param $callback + * @param $data + * @return string + */ + protected function makeString($callback, $data) + { + return $callback.'('.json_encode( + $data + ).');'; } } diff --git a/test/main/JsonXssViewTest.class.php b/test/main/JsonXssViewTest.class.php index 11a9d2de7d..0f8e77804b 100644 --- a/test/main/JsonXssViewTest.class.php +++ b/test/main/JsonXssViewTest.class.php @@ -15,18 +15,40 @@ final class JsonXssViewTest extends TestCase public function testMain() { - $callback = 'myCallback'; + $prefix = 'window.'; + $callback = 'name'; $model = Model::create()->set('array', $this->array); $data = array('array' => $this->array); //setup - $view = JsonXssView::create()->setCallback($callback); + $view = JsonXssView::create(); - //execution and check - $this->assertEquals( - ''."\n", - $view->toString($model) - ); - + '\';'."\n". + ''."\n"; } }