Skip to content

Commit

Permalink
Merge pull request #104 from FriendsOfSymfony/callback-validator
Browse files Browse the repository at this point in the history
Callback validator
  • Loading branch information
willdurand committed Oct 17, 2013
2 parents fd37b11 + 9f63903 commit ae6f14a
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 6 deletions.
4 changes: 3 additions & 1 deletion Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface;
use FOS\JsRoutingBundle\Response\RoutesResponse;
use FOS\JsRoutingBundle\Util\CacheControlConfig;
use FOS\JsRoutingBundle\Validator\CallbackValidator;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand Down Expand Up @@ -102,7 +103,8 @@ public function indexAction(Request $request, $_format)
$content = file_get_contents((string) $cache);

if (null !== $callback = $request->query->get('callback')) {
if (0 === preg_match('/^[a-zA-Z0-9\.$_]+$/', $callback)) {
$validator = new CallbackValidator();
if (!$validator->isValid($callback)) {
throw new HttpException(400, 'Invalid JSONP callback value');
}

Expand Down
5 changes: 1 addition & 4 deletions Tests/Controller/ControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,8 @@ public function testGenerateWithCallback($callback)
public static function dataProviderForTestGenerateWithCallback()
{
return array(
array('foo'),
array('foo123'),
array('fos.Router.data'),
array('$.callback'),
array('_.callback'),
array('foo'),
);
}

Expand Down
60 changes: 60 additions & 0 deletions Tests/Validator/CallbackValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace FOS\JsRoutingBundle\Tests\Validator;

use FOS\JsRoutingBundle\Validator\CallbackValidator;

class CallbackValidatorTest extends \PHPUnit_Framework_TestCase
{
const IS_VALID = true;

const IS_INVALID = false;

/**
* @dataProvider dataProviderForTestIsValid
*/
public function testIsValid($callback, $expected)
{
$validator = new CallbackValidator();
$this->assertEquals($expected, $validator->isValid($callback));
}

public static function dataProviderForTestIsValid()
{
return array(
array('foo', self::IS_VALID),
array('foo123', self::IS_VALID),
array('fos.Router.data', self::IS_VALID),
array('$.callback', self::IS_VALID),
array('_.callback', self::IS_VALID),
array('hello', self::IS_VALID),
array('foo23', self::IS_VALID),
array('$210', self::IS_VALID),
array('_bar', self::IS_VALID),
array('some_var', self::IS_VALID),
array('$', self::IS_VALID),
array('somevar', self::IS_VALID),
array('$.ajaxHandler', self::IS_VALID),
array('array_of_functions[42]', self::IS_VALID),
array('array_of_functions[42][1]', self::IS_VALID),
array('$.ajaxHandler[42][1].foo', self::IS_VALID),
array('array_of_functions["key"]', self::IS_VALID),
array('_function', self::IS_VALID),
array('petersCallback1412331422[12]', self::IS_VALID),
array('(function xss(x){evil()})', self::IS_INVALID),
array('', self::IS_INVALID),
array('alert()', self::IS_INVALID),
array('test()', self::IS_INVALID),
array('a-b', self::IS_INVALID),
array('23foo', self::IS_INVALID),
array('function', self::IS_INVALID),
array(' somevar', self::IS_INVALID),
array('$.23', self::IS_INVALID),
array('array_of_functions[42]foo[1]', self::IS_INVALID),
array('array_of_functions[]', self::IS_INVALID),
array('myFunction[123].false', self::IS_INVALID),
array('myFunction .tester', self::IS_INVALID),
array(':myFunction', self::IS_INVALID),
);
}
}
82 changes: 82 additions & 0 deletions Validator/CallbackValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\JsRoutingBundle\Validator;

/**
* Most of the code above took inspiration from:
* https://gist.github.com/ptz0n/1217080.
*/
class CallbackValidator
{
private $reservedKeywords = array(
'break',
'do',
'instanceof',
'typeof',
'case',
'else',
'new',
'var',
'catch',
'finally',
'return',
'void',
'continue',
'for',
'switch',
'while',
'debugger',
'function',
'this',
'with',
'default',
'if',
'throw',
'delete',
'in',
'try',
'class',
'enum',
'extends',
'super',
'const',
'export',
'import',
'implements',
'let',
'private',
'public',
'yield',
'interface',
'package',
'protected',
'static',
'null',
'true',
'false',
);

public function isValid($callback)
{
foreach (explode('.', $callback) as $identifier) {
if (!preg_match('/^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/', $identifier)) {
return false;
}

if (in_array($identifier, $this->reservedKeywords)) {
return false;
}
}

return true;
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"target-dir": "FOS/JsRoutingBundle",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.4-dev"
}
}
}

0 comments on commit ae6f14a

Please sign in to comment.