Created
March 7, 2016 14:02
-
-
Save tryvin/a9f4622601d19a425267 to your computer and use it in GitHub Desktop.
A basic class to use when loading plugins in a controlled environment within PHP, this class uses runkit to redefine the functions, adding some random prefix, and controlling the access to these functions, like file parameters base dir and such
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class sandboxPluginLoader { | |
private $fileSystemFunctionsMap = array( | |
'realpath' => 0, | |
'chgrp' => 0, 'chmod' => 0, 'chown' => 0, | |
'unlink' => 0, 'copy' => array(0, 1), 'file' => 0, | |
'file_exists' => 0, 'file_get_contents' => 0, 'file_put_contents' => 0, | |
'flock' => 0, 'fopen' => 0, 'glob' => 0, | |
'link' => array(0, 1), 'readfile' => 0, 'readlink' => 0, | |
'rename' => array(0, 1), 'rmdir' => 0, | |
'symlink' => array(0, 1) | |
); | |
private $forbiddenFunctions = array( | |
'var_dump', 'print_r', 'get_declared_classes', 'phpinfo', | |
'exec', 'shell_exec', 'passthru', 'system', | |
'class_exists', 'get_declared_interfaces', 'get_object_vars' | |
); | |
private $functionPrefix = ''; | |
private $pluginDirectoryName = ''; | |
static $sandboxInstance = false; | |
static $isRunningSandbox = false; | |
static function instance($pluginDirectoryName = '') { | |
if ( sandboxPluginLoader::$sandboxInstance === false ) | |
sandboxPluginLoader::$sandboxInstance = new sandboxPluginLoader($pluginDirectoryName); | |
return sandboxPluginLoader::$sandboxInstance; | |
} | |
public function __construct($pluginDirectoryName = '') { | |
if ( ! function_exists('runkit_function_rename') ) | |
throw new Exception("No Runkit found"); | |
$this->functionPrefix = "_" . md5(mt_rand()); | |
$this->pluginDirectoryName = $pluginDirectoryName; | |
} | |
public function startSandbox() { | |
$this->redefineFileSystem(); | |
$this->redefineForbidden(); | |
sandboxPluginLoader::$isRunningSandbox = true; | |
} | |
public function isRunningSandbox() { | |
return sandboxPluginLoader::$isRunningSandbox; | |
} | |
private function redefineForbidden() { | |
foreach($this->forbiddenFunctions as $functionName) { | |
runkit_function_rename( | |
$functionName, | |
sprintf('%s_%s', | |
$this->functionPrefix, | |
$functionName | |
) | |
); | |
$functionCode = sprintf( | |
'$baseDir = "%1$s"; | |
$callStack = debug_backtrace(); | |
$filePath = %2$s_realpath(dirname($callStack[0]["file"])) . DIRECTORY_SEPARATOR; | |
if ( substr($filePath, 0, strlen($baseDir)) == $baseDir ) { | |
return false; | |
} | |
return call_user_func_array( | |
"%2$s_%3$s", | |
func_get_args() | |
); | |
', | |
$this->pluginDirectoryName, | |
$this->functionPrefix, | |
$functionName | |
); | |
runkit_function_add($functionName, '', $functionCode); | |
} | |
} | |
private function redefineFileSystem() { | |
foreach($this->fileSystemFunctionsMap as $functionName => $fileParam) { | |
runkit_function_rename( | |
$functionName, | |
sprintf('%s_%s', | |
$this->functionPrefix, | |
$functionName | |
) | |
); | |
$functionCode = sprintf( | |
'$baseDir = "%1$s"; | |
$fileArg = %2$s; | |
$functionArguments = func_get_args(); | |
$callStack = debug_backtrace(); | |
$filePath = %3$s_realpath(dirname($callStack[0]["file"])) . DIRECTORY_SEPARATOR; | |
if ( substr($filePath, 0, strlen($baseDir)) == $baseDir ) { | |
if ( is_array($fileArg) ) { | |
foreach($fileArg as $fIndex) { | |
$filePath = %3$s_realpath(dirname($functionArguments[$fIndex])) . DIRECTORY_SEPARATOR; | |
if ( substr($filePath, 0, strlen($baseDir)) != $baseDir ) | |
return false; | |
} | |
} | |
else if ( count($functionArguments) > $fileArg ) { | |
$filePath = %3$s_realpath(dirname($functionArguments[$fileArg])) . DIRECTORY_SEPARATOR; | |
if ( substr($filePath, 0, strlen($baseDir)) != $baseDir ) | |
return false; | |
} | |
} | |
set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) { | |
if ( 0 == error_reporting() ) | |
return false; | |
$errstr = str_replace("%3$s_%4$s", "%4$s", $errstr); | |
restore_error_handler(); | |
trigger_error($errstr, E_USER_WARNING); | |
}); | |
$returnedFunction = call_user_func_array( | |
"%3$s_%4$s", | |
func_get_args() | |
); | |
restore_error_handler(); | |
return $returnedFunction; | |
', | |
$this->pluginDirectoryName, | |
(is_array($fileParam) ? | |
sprintf("array(%s)", implode(",", $fileParam)) : $fileParam), | |
$this->functionPrefix, | |
$functionName | |
); | |
runkit_function_add($functionName, '', $functionCode); | |
} | |
} | |
} | |
/*EXAMPLE USAGE: instance it withing the plugin directory as a param, | |
and start the sandbox, after started, it goes till your script finish | |
*/ | |
sandboxPluginLoader::instance(realpath('plugins/testplugin') . DIRECTORY_SEPARATOR)->startSandbox(); | |
//NOW: includes the plugin | |
require_once('plugins/testplugin/controller.php'); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment