Skip to content

Commit ae6f14a

Browse files
committed
Merge pull request #104 from FriendsOfSymfony/callback-validator
Callback validator
2 parents fd37b11 + 9f63903 commit ae6f14a

File tree

5 files changed

+147
-6
lines changed

5 files changed

+147
-6
lines changed

Controller/Controller.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface;
1515
use FOS\JsRoutingBundle\Response\RoutesResponse;
1616
use FOS\JsRoutingBundle\Util\CacheControlConfig;
17+
use FOS\JsRoutingBundle\Validator\CallbackValidator;
1718
use Symfony\Component\Config\ConfigCache;
1819
use Symfony\Component\HttpFoundation\Request;
1920
use Symfony\Component\HttpFoundation\Response;
@@ -102,7 +103,8 @@ public function indexAction(Request $request, $_format)
102103
$content = file_get_contents((string) $cache);
103104

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

Tests/Controller/ControllerTest.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,8 @@ public function testGenerateWithCallback($callback)
5656
public static function dataProviderForTestGenerateWithCallback()
5757
{
5858
return array(
59-
array('foo'),
60-
array('foo123'),
6159
array('fos.Router.data'),
62-
array('$.callback'),
63-
array('_.callback'),
60+
array('foo'),
6461
);
6562
}
6663

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace FOS\JsRoutingBundle\Tests\Validator;
4+
5+
use FOS\JsRoutingBundle\Validator\CallbackValidator;
6+
7+
class CallbackValidatorTest extends \PHPUnit_Framework_TestCase
8+
{
9+
const IS_VALID = true;
10+
11+
const IS_INVALID = false;
12+
13+
/**
14+
* @dataProvider dataProviderForTestIsValid
15+
*/
16+
public function testIsValid($callback, $expected)
17+
{
18+
$validator = new CallbackValidator();
19+
$this->assertEquals($expected, $validator->isValid($callback));
20+
}
21+
22+
public static function dataProviderForTestIsValid()
23+
{
24+
return array(
25+
array('foo', self::IS_VALID),
26+
array('foo123', self::IS_VALID),
27+
array('fos.Router.data', self::IS_VALID),
28+
array('$.callback', self::IS_VALID),
29+
array('_.callback', self::IS_VALID),
30+
array('hello', self::IS_VALID),
31+
array('foo23', self::IS_VALID),
32+
array('$210', self::IS_VALID),
33+
array('_bar', self::IS_VALID),
34+
array('some_var', self::IS_VALID),
35+
array('$', self::IS_VALID),
36+
array('somevar', self::IS_VALID),
37+
array('$.ajaxHandler', self::IS_VALID),
38+
array('array_of_functions[42]', self::IS_VALID),
39+
array('array_of_functions[42][1]', self::IS_VALID),
40+
array('$.ajaxHandler[42][1].foo', self::IS_VALID),
41+
array('array_of_functions["key"]', self::IS_VALID),
42+
array('_function', self::IS_VALID),
43+
array('petersCallback1412331422[12]', self::IS_VALID),
44+
array('(function xss(x){evil()})', self::IS_INVALID),
45+
array('', self::IS_INVALID),
46+
array('alert()', self::IS_INVALID),
47+
array('test()', self::IS_INVALID),
48+
array('a-b', self::IS_INVALID),
49+
array('23foo', self::IS_INVALID),
50+
array('function', self::IS_INVALID),
51+
array(' somevar', self::IS_INVALID),
52+
array('$.23', self::IS_INVALID),
53+
array('array_of_functions[42]foo[1]', self::IS_INVALID),
54+
array('array_of_functions[]', self::IS_INVALID),
55+
array('myFunction[123].false', self::IS_INVALID),
56+
array('myFunction .tester', self::IS_INVALID),
57+
array(':myFunction', self::IS_INVALID),
58+
);
59+
}
60+
}

Validator/CallbackValidator.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSJsRoutingBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\JsRoutingBundle\Validator;
13+
14+
/**
15+
* Most of the code above took inspiration from:
16+
* https://gist.github.com/ptz0n/1217080.
17+
*/
18+
class CallbackValidator
19+
{
20+
private $reservedKeywords = array(
21+
'break',
22+
'do',
23+
'instanceof',
24+
'typeof',
25+
'case',
26+
'else',
27+
'new',
28+
'var',
29+
'catch',
30+
'finally',
31+
'return',
32+
'void',
33+
'continue',
34+
'for',
35+
'switch',
36+
'while',
37+
'debugger',
38+
'function',
39+
'this',
40+
'with',
41+
'default',
42+
'if',
43+
'throw',
44+
'delete',
45+
'in',
46+
'try',
47+
'class',
48+
'enum',
49+
'extends',
50+
'super',
51+
'const',
52+
'export',
53+
'import',
54+
'implements',
55+
'let',
56+
'private',
57+
'public',
58+
'yield',
59+
'interface',
60+
'package',
61+
'protected',
62+
'static',
63+
'null',
64+
'true',
65+
'false',
66+
);
67+
68+
public function isValid($callback)
69+
{
70+
foreach (explode('.', $callback) as $identifier) {
71+
if (!preg_match('/^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/', $identifier)) {
72+
return false;
73+
}
74+
75+
if (in_array($identifier, $this->reservedKeywords)) {
76+
return false;
77+
}
78+
}
79+
80+
return true;
81+
}
82+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"target-dir": "FOS/JsRoutingBundle",
2929
"extra": {
3030
"branch-alias": {
31-
"dev-master": "1.1-dev"
31+
"dev-master": "1.4-dev"
3232
}
3333
}
3434
}

0 commit comments

Comments
 (0)