taskPackPhar('phpmae.phar') ->compress() ->addFile('phpmae.php', 'phpmae.php') ->addFile('config.php', 'config.php') ->addFile('web/index.php', 'web/index.php') ->stub('stub.php'); $finder = Finder::create() ->name('*.php') ->in('classes'); foreach ($finder as $file) { $pharTask->addFile('classes/'.$file->getRelativePathname(), $file->getRealPath()); } $finder = Finder::create()->files() ->name('*.php') ->in('vendor'); foreach ($finder as $file) { $pharTask->addStripped('vendor/'.$file->getRelativePathname(), $file->getRealPath()); } $finder = Finder::create()->files() ->name('*.php') ->name('*.json') ->in('stacks'); foreach ($finder as $file) { $pharTask->addStripped('stacks/'.$file->getRelativePathname(),$file->getRealPath()); } $pharTask->run(); // Verify Phar is packed correctly $code = $this->_exec('php phpmae.phar'); } public function sanitizeDependencies($directory) { require_once "vendor/autoload.php"; $finder = Finder::create()->files() ->name('*.php') ->in($directory) ->notPath('composer'); $forbiddenFunctions = [ 'file_get_contents', 'file_put_contents', 'fopen' ]; foreach ($finder as $file) { try { $content = file_get_contents($file); foreach ($forbiddenFunctions as $func) { $content = preg_replace('/\b(?)'.$func.'\(/', '\CloudObjects\PhpMAE\Sandbox\FunctionExecutor::returnNull(', $content); $content = str_replace('\\\CloudObjects', '\CloudObjects', $content); } file_put_contents($file, $content); } catch (\Exception $e) { $this->say($file.': '.get_class($e).' '.$e->getMessage()); } } } public function buildFunctionWhitelist() { $allFunctions = get_defined_functions()['internal']; $whitelisted = file_exists('data/function.whitelist.json') ? json_decode(file_get_contents('data/function.whitelist.json'), true) : []; $blacklisted = file_exists('data/function.blacklist.json') ? json_decode(file_get_contents('data/function.blacklist.json'), true) : []; $client = new \GuzzleHttp\Client([ 'base_uri' => 'http://php.net/manual/en/' ]); $autoBlacklistPrefix = ''; $progress = (count($whitelisted) + count($blacklisted)) / count($allFunctions) * 100; $this->say($progress.'% of functions handled, let\'s do the rest ...'); foreach ($allFunctions as $f) { // Ignore if already handled if (in_array($f, $whitelisted) || in_array($f, $blacklisted)) continue; $this->yell($f); if ($autoBlacklistPrefix != '' && substr($f, 0, strlen($autoBlacklistPrefix)) == $autoBlacklistPrefix) { $this->say('Blacklisted automatically.'); $blacklisted[] = $f; continue; } $autoBlacklistPrefix = ''; try { // Get and display documentation $crawler = new \Symfony\Component\DomCrawler\Crawler( $client->get('function.'.str_replace('_', '-', $f).'.php')->getBody()->getContents() ); $this->say($crawler->filter('.refpurpose')->text()); $this->say($crawler->filter('.refsect1.description')->text()); } catch (\Exception $e) { $this->say('Error while retrieving information: '.$e->getMessage()); } // Ask what to do $action = null; while (!in_array($action, ['w','b','a','s','x'])) $action = $this->ask('(w)hitelist, (b)lacklist, (a)ll blacklist, (s)kip, e(x)it'); switch ($action) { case "w": $whitelisted[] = $f; break; case "b": $blacklisted[] = $f; break; case "a": $blacklisted[] = $f; $autoBlacklistPrefix = substr($f, 0, strpos($f, '_')+1); break; case "x": file_put_contents('data/function.whitelist.json', json_encode($whitelisted)); file_put_contents('data/function.blacklist.json', json_encode($blacklisted)); exit(0); } // Save on every step to prevent data loss from crashes file_put_contents('data/function.whitelist.json', json_encode($whitelisted)); file_put_contents('data/function.blacklist.json', json_encode($blacklisted)); } } public function installStack($stack) { require_once "vendor/autoload.php"; // Fetch stack definition from CloudObjects Core $stackCoid = COIDParser::fromString($stack); $retriever = new ObjectRetriever([ 'auth_ns' => 'phpmae.dev', 'auth_secret' => getenv('PHPMAE_SHARED_SECRET') ]); $stackObject = $retriever->getObject($stackCoid); $composerFileContent = $retriever->getAttachment($stackCoid, $stackObject->getProperty('coid://phpmae.dev/hasAttachedComposerFile') ->getId()); Assert::startsWith($composerFileContent, '{'); $lockFileContent = $retriever->getAttachment($stackCoid, $stackObject->getProperty('coid://phpmae.dev/hasAttachedLockFile') ->getId()); Assert::startsWith($lockFileContent, '{'); $stackDir = __DIR__.'/stacks/'.md5($stack); // Install stack $this->taskFilesystemStack() ->mkdir($stackDir) ->run(); $this->taskWriteToFile($stackDir.'/composer.json') ->text($composerFileContent) ->run(); $this->taskWriteToFile($stackDir.'/composer.lock') ->text($lockFileContent) ->run(); // Find whitelisted classes $whitelistedClasses = []; foreach ($stackObject->getProperty('coid://phpmae.dev/whitelistsClassname') as $whitelistedClass) $whitelistedClasses[] = $whitelistedClass->getValue(); $this->taskWriteToFile($stackDir.'/meta.json') ->text(json_encode([ 'rev' => $stackObject->getProperty(ObjectRetriever::REVISION_PROPERTY)->getValue(), 'whitelisted_classes' => $whitelistedClasses ])) ->run(); $this->taskComposerInstall(__DIR__.'/composer.phar') ->dir($stackDir) ->run(); // Sanitize stack $this->sanitizeDependencies($stackDir); } }