Updated all dependencies and prepared for PHP 8

This commit is contained in:
2026-06-19 09:37:22 +00:00
parent 941c9104dc
commit 5f9f6ca2ff
21 changed files with 3334 additions and 2643 deletions
+4 -4
View File
@@ -12,7 +12,7 @@ use DI\Container, DI\FactoryInterface, Invoker\InvokerInterface;
use DI\Definition\Source\DefinitionArray, DI\Definition\Source\SourceChain;
use Psr\Http\Message\RequestInterface;
use CloudObjects\SDK\ObjectRetriever, CloudObjects\SDK\COIDParser,
CloudObjects\SDK\NodeReader;
CloudObjects\SDK\NodeReader, CloudObjects\SDK\Constants;
use CloudObjects\PhpMAE\Exceptions\PhpMAEException;
class ClassRepository {
@@ -147,8 +147,8 @@ class ClassRepository {
$objectRetriever = $this->container->get(ObjectRetriever::class);
// Get revision
$revision = $object->getProperty(ObjectRetriever::REVISION_PROPERTY)
? $object->getProperty(ObjectRetriever::REVISION_PROPERTY)->getValue()
$revision = $object->getProperty(Constants::PROPERTY_REVISION)
? $object->getProperty(Constants::PROPERTY_REVISION)->getValue()
: 'LocalConfig';
// Build filename where cached version should exist
@@ -265,7 +265,7 @@ class ClassRepository {
else
$sourceCode = $objectRetriever->getAttachment($uri, $sourceUrl->getValue());
}
// Run source code through validator to ensure sanity
$validator = new ClassValidator;
$validator->validateInterface($sourceCode, $uri);
+2 -3
View File
@@ -8,7 +8,7 @@ namespace CloudObjects\PhpMAE;
use CloudObjects\PhpMAE\Sandbox\CustomizedSandbox;
use PHPSandbox\SandboxWhitelistVisitor, PHPSandbox\ValidatorVisitor;
use PhpParser\ParserFactory, PhpParser\NodeTraverser;
use PhpParser\ParserFactory, PhpParser\PhpVersion, PhpParser\NodeTraverser;
use ML\IRI\IRI;
use CloudObjects\SDK\COIDParser;
use CloudObjects\PhpMAE\Exceptions\PhpMAEException;
@@ -43,7 +43,6 @@ class ClassValidator {
'GuzzleHttp\Client',
'GuzzleHttp\HandlerStack', 'GuzzleHttp\Middleware',
'GuzzleHttp\Handler\CurlHandler',
'GuzzleHttp\Subscriber\Oauth\Oauth1',
'GuzzleHttp\Promise',
'Dflydev\FigCookies\SetCookie',
'Symfony\Component\Mime\Email',
@@ -105,7 +104,7 @@ class ClassValidator {
$stack = ClassRepository::DEFAULT_STACK) {
// Initialize parser and parse source code
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$parser = (new ParserFactory)->createForVersion(PhpVersion::getHostVersion());
$ast = $parser->parse($sourceCode);
// Parse and dump use statements
+1 -1
View File
@@ -14,7 +14,7 @@ use Psr\Http\Message\RequestInterface, Psr\Http\Message\ResponseInterface;
use DI\ContainerBuilder;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Cache\FilesystemCache;
use Slim\Http\Response;
use Slim\Psr7\Response;
use Symfony\Component\Mailer\MailerInterface,
Symfony\Component\Mailer\Transport, Symfony\Component\Mailer\Mailer;
use CloudObjects\SDK\ObjectRetriever, CloudObjects\SDK\NodeReader, CloudObjects\SDK\COIDParser;
+1 -1
View File
@@ -20,7 +20,7 @@ class SandboxedContainer implements ContainerInterface {
$this->container = $container;
}
public function has($id) {
public function has($id) : bool {
return $this->container->has($id);
}
@@ -16,7 +16,7 @@ use DI\Definition\Source\DefinitionSource, DI\Definition\Source\Autowiring,
*/
class WhitelistReflectionBasedAutowiring extends ReflectionBasedAutowiring implements DefinitionSource, Autowiring {
public function autowire(string $name, ObjectDefinition $definition = null) {
public function autowire(string $name, ?ObjectDefinition $definition = null) : ?ObjectDefinition {
return isset($definition) ? parent::autowire($name, $definition) : null;
}
+47 -43
View File
@@ -10,8 +10,9 @@ use Psr\Http\Message\RequestInterface, Psr\Http\Message\ResponseInterface,
Psr\Http\Message\ServerRequestInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\App;
use Slim\Http\Headers, Slim\Http\Request, Slim\Http\Response, Slim\Http\Environment;
use Slim\Psr7\Response;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
use ML\IRI\IRI;
use ML\JsonLD\Node;
use Tuupola\Middleware\HttpBasicAuthentication,
@@ -27,7 +28,7 @@ use CloudObjects\PhpMAE\Exceptions\PhpMAEException;
class Engine implements RequestHandlerInterface {
const SKEY = '__self';
const PREPARE_TIME_LIMIT = 2;
const CLASS_TIME_LIMIT = 5;
@@ -35,7 +36,6 @@ class Engine implements RequestHandlerInterface {
private $objectRetriever;
private $classRepository;
private $slim;
private $container;
private $reader;
@@ -43,12 +43,11 @@ class Engine implements RequestHandlerInterface {
private $runClass;
public function __construct(ObjectRetriever $objectRetriever,
ClassRepository $classRepository, App $slim,
ClassRepository $classRepository,
ErrorHandler $errorHandler, ContainerInterface $container) {
$this->objectRetriever = $objectRetriever;
$this->classRepository = $classRepository;
$this->slim = $slim;
$this->container = $container;
$this->reader = new NodeReader([
'prefixes' => [
@@ -62,7 +61,7 @@ class Engine implements RequestHandlerInterface {
}, $errorHandler); // see: http://stackoverflow.com/questions/4410632/handle-fatal-errors-in-php-using-register-shutdown-function
}
private function isObjectPublic() {
private function isObjectPublic() {
return ($this->reader->hasProperty($this->object, 'co:isVisibleTo')
&& $this->reader->getFirstValueIRI($this->object, 'co:isVisibleTo')
->equals(self::CO_PUBLIC)
@@ -85,13 +84,13 @@ class Engine implements RequestHandlerInterface {
return new CorsMiddleware($defaultConfig); // every origin is allowed, by class
$origins = $this->reader->getAllValuesString($this->object, 'phpmae:allowsCORSOrigin');
if ($this->container->has('global_cors_origins'))
$origins = array_merge($origins, explode('|', $this->container->get('global_cors_origins')));
if (count($origins) == 0 || trim($origins[0]) == '')
return null; // no CORS enabled
return new CorsMiddleware(array_merge($defaultConfig, [
'origin' => $origins
])); // configured CORS middleware
@@ -120,14 +119,14 @@ class Engine implements RequestHandlerInterface {
== SharedSecretAuthentication::RESULT_OK);
if ($authResult != true)
continue;
if (substr($as, 14) == 'runclass')
$authenticated = (substr($object->getId(), 7, strlen($args['user']) +1) == $args['user'].'/');
else
$authenticated = (substr($as, 14) == 'coid://'.$args['user']);
} elseif (substr($as, 0, 4) != 'none')
throw new PhpMAEException("Unsupported authentication scheme!");
if ($authenticated == true)
break;
}
@@ -149,14 +148,14 @@ class Engine implements RequestHandlerInterface {
if ($this->reader->getFirstValueBool($this->object, 'phpmae:allowsGETRequests'))
$input = $request->getQueryParams();
else
return (new Response(405));
return new Response(405);
} elseif ($request->getMethod() == 'POST') {
// POST is always allowed
$input = $request->getParsedBody();
if (!is_array($input))
$input = [];
} else
return (new Response(405));
return new Response(405);
set_time_limit($this->container->has('execution_time_limit')
? $this->container->get('execution_time_limit') : self::CLASS_TIME_LIMIT);
@@ -171,25 +170,29 @@ class Engine implements RequestHandlerInterface {
*/
public function generateResponse($content) {
$lowercaseContent = is_string($content) ? strtolower($content) : '';
if (!isset($content))
if (!isset($content)) {
// Empty response
$response = new Response(204);
elseif (is_string($content) && (substr($lowercaseContent, 0, 5) == '<html' || substr($lowercaseContent, 0, 14) == '<!doctype html'))
} elseif (is_string($content) && (substr($lowercaseContent, 0, 5) == '<html' || substr($lowercaseContent, 0, 14) == '<!doctype html')) {
// HTML response
$response = (new Response)->write($content);
elseif (is_string($content) && (substr($lowercaseContent, 0, 7) == 'http://' || substr($lowercaseContent, 0, 8) == 'https://'))
$response = new Response(200);
$response->getBody()->write($content);
} elseif (is_string($content) && (substr($lowercaseContent, 0, 7) == 'http://' || substr($lowercaseContent, 0, 8) == 'https://')) {
// Redirect response
$response = (new Response)->withRedirect($content);
elseif (is_string($content) || is_numeric($content))
$response = (new Response(302))->withHeader('Location', $content);
} elseif (is_string($content) || is_numeric($content)) {
// Plain text response
$response = (new Response)->withHeader('Content-Type', 'text/plain')->write($content);
elseif (is_object($content) && in_array(ResponseInterface::class, class_implements($content)))
$response = (new Response(200))->withHeader('Content-Type', 'text/plain');
$response->getBody()->write((string)$content);
} elseif (is_object($content) && in_array(ResponseInterface::class, class_implements($content))) {
// Existing response to pass through
$response = $content;
else
} else {
// JSON response (default)
$response = (new Response)->withJson($content);
$response = (new Response(200))->withHeader('Content-Type', 'application/json');
$response->getBody()->write(json_encode($content));
}
// TODO: add support for XML
// Add cookies if any
@@ -199,7 +202,7 @@ class Engine implements RequestHandlerInterface {
$response = FigResponseCookies::set($response, $cookie);
}
}
return $response;
}
@@ -219,7 +222,8 @@ class Engine implements RequestHandlerInterface {
* Start execution of a request.
*/
public function execute(RequestInterface $request) {
$path = rtrim($request->getUri()->getBasePath().$request->getUri()->getPath(), '/');
$path = $request->getUri()->getPath();
switch ($path) {
case "":
case "/":
@@ -227,7 +231,7 @@ class Engine implements RequestHandlerInterface {
$file = ($this->container
->get(InteractiveRunController::class)
->isEnabled()) ? 'app.html' : 'app_disabled.html';
return $this->generateResponse(file_get_contents(__DIR__.'/../static/'.$file));
case "/run":
// Run interactive code request
@@ -243,7 +247,8 @@ class Engine implements RequestHandlerInterface {
if (file_exists(__DIR__.'/../static'.$path)) {
// Proxy for static files
$filename = realpath(__DIR__.'/../static'.$path);
$response = (new Response)->write(file_get_contents($filename));
$response = new Response(200);
$response->getBody()->write(file_get_contents($filename));
switch (pathinfo($filename, PATHINFO_EXTENSION)) {
case "css":
return $response->withHeader('Content-Type', 'text/css');
@@ -252,12 +257,12 @@ class Engine implements RequestHandlerInterface {
default:
return $response;
}
}
$coid = COIDParser::fromString(substr($path, 1));
$this->loadRunClass($coid, $request);
// Process a standard request for a phpMAE class
$queue = [
$this->getCORSMiddleware(),
@@ -266,7 +271,7 @@ class Engine implements RequestHandlerInterface {
];
$relay = new Relay(
array_filter($queue, function ($q) {
return $q !== null;
return $q !== null;
})
);
return $relay->handle($request);
@@ -278,7 +283,7 @@ class Engine implements RequestHandlerInterface {
*/
public function loadRunClass(IRI $coid, RequestInterface $request = null) {
if (COIDParser::isValidCOID($coid) && COIDParser::getType($coid) != COIDParser::COID_ROOT) {
$this->object = $this->objectRetriever->get($coid);
$this->object = $this->objectRetriever->getObjectNode($coid);
if (!isset($this->object))
throw new PhpMAEException("The object <" . (string)$coid . "> does not exist or this phpMAE instance is not allowed to access it.");
$this->runClass = $this->classRepository->createInstance($this->object, $request);
@@ -315,18 +320,17 @@ class Engine implements RequestHandlerInterface {
public function run() {
set_time_limit(self::PREPARE_TIME_LIMIT);
$env = new Environment($_SERVER);
$request = Request::createFromEnvironment($env);
$request = ServerRequestCreatorFactory::create()->createServerRequestFromGlobals();
try {
$response = $this->execute($request);
} catch (PhpMAEException $e) {
// Create plain-text error response
$response = (new Response(500))
->withHeader('Content-Type', 'text/plain')
->write($e->getMessage());
$response = (new Response(500))->withHeader('Content-Type', 'text/plain');
$response->getBody()->write($e->getMessage());
}
$this->slim->respond($response);
(new ResponseEmitter())->emit($response);
}
}
}
+9 -11
View File
@@ -6,27 +6,25 @@
namespace CloudObjects\PhpMAE;
use Slim\App;
use Slim\Http\Response;
use Slim\Psr7\Response;
use Slim\ResponseEmitter;
use ML\JsonLD\Node;
use CloudObjects\SDK\ObjectRetriever;
class ErrorHandler {
private $classMap = [];
private $slim;
private $responseGenerator;
public function __construct(App $slim) {
$this->slim = $slim;
ini_set("display_errors", 0);
public function __construct() {
// ini_set("display_errors", 0);
$this->setResponseGenerator(function($error, $object) {
$message = substr($error['message'], 0, strpos($error['message'], ' in /'));
return (new Response(500))
->withHeader('Content-Type', 'text/plain')
->write("Error in implementation of <".$object->getId()."> at revision "
$response = (new Response(500))->withHeader('Content-Type', 'text/plain');
$response->getBody()->write("Error in implementation of <".$object->getId()."> at revision "
. $object->getProperty(ObjectRetriever::REVISION_PROPERTY)->getValue()
. ":\n".$message);
return $response;
});
}
@@ -42,9 +40,9 @@ class ErrorHandler {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [ E_ERROR, E_COMPILE_ERROR ])
&& isset($this->classMap[$error['file']])) {
$response = call_user_func($this->responseGenerator, $error, $this->classMap[$error['file']]);
$this->slim->respond($response);
(new ResponseEmitter())->emit($response);
}
}
+5 -4
View File
@@ -7,7 +7,7 @@
namespace CloudObjects\PhpMAE;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Response;
use Slim\Psr7\Response;
/**
* The JsonRPCTransport writes JsonRPC output into a Slim response.
@@ -19,10 +19,11 @@ class JsonRPCTransport {
public function reply($data) {
if (is_a($data, ResponseInterface::class))
$this->response = $data->withHeader('C-PhpMae-Passthru', '1');
else
else {
$this->response = (new Response(200))
->withHeader('Content-Type', 'application/json')
->write($data);
->withHeader('Content-Type', 'application/json');
$this->response->getBody()->write($data);
}
}
public function receive() {
+47 -41
View File
@@ -10,9 +10,11 @@ use InvalidArgumentException;
use Psr\Http\Message\RequestInterface, Psr\Http\Message\ResponseInterface;
use Psr\Container\ContainerInterface;
use Slim\App;
use Tuupola\Middleware\CorsMiddleware;;
use Slim\Http\Environment, Slim\Http\Uri, Slim\Http\Response,
Slim\Http\Request, Slim\Http\Headers;
use Slim\Factory\AppFactory;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
use Slim\Psr7\Response;
use Tuupola\Middleware\CorsMiddleware;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Psr7\ServerRequest;
@@ -34,17 +36,16 @@ class Router {
public function __construct(Engine $engine, ObjectRetriever $objectRetriever,
ContainerInterface $container) {
$this->engine = $engine;
$this->objectRetriever = $objectRetriever;
$this->container = $container;
}
private function getRequestHeaders(Request $request) {
$headers = new Headers; // this instance is required to access the "normalizeKey" method
private function getRequestHeaders(RequestInterface $request) {
$output = [];
foreach (array_keys($request->getHeaders()) as $key) {
$key = $headers->normalizeKey($key);
$key = strtolower($key);
if (substr($key, 0, 9) == 'c-phpmae-' || $key == 'host') {
// do not forward "Host" header or custom phpMAE headers
continue;
@@ -83,14 +84,14 @@ class Router {
$router = $this;
$engine = $this->engine;
$retriever = $this->getObjectRetriever(new IRI($object->getId()));
foreach ($routes as $r) {
if (!$reader->hasProperty($r, 'wa:hasVerb') || !$reader->hasProperty($r, 'wa:hasPath'))
throw new PhpMAEException("Incomplete route configuration! Routes must have wa:hasVerb and wa:hasPath.");
$app->map([ $reader->getFirstValueString($r, 'wa:hasVerb') ],
$reader->getFirstValueString($r, 'wa:hasPath'),
function(RequestInterface $request, ResponseInterface $response, $args) use ($r, $reader, $router, $engine, $retriever, $object) {
function(RequestInterface $request, ResponseInterface $response, $args) use ($r, $reader, $router, $engine, $retriever, $object) {
if ($reader->hasProperty($r, 'phpmae:runsClass')) {
try {
$params = [];
@@ -112,10 +113,13 @@ class Router {
$params = $request->getQueryParams();
}
} catch (InvalidArgumentException $e) {
return (new Response(400))->withJson([
$errorResponse = (new Response(400))
->withHeader('Content-Type', 'application/json');
$errorResponse->getBody()->write(json_encode([
'error' => 'InputValidationFailed',
'message' => $e->getMessage()
]);
]));
return $errorResponse;
}
// Add static parameters from Router
@@ -158,7 +162,10 @@ class Router {
if (isset($rpcResponse['result'])) {
return $engine->generateResponse($rpcResponse['result']);
} else {
return (new Response(500))->withJson($rpcResponse);
$errResponse = (new Response(500))
->withHeader('Content-Type', 'application/json');
$errResponse->getBody()->write(json_encode($rpcResponse));
return $errResponse;
}
} else {
// Generic class execution (invokable)
@@ -167,15 +174,16 @@ class Router {
} elseif ($reader->hasProperty($r, 'phpmae:redirectsToURL')) {
// Route to redirect to a specific external URL
return (new Response)->withRedirect($reader->getFirstValueString($r, 'phpmae:redirectsToURL'));
return (new Response(302))
->withHeader('Location', $reader->getFirstValueString($r, 'phpmae:redirectsToURL'));
} elseif ($reader->hasProperty($r, 'phpmae:redirectsToBaseURL')) {
// Route to redirect to an external base URL
$baseUrl = $reader->getFirstValueIRI($r, 'phpmae:redirectsToBaseURL');
$uri = $request->getUri();
$targetUrl = (string)$baseUrl->resolve(substr($uri->getPath(), 1) . ($uri->getQuery() != '' ? '?'.$uri->getQuery() : ''));
return (new Response)->withRedirect($targetUrl);
return (new Response(302))->withHeader('Location', $targetUrl);
} elseif ($reader->hasProperty($r, 'phpmae:proxiesRequestsToBaseURL')) {
// Route to proxy requests to an external URL
@@ -197,25 +205,31 @@ class Router {
// Rule to serve an attached file
$etag = '"'.md5($reader->getFirstValueString($object, 'co:isAtRevision')
.'+'.$reader->getFirstValueString($r, 'phpmae:servesStaticFileAttachment')).'"';
if ($request->hasHeader('If-None-Match') && strpos($request->getHeaderLine('If-None-Match'), $etag) > -1) {
// Can use cached version
return (new Response(304))->withHeader('ETag', $etag)
return (new Response(304))
->withHeader('ETag', $etag)
->withHeader('Cache-Control', 'max-age='.self::STATIC_CACHE_TTL.', public');
}
$attachmentContent = $retriever->getAttachment(new IRI($object->getId()),
$reader->getFirstValueString($r, 'phpmae:servesStaticFileAttachment'));
if (!isset($attachmentContent))
return (new Response(501))->write("Requested static file attachment cannot be found.");
if (!isset($attachmentContent)) {
$notFoundResponse = new Response(501);
$notFoundResponse->getBody()->write("Requested static file attachment cannot be found.");
return $notFoundResponse;
}
return $engine->generateResponse($attachmentContent)
->withHeader('ETag', $etag)
->withHeader('Cache-Control', 'max-age='.self::STATIC_CACHE_TTL.', public');
} else {
// Route has no implementation
return (new Response(501))->write("Route implementation not available or no access granted.");
$notImplResponse = new Response(501);
$notImplResponse->getBody()->write("Route implementation not available or no access granted.");
return $notImplResponse;
}
});
}
@@ -235,26 +249,20 @@ class Router {
* Setup and run router.
*/
public function run() {
try {
try {
$modes = explode('|', $this->container->get('mode'));
$fallback = false;
$configuration = [
'settings' => [
'displayErrorDetails' => true,
],
];
$c = new \Slim\Container($configuration);
$app = new App($c);
$app = AppFactory::create();
$serverRequest = ServerRequestCreatorFactory::create()->createServerRequestFromGlobals();
$router = null;
$env = new Environment($_SERVER);
foreach ($modes as $m) {
switch ($m) {
case 'router:vhost':
if ($router == null) {
$uri = Uri::createFromEnvironment($env);
if ($uri->getHost() == 'localhost' || filter_var($uri->getHost(), FILTER_VALIDATE_IP) !== false) continue;
$uri = $serverRequest->getUri();
if ($uri->getHost() == 'localhost' || filter_var($uri->getHost(), FILTER_VALIDATE_IP) !== false) break;
$namespace = $this->objectRetriever->get('coid://'.$uri->getHost());
if (isset($namespace) && $routerCoid = $namespace->getProperty('coid://phpmae.dev/hasRouter'))
@@ -263,9 +271,8 @@ class Router {
break;
case 'router:header':
if ($router == null) {
$request = Request::createFromEnvironment($env);
if ($request->hasHeader('C-PhpMae-Router-COID'))
$router = $this->getRouter(new IRI($request->getHeaderLine('C-PhpMae-Router-COID')));
if ($serverRequest->hasHeader('C-PhpMae-Router-COID'))
$router = $this->getRouter(new IRI($serverRequest->getHeaderLine('C-PhpMae-Router-COID')));
}
break;
case 'default':
@@ -281,7 +288,7 @@ class Router {
if (isset($router)) {
$this->configure($app, $router);
$response = $app->run(true);
$response = $app->handle($serverRequest);
} elseif ($fallback) {
$this->container->get(Engine::class)
->run();
@@ -292,11 +299,10 @@ class Router {
} catch (PhpMAEException $e) {
// Create plain-text error response
$response = (new Response(500))
->withHeader('Content-Type', 'text/plain')
->write($e->getMessage());
$response = (new Response(500))->withHeader('Content-Type', 'text/plain');
$response->getBody()->write($e->getMessage());
}
$app->respond($response);
(new ResponseEmitter())->emit($response);
}
}
}
+5 -7
View File
@@ -8,7 +8,7 @@ namespace CloudObjects\PhpMAE\Sandbox;
use PHPSandbox\PHPSandbox, PHPSandbox\SandboxWhitelistVisitor,
PHPSandbox\Error;
use PhpParser\NodeTraverser, PhpParser\ParserFactory;
use PhpParser\NodeTraverser, PhpParser\PhpVersion, PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
use PhpParser\Error as ParserError;
@@ -38,10 +38,10 @@ class CustomizedSandbox extends PHPSandbox {
]);
}
public function validate($code) {
public function validate($code) : self {
$this->preparsed_code = $this->disassemble($code);
$factory = new ParserFactory;
$parser = $factory->create(ParserFactory::PREFER_PHP7);
$parser = $factory->createForVersion(PhpVersion::getHostVersion());
try {
$this->parsed_ast = $parser->parse($this->preparsed_code);
} catch (ParserError $error) {
@@ -55,13 +55,11 @@ class CustomizedSandbox extends PHPSandbox {
($this->allow_traits && $this->auto_whitelist_traits) ||
($this->allow_globals && $this->auto_whitelist_globals)){
$traverser = new NodeTraverser;
$whitelister = new SandboxWhitelistVisitor($this);
$traverser->addVisitor($whitelister);
$traverser->addVisitor(new SandboxWhitelistVisitor($this));
$traverser->traverse($this->parsed_ast);
}
$traverser = new NodeTraverser;
$validator = new CustomizedValidatorVisitor($this);
$traverser->addVisitor($validator);
$traverser->addVisitor(new CustomizedValidatorVisitor($this));
$this->prepared_ast = $traverser->traverse($this->parsed_ast);
$this->prepared_code = $prettyPrinter->prettyPrint($this->prepared_ast);
return $this;
+351 -110
View File
@@ -6,15 +6,50 @@
namespace CloudObjects\PhpMAE\Sandbox;
use PhpParser\Node, PhpParser\NodeVisitorAbstract;
use PHPSandbox\PHPSandbox, PHPSandbox\ValidatorVisitor,
PHPSandbox\Error;
use PhpParser\Node,
PhpParser\NodeTraverser,
PhpParser\NodeVisitorAbstract,
Throwable;
class CustomizedValidatorVisitor extends ValidatorVisitor {
/**
* Validator class for PHP Sandboxes.
*
* This class takes parsed AST code and checks it against the passed PHPSandbox instance
* configuration for errors, and throws exceptions if they are found
*
* @namespace PHPSandbox
*
* @author Elijah Horton <elijah@corveda.com>
* @version 3.0
*/
class CustomizedValidatorVisitor extends NodeVisitorAbstract {
/** The PHPSandbox instance to check against
* @var PHPSandbox
*/
protected CustomizedSandbox $sandbox;
/** ValidatorVisitor class constructor
*
* This constructor takes a passed PHPSandbox instance to check against for validating sandboxed code.
*
* @param CustomizedSandbox $sandbox The PHPSandbox instance to check against
*/
public function __construct(CustomizedSandbox $sandbox){
$this->sandbox = $sandbox;
}
/** Examine the current PhpParser_Node node against the PHPSandbox configuration for validating sandboxed code
*
* @param Node $node The sandboxed $node to validate
*
* @throws Throwable Throws an exception if validation fails
*
* @return Node|bool|null Return rewritten node, false if node must be removed, or null if no changes to the node are made
*/
public function leaveNode(Node $node){
if($node instanceof Node\Arg){
return new Node\Expr\FuncCall(new Node\Name\FullyQualified(($node->value instanceof Node\Expr\Variable) ? 'PHPSandbox\\wrapByRef' : 'PHPSandbox\\wrap'), [$node, new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox')], $node->getAttributes());
if($node instanceof Node\Arg && $this->sandbox->sandbox_strings){
return new Node\Expr\FuncCall(new Node\Name\FullyQualified(($node->value instanceof Node\Expr\Variable) ? 'PHPSandbox\\wrapByRef' : 'PHPSandbox\\wrap'), [$node, new Node\Expr\StaticCall(new Node\Name\FullyQualified("PHPSandbox\\PHPSandbox"), 'getSandbox', [new Node\Arg(new Node\Scalar\String_($this->sandbox->name))])], $node->getAttributes());
} else if($node instanceof Node\Stmt\InlineHTML){
if(!$this->sandbox->allow_escaping){
$this->sandbox->validationError("Sandboxed code attempted to escape to HTML!", Error::ESCAPE_ERROR, $node);
@@ -24,140 +59,140 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
$this->sandbox->validationError("Sandboxed code attempted to cast!", Error::CAST_ERROR, $node);
}
if($node instanceof Node\Expr\Cast\Int_){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_intval', [new Node\Arg($node->expr)], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_intval', [new Node\Arg($node->expr)], $node->getAttributes());
} else if($node instanceof Node\Expr\Cast\Double){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_floatval', [new Node\Arg($node->expr)], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_floatval', [new Node\Arg($node->expr)], $node->getAttributes());
} else if($node instanceof Node\Expr\Cast\Bool_){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_boolval', [new Node\Arg($node->expr)], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_boolval', [new Node\Arg($node->expr)], $node->getAttributes());
} else if($node instanceof Node\Expr\Cast\Array_){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_arrayval', [new Node\Arg($node->expr)], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_arrayval', [new Node\Arg($node->expr)], $node->getAttributes());
} else if($node instanceof Node\Expr\Cast\Object_){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_objectval', [new Node\Arg($node->expr)], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_objectval', [new Node\Arg($node->expr)], $node->getAttributes());
}
} else if($node instanceof Node\Expr\FuncCall){
if($node->name instanceof Node\Name){
$name = strtolower($node->name->toString());
if(!$this->sandbox->checkFunc($name)){
$this->sandbox->validationError("Function failed custom validation!", Error::VALID_FUNC_ERROR, $node);
$this->sandbox->validationError('Function failed custom validation!', Error::VALID_FUNC_ERROR, $node);
}
if($this->sandbox->isDefinedFunc($name)){
$args = $node->args;
array_unshift($args, new Node\Arg(new Node\Scalar\String_($name)));
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), 'call_func', $args, $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), 'call_func', $args, $node->getAttributes());
}
if($this->sandbox->overwrite_defined_funcs && in_array($name, PHPSandbox::$defined_funcs)){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name([$name])))], $node->getAttributes());
if($this->sandbox->overwrite_defined_funcs && in_array($name, CustomizedSandbox::$defined_funcs)){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name([$name])))], $node->getAttributes());
}
if($this->sandbox->overwrite_sandboxed_string_funcs && in_array($name, PHPSandbox::$sandboxed_string_funcs)){
if($this->sandbox->overwrite_sandboxed_string_funcs && in_array($name, CustomizedSandbox::$sandboxed_string_funcs)){
$args = $node->args;
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_' . $name, $args, $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_' . $name, $args, $node->getAttributes());
}
if($this->sandbox->overwrite_func_get_args && in_array($name, PHPSandbox::$arg_funcs)){
if($name == 'func_get_arg'){
if($this->sandbox->overwrite_func_get_args && in_array($name, CustomizedSandbox::$arg_funcs)){
if($name === 'func_get_arg'){
$index = new Node\Arg(new Node\Scalar\LNumber(0));
if(isset($node->args[0]) && $node->args[0] instanceof Node\Arg){
$index = $node->args[0];
}
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name(['func_get_args']))), $index], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name(['func_get_args']))), $index], $node->getAttributes());
}
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name(['func_get_args'])))], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_' . $name, [new Node\Arg(new Node\Expr\FuncCall(new Node\Name(['func_get_args'])))], $node->getAttributes());
}
} else {
return new Node\Expr\Ternary(
new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), 'check_func', [new Node\Arg($node->name)], $node->getAttributes()),
new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), 'checkFunc', [new Node\Arg($node->name)], $node->getAttributes()),
$node,
new Node\Expr\ConstFetch(new Node\Name('null'))
);
}
} else if($node instanceof Node\Stmt\Function_){
if(!$this->sandbox->allow_functions){
$this->sandbox->validationError("Sandboxed code attempted to define function!", Error::DEFINE_FUNC_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define function!', Error::DEFINE_FUNC_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('function')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'function');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'function');
}
if(!$node->name){
$this->sandbox->validationError("Sandboxed code attempted to define unnamed function!", Error::DEFINE_FUNC_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to define unnamed function!', Error::DEFINE_FUNC_ERROR, $node, '');
}
if($this->sandbox->isDefinedFunc($node->name)){
$this->sandbox->validationError("Sandboxed code attempted to redefine function!", Error::DEFINE_FUNC_ERROR, $node, $node->name);
$this->sandbox->validationError('Sandboxed code attempted to redefine function!', Error::DEFINE_FUNC_ERROR, $node, $node->name);
}
if($node->byRef && !$this->sandbox->allow_references){
$this->sandbox->validationError("Sandboxed code attempted to define function return by reference!", Error::BYREF_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define function return by reference!', Error::BYREF_ERROR, $node);
}
} else if($node instanceof Node\Expr\Closure){
if(!$this->sandbox->allow_closures){
$this->sandbox->validationError("Sandboxed code attempted to create a closure!", Error::CLOSURE_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to create a closure!', Error::CLOSURE_ERROR, $node);
}
} else if($node instanceof Node\Stmt\Class_){
if(!$this->sandbox->allow_classes){
$this->sandbox->validationError("Sandboxed code attempted to define class!", Error::DEFINE_CLASS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define class!', Error::DEFINE_CLASS_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('class')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'class');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'class');
}
if(!$node->name){
$this->sandbox->validationError("Sandboxed code attempted to define unnamed class!", Error::DEFINE_CLASS_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to define unnamed class!', Error::DEFINE_CLASS_ERROR, $node, '');
}
if(!$this->sandbox->checkClass($node->name)){
$this->sandbox->validationError("Class failed custom validation!", Error::VALID_CLASS_ERROR, $node, $node->name);
$this->sandbox->validationError('Class failed custom validation!', Error::VALID_CLASS_ERROR, $node, $node->name);
}
if($node->extends instanceof Node\Name){
if(!$this->sandbox->checkKeyword('extends')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'extends');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'extends');
}
if(!$node->extends->toString()){
$this->sandbox->validationError("Sandboxed code attempted to extend unnamed class!", Error::DEFINE_CLASS_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to extend unnamed class!', Error::DEFINE_CLASS_ERROR, $node, '');
}
if(!$this->sandbox->checkClass($node->extends->toString(), true)){
$this->sandbox->validationError("Class extension failed custom validation!", Error::VALID_CLASS_ERROR, $node, $node->extends->toString());
$this->sandbox->validationError('Class extension failed custom validation!', Error::VALID_CLASS_ERROR, $node, $node->extends->toString());
}
}
if(is_array($node->implements)){
if(!$this->sandbox->checkKeyword('implements')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'implements');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'implements');
}
foreach($node->implements as $implement){
/**
* @var Node\Name $implement
*/
if(!$implement->toString()){
$this->sandbox->validationError("Sandboxed code attempted to implement unnamed interface!", Error::DEFINE_INTERFACE_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to implement unnamed interface!', Error::DEFINE_INTERFACE_ERROR, $node, '');
}
if(!$this->sandbox->checkInterface($implement->toString())){
$this->sandbox->validationError("Interface failed custom validation!", Error::VALID_INTERFACE_ERROR, $node, $implement->toString());
$this->sandbox->validationError('Interface failed custom validation!', Error::VALID_INTERFACE_ERROR, $node, $implement->toString());
}
}
}
} else if($node instanceof Node\Stmt\Interface_){
if(!$this->sandbox->allow_interfaces){
$this->sandbox->validationError("Sandboxed code attempted to define interface!", Error::DEFINE_INTERFACE_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define interface!', Error::DEFINE_INTERFACE_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('interface')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'interface');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'interface');
}
if(!$node->name){
$this->sandbox->validationError("Sandboxed code attempted to define unnamed interface!", Error::DEFINE_INTERFACE_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to define unnamed interface!', Error::DEFINE_INTERFACE_ERROR, $node, '');
}
if(!$this->sandbox->checkInterface($node->name)){
$this->sandbox->validationError("Interface failed custom validation!", Error::VALID_INTERFACE_ERROR, $node, $node->name);
$this->sandbox->validationError('Interface failed custom validation!', Error::VALID_INTERFACE_ERROR, $node, $node->name);
}
} else if($node instanceof Node\Stmt\Trait_){
if(!$this->sandbox->allow_traits){
$this->sandbox->validationError("Sandboxed code attempted to define trait!", Error::DEFINE_TRAIT_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define trait!', Error::DEFINE_TRAIT_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('trait')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'trait');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'trait');
}
if(!$node->name){
$this->sandbox->validationError("Sandboxed code attempted to define unnamed trait!", Error::DEFINE_TRAIT_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to define unnamed trait!', Error::DEFINE_TRAIT_ERROR, $node, '');
}
if(!$this->sandbox->checkTrait($node->name)){
$this->sandbox->validationError("Trait failed custom validation!", Error::VALID_TRAIT_ERROR, $node, $node->name);
$this->sandbox->validationError('Trait failed custom validation!', Error::VALID_TRAIT_ERROR, $node, $node->name);
}
} else if($node instanceof Node\Stmt\TraitUse){
if(!$this->sandbox->checkKeyword('use')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'use');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'use');
}
if(is_array($node->traits)){
foreach($node->traits as $trait){
@@ -165,26 +200,26 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
* @var Node\Name $trait
*/
if(!$trait->toString()){
$this->sandbox->validationError("Sandboxed code attempted to use unnamed trait!", Error::DEFINE_TRAIT_ERROR, $node, '');
$this->sandbox->validationError('Sandboxed code attempted to use unnamed trait!', Error::DEFINE_TRAIT_ERROR, $node, '');
}
if(!$this->sandbox->checkTrait($trait->toString())){
$this->sandbox->validationError("Trait failed custom validation!", Error::VALID_TRAIT_ERROR, $node, $trait->toString());
$this->sandbox->validationError('Trait failed custom validation!', Error::VALID_TRAIT_ERROR, $node, $trait->toString());
}
}
}
} else if($node instanceof Node\Expr\Yield_){
if(!$this->sandbox->allow_generators){
$this->sandbox->validationError("Sandboxed code attempted to create a generator!", Error::GENERATOR_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to create a generator!', Error::GENERATOR_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('yield')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'yield');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'yield');
}
} else if($node instanceof Node\Stmt\Global_){
if(!$this->sandbox->allow_globals){
$this->sandbox->validationError("Sandboxed code attempted to use global keyword!", Error::GLOBALS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to use global keyword!', Error::GLOBALS_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('global')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'global');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'global');
}
foreach($node->vars as $var){
/**
@@ -192,57 +227,57 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
*/
if($var instanceof Node\Expr\Variable){
if(!$this->sandbox->checkGlobal($var->name)){
$this->sandbox->validationError("Global failed custom validation!", Error::VALID_GLOBAL_ERROR, $node, $var->name);
$this->sandbox->validationError('Global failed custom validation!', Error::VALID_GLOBAL_ERROR, $node, $var->name);
}
} else {
$this->sandbox->validationError("Sandboxed code attempted to pass non-variable to global keyword!", Error::DEFINE_GLOBAL_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to pass non-variable to global keyword!', Error::DEFINE_GLOBAL_ERROR, $node);
}
}
} else if($node instanceof Node\Expr\Variable){
if(!is_string($node->name)){
$this->sandbox->validationError("Sandboxed code attempted dynamically-named variable call!", Error::DYNAMIC_VAR_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted dynamically-named variable call!', Error::DYNAMIC_VAR_ERROR, $node);
}
if($node->name == $this->sandbox->name){
$this->sandbox->validationError("Sandboxed code attempted to access the PHPSandbox instance!", Error::SANDBOX_ACCESS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to access the PHPSandbox instance!', Error::SANDBOX_ACCESS_ERROR, $node);
}
if(in_array($node->name, PHPSandbox::$superglobals)){
if(in_array($node->name, CustomizedSandbox::$superglobals)){
if(!$this->sandbox->checkSuperglobal($node->name)){
$this->sandbox->validationError("Superglobal failed custom validation!", Error::VALID_SUPERGLOBAL_ERROR, $node, $node->name);
$this->sandbox->validationError('Superglobal failed custom validation!', Error::VALID_SUPERGLOBAL_ERROR, $node, $node->name);
}
if($this->sandbox->overwrite_superglobals){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_get_superglobal', [new Node\Arg(new Node\Scalar\String_($node->name))], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_get_superglobal', [new Node\Arg(new Node\Scalar\String_($node->name))], $node->getAttributes());
}
} else {
if(!$this->sandbox->checkVar($node->name)){
$this->sandbox->validationError("Variable failed custom validation!", Error::VALID_VAR_ERROR, $node, $node->name);
$this->sandbox->validationError('Variable failed custom validation!', Error::VALID_VAR_ERROR, $node, $node->name);
}
}
} else if($node instanceof Node\Stmt\StaticVar){
if(!$this->sandbox->allow_static_variables){
$this->sandbox->validationError("Sandboxed code attempted to create static variable!", Error::STATIC_VAR_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to create static variable!', Error::STATIC_VAR_ERROR, $node);
}
if(!is_string($node->name)){
$this->sandbox->validationError("Sandboxed code attempted dynamically-named static variable call!", Error::DYNAMIC_STATIC_VAR_ERROR, $node);
if(!is_string($node->var->name)){
$this->sandbox->validationError('Sandboxed code attempted dynamically-named static variable call!', Error::DYNAMIC_STATIC_VAR_ERROR, $node);
}
if(!$this->sandbox->checkVar($node->name)){
$this->sandbox->validationError("Variable failed custom validation!", Error::VALID_VAR_ERROR, $node, $node->name);
if(!$this->sandbox->checkVar($node->var->name)){
$this->sandbox->validationError('Variable failed custom validation!', Error::VALID_VAR_ERROR, $node, $node->var->name);
}
if($node->default instanceof Node\Expr\New_){
$node->default = $node->default->args[0];
}
} else if($node instanceof Node\Stmt\Const_){
$this->sandbox->validationError("Sandboxed code cannot use const keyword in the global scope!", Error::GLOBAL_CONST_ERROR, $node);
$this->sandbox->validationError('Sandboxed code cannot use const keyword in the global scope!', Error::GLOBAL_CONST_ERROR, $node);
} else if($node instanceof Node\Expr\ConstFetch){
if(!$node->name instanceof Node\Name){
$this->sandbox->validationError("Sandboxed code attempted dynamically-named constant call!", Error::DYNAMIC_CONST_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted dynamically-named constant call!', Error::DYNAMIC_CONST_ERROR, $node);
}
if(!$this->sandbox->checkConst($node->name->toString())){
$this->sandbox->validationError("Constant failed custom validation!", Error::VALID_CONST_ERROR, $node, $node->name->toString());
$this->sandbox->validationError('Constant failed custom validation!', Error::VALID_CONST_ERROR, $node, $node->name->toString());
}
} else if($node instanceof Node\Expr\ClassConstFetch || $node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\StaticPropertyFetch){
$class = $node->class;
if(!$class instanceof Node\Name){
$this->sandbox->validationError("Sandboxed code attempted dynamically-named class call!", Error::DYNAMIC_CLASS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted dynamically-named class call!', Error::DYNAMIC_CLASS_ERROR, $node);
}
if($this->sandbox->isDefinedClass($class)){
$node->class = new Node\Name($this->sandbox->getDefinedClass($class));
@@ -251,7 +286,7 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
* @var Node\Name $class
*/
if(!$this->sandbox->checkClass($class->toString())){
$this->sandbox->validationError("Class constant failed custom validation!", Error::VALID_CLASS_ERROR, $node, $class->toString());
$this->sandbox->validationError('Class constant failed custom validation!', Error::VALID_CLASS_ERROR, $node, $class->toString());
}
return $node;
} else if($node instanceof Node\Param && $node->type instanceof Node\Name){
@@ -262,13 +297,13 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
return $node;
} else if($node instanceof Node\Expr\New_){
if(!$this->sandbox->allow_objects){
$this->sandbox->validationError("Sandboxed code attempted to create object!", Error::CREATE_OBJECT_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to create object!', Error::CREATE_OBJECT_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('new')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'new');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'new');
}
if(!$node->class instanceof Node\Name){
$this->sandbox->validationError("Sandboxed code attempted dynamically-named class call!", Error::DYNAMIC_CLASS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted dynamically-named class call!', Error::DYNAMIC_CLASS_ERROR, $node);
}
$class = $node->class->toString();
if($this->sandbox->isDefinedClass($class)){
@@ -278,25 +313,25 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
return $node;
} else if($node instanceof Node\Expr\ErrorSuppress){
if(!$this->sandbox->allow_error_suppressing){
$this->sandbox->validationError("Sandboxed code attempted to suppress error!", Error::ERROR_SUPPRESS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to suppress error!', Error::ERROR_SUPPRESS_ERROR, $node);
}
} else if($node instanceof Node\Expr\AssignRef){
if(!$this->sandbox->allow_references){
$this->sandbox->validationError("Sandboxed code attempted to assign by reference!", Error::BYREF_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to assign by reference!', Error::BYREF_ERROR, $node);
}
} else if($node instanceof Node\Stmt\HaltCompiler){
if(!$this->sandbox->allow_halting){
$this->sandbox->validationError("Sandboxed code attempted to halt compiler!", Error::HALT_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to halt compiler!', Error::HALT_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('halt')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'halt');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'halt');
}
} else if($node instanceof Node\Stmt\Namespace_){
if(!$this->sandbox->allow_namespaces){
$this->sandbox->validationError("Sandboxed code attempted to define namespace!", Error::DEFINE_NAMESPACE_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to define namespace!', Error::DEFINE_NAMESPACE_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('namespace')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'namespace');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'namespace');
}
if($node->name instanceof Node\Name){
$namespace = $node->name->toString();
@@ -305,99 +340,305 @@ class CustomizedValidatorVisitor extends ValidatorVisitor {
$this->sandbox->defineNamespace($namespace);
}
} else {
$this->sandbox->validationError("Sandboxed code attempted use invalid namespace!", Error::DEFINE_NAMESPACE_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted use invalid namespace!', Error::DEFINE_NAMESPACE_ERROR, $node);
}
return $node->stmts;
} else if($node instanceof Node\Stmt\Use_){
if(!$this->sandbox->allow_aliases){
$this->sandbox->validationError("Sandboxed code attempted to use namespace and/or alias!", Error::DEFINE_ALIAS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to use namespace and/or alias!', Error::DEFINE_ALIAS_ERROR, $node);
}
if(!$this->sandbox->checkKeyword('use')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'use');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'use');
}
foreach($node->uses as $use){
/**
* @var Node\Stmt\UseUse $use
*/
if($use instanceof Node\Stmt\UseUse && $use->name instanceof Node\Name && (is_string($use->alias) || is_null($use->alias))){
if($use instanceof Node\Stmt\UseUse && $use->name instanceof Node\Name && (is_string($use->alias) || $use->alias instanceof Node\Identifier || is_null($use->alias))){
$this->sandbox->checkAlias($use->name->toString());
if($use->alias){
if(!$this->sandbox->checkKeyword('as')){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, 'as');
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, 'as');
}
}
$this->sandbox->defineAlias($use->name->toString(), $use->alias);
$this->sandbox->whitelistClass($use->getAlias());
$this->sandbox->whitelistType($use->getAlias());
$this->sandbox->defineAlias($use->name->toString(), $use->getAlias());
} else {
$this->sandbox->validationError("Sandboxed code attempted use invalid namespace or alias!", Error::DEFINE_ALIAS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted use invalid namespace or alias!', Error::DEFINE_ALIAS_ERROR, $node);
}
}
return false;
return NodeTraverser::REMOVE_NODE;
} else if($node instanceof Node\Expr\ShellExec){
if($this->sandbox->isDefinedFunc('shell_exec')){
$args = [
new Node\Arg(new Node\Scalar\String_('shell_exec')),
new Node\Arg(new Node\Scalar\String_(implode('', $node->parts)))
];
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), 'call_func', $args, $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), 'call_func', $args, $node->getAttributes());
}
if($this->sandbox->hasWhitelistedFuncs()){
if(!$this->sandbox->isWhitelistedFunc('shell_exec')){
$this->sandbox->validationError("Sandboxed code attempted to use shell execution backticks when the shell_exec function is not whitelisted!", Error::BACKTICKS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to use shell execution backticks when the shell_exec function is not whitelisted!', Error::BACKTICKS_ERROR, $node);
}
} else if($this->sandbox->hasBlacklistedFuncs() && $this->sandbox->isBlacklistedFunc('shell_exec')){
$this->sandbox->validationError("Sandboxed code attempted to use shell execution backticks when the shell_exec function is blacklisted!", Error::BACKTICKS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to use shell execution backticks when the shell_exec function is blacklisted!', Error::BACKTICKS_ERROR, $node);
}
if(!$this->sandbox->allow_backticks){
$this->sandbox->validationError("Sandboxed code attempted to use shell execution backticks!", Error::BACKTICKS_ERROR, $node);
$this->sandbox->validationError('Sandboxed code attempted to use shell execution backticks!', Error::BACKTICKS_ERROR, $node);
}
} else if($name = $this->isMagicConst($node)){
if(!$this->sandbox->checkMagicConst($name)){
$this->sandbox->validationError("Magic constant failed custom validation!", Error::VALID_MAGIC_CONST_ERROR, $node, $name);
$this->sandbox->validationError('Magic constant failed custom validation!', Error::VALID_MAGIC_CONST_ERROR, $node, $name);
}
if($this->sandbox->isDefinedMagicConst($name)){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_get_magic_const', [new Node\Arg(new Node\Scalar\String_($name))], $node->getAttributes());
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_get_magic_const', [new Node\Arg(new Node\Scalar\String_($name))], $node->getAttributes());
} else if(($filepath = $this->sandbox->getExecutingFile()) && in_array($name, ['__DIR__', '__FILE__'], true)){
return new Node\Scalar\String_($name === '__DIR__' ? dirname($filepath) : $filepath);
}
} else if($name = $this->isKeyword($node)){
if(!$this->sandbox->checkKeyword($name)){
$this->sandbox->validationError("Keyword failed custom validation!", Error::VALID_KEYWORD_ERROR, $node, $name);
$this->sandbox->validationError('Keyword failed custom validation!', Error::VALID_KEYWORD_ERROR, $node, $name);
}
if($node instanceof Node\Expr\Include_ && !$this->sandbox->allow_includes){
$this->sandbox->validationError("Sandboxed code attempted to include files!", Error::INCLUDE_ERROR, $node, $name);
$this->sandbox->validationError('Sandboxed code attempted to include files!', Error::INCLUDE_ERROR, $node, $name);
} else if($node instanceof Node\Expr\Include_ &&
(
($node->type == Node\Expr\Include_::TYPE_INCLUDE && $this->sandbox->isDefinedFunc('include'))
|| ($node->type == Node\Expr\Include_::TYPE_INCLUDE_ONCE && $this->sandbox->isDefinedFunc('include_once'))
|| ($node->type == Node\Expr\Include_::TYPE_REQUIRE && $this->sandbox->isDefinedFunc('require'))
|| ($node->type == Node\Expr\Include_::TYPE_REQUIRE_ONCE && $this->sandbox->isDefinedFunc('require_once'))
)){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), 'call_func', [new Node\Arg(new Node\Scalar\String_($name)), new Node\Arg($node->expr)], $node->getAttributes());
)
){
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), 'call_func', [new Node\Arg(new Node\Scalar\String_($name)), new Node\Arg($node->expr)], $node->getAttributes());
} else if($node instanceof Node\Expr\Include_ && $this->sandbox->sandbox_includes){
switch($node->type){
case Node\Expr\Include_::TYPE_INCLUDE_ONCE:
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_include_once', [new Node\Arg($node->expr)], $node->getAttributes());
break;
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_include_once', [new Node\Arg($node->expr)], $node->getAttributes());
case Node\Expr\Include_::TYPE_REQUIRE:
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_require', [new Node\Arg($node->expr)], $node->getAttributes());
break;
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_require', [new Node\Arg($node->expr)], $node->getAttributes());
case Node\Expr\Include_::TYPE_REQUIRE_ONCE:
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_require_once', [new Node\Arg($node->expr)], $node->getAttributes());
break;
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_require_once', [new Node\Arg($node->expr)], $node->getAttributes());
case Node\Expr\Include_::TYPE_INCLUDE:
default:
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified("CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox"), 'getGlobalSandbox'), '_include', [new Node\Arg($node->expr)], $node->getAttributes());
break;
return new Node\Expr\MethodCall(new Node\Expr\StaticCall(new Node\Name\FullyQualified('CloudObjects\\PhpMAE\\Sandbox\\CustomizedSandbox'), 'getGlobalSandbox'), '_include', [new Node\Arg($node->expr)], $node->getAttributes());
}
}
} else if($name = $this->isOperator($node)){
if(!$this->sandbox->checkOperator($name)){
$this->sandbox->validationError("Operator failed custom validation!", Error::VALID_OPERATOR_ERROR, $node, $name);
$this->sandbox->validationError('Operator failed custom validation!', Error::VALID_OPERATOR_ERROR, $node, $name);
}
} else if($name = $this->isPrimitive($node)){
if(!$this->sandbox->checkPrimitive($name)){
$this->sandbox->validationError("Primitive failed custom validation!", Error::VALID_PRIMITIVE_ERROR, $node, $name);
$this->sandbox->validationError('Primitive failed custom validation!', Error::VALID_PRIMITIVE_ERROR, $node, $name);
}
}
return null;
}
}
/** Test the current PhpParser_Node node to see if it is a magic constant, and return the name if it is and null if it is not
*
* @param Node $node The sandboxed $node to test
*
* @return string|null Return string name of node, or null if it is not a magic constant
*/
protected function isMagicConst(Node $node) : ?string {
return ($node instanceof Node\Scalar\MagicConst) ? $node->getName() : null;
}
/** Test the current PhpParser_Node node to see if it is a keyword, and return the name if it is and null if it is not
*
* @param Node $node The sandboxed $node to test
*
* @return string|null Return string name of node, or null if it is not a keyword
*/
protected function isKeyword(Node $node) : ?string {
switch($node->getType()){
case 'Expr_Eval':
return 'eval';
case 'Expr_Exit':
return 'exit';
case 'Expr_Include':
return 'include';
case 'Stmt_Echo':
case 'Expr_Print': //for our purposes print is treated as functionally equivalent to echo
return 'echo';
case 'Expr_Clone':
return 'clone';
case 'Expr_Empty':
return 'empty';
case 'Expr_Yield':
return 'yield';
case 'Stmt_Goto':
case 'Stmt_Label': //no point in using labels without goto
return 'goto';
case 'Stmt_If':
case 'Stmt_Else': //no point in using ifs without else
case 'Stmt_ElseIf': //no point in using ifs without elseif
return 'if';
case 'Stmt_Break':
return 'break';
case 'Stmt_Switch':
case 'Stmt_Case': //no point in using cases without switch
return 'switch';
case 'Stmt_Try':
case 'Stmt_Catch': //no point in using catch without try
case 'Stmt_TryCatch': //no point in using try, catch or finally without try
return 'try';
case 'Stmt_Throw':
return 'throw';
case 'Stmt_Unset':
return 'unset';
case 'Stmt_Return':
return 'return';
case 'Stmt_Static':
return 'static';
case 'Stmt_While':
case 'Stmt_Do': //no point in using do without while
return 'while';
case 'Stmt_Declare':
case 'Stmt_DeclareDeclare': //no point in using declare key=>value without declare
return 'declare';
case 'Stmt_For':
case 'Stmt_Foreach': //no point in using foreach without for
return 'for';
case 'Expr_Instanceof':
return 'instanceof';
case 'Expr_Isset':
return 'isset';
case 'Expr_List':
return 'list';
}
return null;
}
/** Test the current PhpParser_Node node to see if it is an operator, and return the name if it is and null if it is not
*
* @param Node $node The sandboxed $node to test
*
* @return string|null Return string name of node, or null if it is not an operator
*/
protected function isOperator(Node $node) : ?string {
switch($node->getType()){
case 'Expr_Assign':
return '=';
case 'Expr_AssignBitwiseAnd':
return '&=';
case 'Expr_AssignBitwiseOr':
return '|=';
case 'Expr_AssignBitwiseXor':
return '^=';
case 'Expr_AssignConcat':
return '.=';
case 'Expr_AssignDiv':
return '/=';
case 'Expr_AssignMinus':
return '-=';
case 'Expr_AssignMod':
return '%=';
case 'Expr_AssignMul':
return '*=';
case 'Expr_AssignPlus':
return '+=';
case 'Expr_AssignRef':
return '=&';
case 'Expr_AssignShiftLeft':
return '<<=';
case 'Expr_AssignShiftRight':
return '>>=';
case 'Expr_BitwiseAnd':
return '&';
case 'Expr_BitwiseNot':
return '~';
case 'Expr_BitwiseOr':
return '|';
case 'Expr_BitwiseXor':
return '^';
case 'Expr_BooleanAnd':
return '&&';
case 'Expr_BooleanNot':
return '!';
case 'Expr_BooleanOr':
return '||';
case 'Expr_Concat':
return '.';
case 'Expr_Div':
return '/';
case 'Expr_Equal':
return '==';
case 'Expr_Greater':
return '>';
case 'Expr_GreaterOrEqual':
return '>=';
case 'Expr_Identical':
return '===';
case 'Expr_LogicalAnd':
return 'and';
case 'Expr_LogicalOr':
return 'or';
case 'Expr_LogicalXor':
return 'xor';
case 'Expr_Minus':
return '-';
case 'Expr_Mod':
return '%';
case 'Expr_Mul':
return '*';
case 'Expr_NotEqual':
return '!=';
case 'Expr_NotIdentical':
return '!==';
case 'Expr_Plus':
return '+';
case 'Expr_PostDec':
return 'n--';
case 'Expr_PostInc':
return 'n++';
case 'Expr_PreDec':
return '--n';
case 'Expr_PreInc':
return '++n';
case 'Expr_ShiftLeft':
return '<<';
case 'Expr_ShiftRight':
return '>>';
case 'Expr_Smaller':
return '<';
case 'Expr_SmallerOrEqual':
return '<=';
case 'Expr_Ternary':
return '?';
case 'Expr_UnaryMinus':
return '-n';
case 'Expr_UnaryPlus':
return '+n';
}
return null;
}
/** Test the current PhpParser_Node node to see if it is a primitive, and return the name if it is and null if it is not
*
* @param Node $node The sandboxed $node to test
*
* @return string|null Return string name of node, or null if it is not a primitive
*/
protected function isPrimitive(Node $node) : ?string {
switch($node->getType()){
case 'Expr_Cast_Array':
case 'Expr_Array':
return 'array';
case 'Expr_Cast_Bool': //booleans are treated as constants otherwise. . .
return 'bool';
case 'Expr_Cast_String':
case 'Scalar_String':
case 'Scalar_Encapsed':
return 'string';
case 'Expr_Cast_Double':
case 'Scalar_DNumber':
return 'float';
case 'Expr_Cast_Int':
case 'Scalar_LNumber':
return 'int';
case 'Expr_Cast_Object':
return 'object';
}
return null;
}
}
+2 -2
View File
@@ -13,8 +13,8 @@ class TwigTemplate {
public function __construct($key, $content, $cachePath) {
$this->key = $key;
$this->environment = new \Twig_Environment(
new \Twig_Loader_Array([ $key => $content ]),
$this->environment = new \Twig\Environment(
new \Twig\Loader\ArrayLoader([ $key => $content ]),
[ 'cache' => $cachePath ]
);
}
+11 -10
View File
@@ -3,12 +3,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace CloudObjects\PhpMAE;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\RequestInterface;
use Slim\Http\Response;
use Slim\Psr7\Response;
use ML\IRI\IRI;
use ML\JsonLD\JsonLD;
use CloudObjects\Utilities\RDF\Arc2JsonLdConverter;
@@ -23,14 +23,14 @@ class UploadController {
public function __construct(ContainerInterface $container, ObjectRetriever $objectRetriever,
ClassRepository $classRepository) {
$this->container = $container;
$this->objectRetriever = $objectRetriever;
$this->classRepository = $classRepository;
}
}
private function uploadSource(RequestInterface $request) {
$object = $this->objectRetriever->get($request->getQueryParam('coid'));
$object = $this->objectRetriever->get($request->getQueryParams()['coid'] ?? null);
if (!$object)
throw new PhpMAEException("Unable to retrieve object.");
@@ -42,10 +42,11 @@ class UploadController {
new IRI($object->getId()),
TypeChecker::getAdditionalTypes($object));
} catch (\Exception $e) {
$response = (new Response(400))->withJson([
$response = (new Response(400))->withHeader('Content-Type', 'application/json');
$response->getBody()->write(json_encode([
'error_code' => get_class($e),
'error_message' => $e->getMessage()
]);
]));
return $response;
}
@@ -67,12 +68,12 @@ class UploadController {
// Validate config
if (!isset($jsonLdConfig->{'@id'})
|| $jsonLdConfig->{'@id'} != $request->getQueryParam('coid')) {
|| $jsonLdConfig->{'@id'} != ($request->getQueryParams()['coid'] ?? null)) {
throw new PhpMAEException("Uploaded configuration does not correspond to object.");
}
// Store configuration
$iri = new IRI($request->getQueryParam('coid'));
$iri = new IRI($request->getQueryParams()['coid'] ?? null);
$path = $this->container->get('uploads_dir')
.DIRECTORY_SEPARATOR.'config'
.DIRECTORY_SEPARATOR.$iri->getHost()
@@ -91,7 +92,7 @@ class UploadController {
if ($request->getMethod() != 'PUT')
throw new PhpMAEException("Must use PUT for uploading to test environment.");
switch ($request->getQueryParam('type')) {
switch ($request->getQueryParams()['type'] ?? null) {
case "source":
return $this->uploadSource($request);
break;