Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Validate JSONP Callback
<?php
/**
* Validate JSONP Callback
*
* https://github.com/tav/scripts/blob/master/validate_jsonp.py
* https://github.com/talis/jsonp-validator/blob/master/src/main/java/com/talis/jsonp/JsonpCallbackValidator.java
* http://tav.espians.com/sanitising-jsonp-callback-identifiers-for-security.html
* http://news.ycombinator.com/item?id=809291
*
* ^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$
*
*/
class Jsonp {
/**
* Validation tests
*
* @var array
*
* @access private
*/
private $_tests = array(
'' => false,
'hello' => true,
'alert()' => false,
'test()' => false,
'a-b' => false,
'23foo' => false,
'foo23' => true,
'$210' => true,
'_bar' => true,
'some_var' => true,
'$' => true,
'somevar' => true,
'function' => false,
' somevar' => false,
'$.ajaxHandler' => true,
'$.23' => false,
'array_of_functions[42]' => true,
'array_of_functions[42][1]' => true,
'$.ajaxHandler[42][1].foo' => true,
'array_of_functions[42]foo[1]' => false,
'array_of_functions[]' => false,
'array_of_functions["key"]' => true,
'myFunction[123].false' => false,
'myFunction .tester' => false,
'_function' => true,
'petersCallback1412331422[12]' => true,
':myFunction' => false
);
/**
* Is valid callback
*
* @param string $callback
*
* @return boolean
*/
function isValidCallback($callback)
{
$reserved = 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'
);
foreach(explode('.', $callback) as $identifier) {
if(!preg_match('/^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/', $identifier)) {
return false;
}
if(in_array($identifier, $reserved)) {
return false;
}
}
return true;
}
/**
* Test callback strings
*
* @param string $callback
*
* @return void
*
* @access private
*/
private function _test($callback, $valid)
{
$vocal = $valid ? 'valid' : 'invalid';
if($this->isValidCallback($callback) === $valid) {
echo '"'.$callback.'" <span style="color:green">passed as '.$vocal.'</span>.', "\n";
return true;
}
else {
echo '"'.$callback.'" <span style="color:red;font-weight:700;">failed as '.$vocal.'</span>.', "\n";
return false;
}
}
/**
* Run all tests
*
* @return void
*
* @access public
*/
function runTests()
{
echo '<strong>Testing ', count($this->_tests), ' callback methods:</strong>', "\n\n";
$passed = 0;
foreach($this->_tests as $callback => $valid) {
$passed = self::_test($callback, $valid) ? $passed+1 : $passed;
}
echo "\n", $passed, ' of ', count($this->_tests), ' tests passed.';
}
}
$jsonp = new Jsonp();
echo '<pre>';
$jsonp->runTests();
@ptz0n

This comment has been minimized.

Copy link
Owner Author

commented Sep 16, 2011

Testing 27 callback methods:

"" passed as invalid.
"hello" passed as valid.
"alert()" passed as invalid.
"test()" passed as invalid.
"a-b" passed as invalid.
"23foo" passed as invalid.
"foo23" passed as valid.
"$210" passed as valid.
"_bar" passed as valid.
"some_var" passed as valid.
"$" passed as valid.
"somevar" passed as valid.
"function" passed as invalid.
" somevar" passed as invalid.
"$.ajaxHandler" passed as valid.
"$.23" passed as invalid.
"array_of_functions[42]" passed as valid.
"array_of_functions[42][1]" passed as valid.
"$.ajaxHandler[42][1].foo" passed as valid.
"array_of_functions[42]foo[1]" passed as invalid.
"array_of_functions[]" passed as invalid.
"array_of_functions["key"]" passed as valid.
"myFunction[123].false" passed as invalid.
"myFunction .tester" passed as invalid.
"_function" passed as valid.
"petersCallback1412331422[12]" passed as valid.
":myFunction" passed as invalid.

27 of 27 tests passed.

Thanks to Tobias Sjösten for updated regex.

@Daniel15

This comment has been minimized.

Copy link

commented Jul 9, 2012

Thanks for that, here's a C# port of this code: https://gist.github.com/3074365

@ptrm

This comment has been minimized.

Copy link

commented May 17, 2013

Many thanks. And just in case, here's a one-liner for that:

^(?(?!(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)\b)(?<pat>[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|'.+'|\d+)\])*?))(\.(?P>pat))*$
@braco

This comment has been minimized.

Copy link

commented Aug 14, 2013

This regex doesn't seem to allow "foo.bar"?

This might be better:

/^([a-zA-Z_$][0-9a-zA-Z_$](?:.[0-9a-zA-Z$]+)?(?:[(?:"".+""|'.+'|\d+)])_?){1,}$/

test here:

http://burkeware.com/software/regex_playground.html

@Daniel15

This comment has been minimized.

Copy link

commented Aug 21, 2013

@braco It splits by . and then checks every segment individually, so will allow foo.bar.

@fyrye

This comment has been minimized.

Copy link

commented Jan 23, 2014

eval should probably also be added to the reserved list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.