Skip to content

Commit

Permalink
fix handle php-parser 5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskapp committed Sep 10, 2024
1 parent d185cd1 commit 07b13d7
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 6 deletions.
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"nikic/php-parser": "^4.0|^5.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"vimeo/psalm": "^5.0"
"phpunit/phpunit": "^9.0"
},
"autoload": {
"psr-4": {
Expand Down
13 changes: 9 additions & 4 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
class Parser
{
private SecurityManager $securityManager;
private int $parserType;
private ?int $parserType;
private ParserFactory $parserFactory;

public function __construct(SecurityManager $securityManager = null, $parserType = ParserFactory::PREFER_PHP7)
public function __construct(SecurityManager $securityManager = null, ?int $parserType = null)
{
$this->securityManager = $securityManager ?? new SecurityManager();
$this->parserType = $parserType;
Expand All @@ -53,7 +53,13 @@ public function __construct(SecurityManager $securityManager = null, $parserType
*/
public function parse(string $code): string
{
$parser = $this->parserFactory->create($this->parserType);
if (method_exists($this->parserFactory, 'createForNewestSupportedVersion')) {
$parser = $this->parserFactory->createForNewestSupportedVersion();
$printer = new Printer5($this->securityManager);
} else {
$parser = $this->parserFactory->create($this->parserType ?? ParserFactory::PREFER_PHP7);
$printer = new Printer($this->securityManager);
}

try {
$ast = $parser->parse($code);
Expand All @@ -65,7 +71,6 @@ public function parse(string $code): string
throw new ParseException('Found no tokens');
}

$printer = new Printer($this->securityManager);
return $printer->prettyPrintFile($ast);
}
}
241 changes: 241 additions & 0 deletions src/Printer5.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
<?php
/*
* PSX is an open source PHP framework to develop RESTful APIs.
* For the current version and information visit <https://phpsx.org>
*
* Copyright 2010-2023 Christoph Kappestein <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace PSX\Sandbox;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinter\Standard;

/**
* Printer
*
* @author Christoph Kappestein <[email protected]>
* @license http://www.apache.org/licenses/LICENSE-2.0
* @link https://phpsx.org
*/
class Printer5 extends Standard
{
private SecurityManager $securityManager;

public function __construct(SecurityManager $securityManager, array $options = [])
{
parent::__construct($options);

$this->securityManager = $securityManager;
}

protected function pExpr_FuncCall(Expr\FuncCall $node): string
{
$functionName = $this->pCallLhs($node->name);

$this->securityManager->checkFunctionCall($functionName, $node->args);

return parent::pExpr_FuncCall($node);
}

protected function pExpr_Eval(Expr\Eval_ $node): string
{
throw new SecurityException('Eval is not allowed');
}

protected function pExpr_Include(Expr\Include_ $node, int $precedence, int $lhsPrecedence): string
{
throw new SecurityException('Include is not allowed');
}

protected function pExpr_ShellExec(Expr\ShellExec $node): string
{
throw new SecurityException('Shell exec is not allowed');
}

protected function pExpr_New(Expr\New_ $node): string
{
if ($node->class instanceof Stmt\Class_) {
throw new SecurityException('Anonymous class is not allowed');
}

$class = $this->p($node->class);

$this->securityManager->checkClassIsAllowed($class);

return parent::pExpr_New($node);
}

protected function pStaticDereferenceLhs(Node $node): string
{
$class = $this->p($node);

$this->securityManager->checkClassIsAllowed($class);

return parent::pStaticDereferenceLhs($node);
}

protected function pExpr_Exit(Expr\Exit_ $node): string
{
throw new SecurityException('Exit is not allowed');
}

protected function pStmt_Interface(Stmt\Interface_ $node): string
{
throw new SecurityException('Interface is not allowed');
}

protected function pStmt_Class(Stmt\Class_ $node): string
{
throw new SecurityException('Class is not allowed');
}

protected function pStmt_Trait(Stmt\Trait_ $node): string
{
throw new SecurityException('Trait is not allowed');
}

protected function pStmt_TraitUse(Stmt\TraitUse $node): string
{
throw new SecurityException('Trait use is not allowed');
}

protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node): string
{
throw new SecurityException('Trait use adaption is not allowed');
}

protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node): string
{
throw new SecurityException('Trait use adaption alias is not allowed');
}

protected function pStmt_Property(Stmt\Property $node): string
{
throw new SecurityException('Property is not allowed');
}

protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node): string
{
throw new SecurityException('Property property is not allowed');
}

protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string
{
throw new SecurityException('Class method is not allowed');
}

protected function pStmt_ClassConst(Stmt\ClassConst $node): string
{
throw new SecurityException('Class const is not allowed');
}

protected function pStmt_Function(Stmt\Function_ $node): string
{
$this->securityManager->defineFunction((string)$node->name);

return parent::pStmt_Function($node);
}

protected function pConst(\PhpParser\Node\Const_ $node): string
{
$this->securityManager->checkDefineConstant();
return parent::pConst($node);
}

protected function pStmt_Declare(Stmt\Declare_ $node): string
{
throw new SecurityException('Declare is not allowed');
}

protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node): string
{
throw new SecurityException('Declare declare is not allowed');
}

protected function pStmt_Echo(Stmt\Echo_ $node): string
{
throw new SecurityException('Echo is not allowed');
}

protected function pStmt_Expression(Stmt\Expression $node): string
{
$expression = $this->p($node->expr);

if (\preg_match('/print\W+/i', $expression)) {
throw new SecurityException('Print is not allowed');
}

return $expression . ';';
}

protected function pStmt_Global(Stmt\Global_ $node): string
{
throw new SecurityException('Global is not allowed');
}

protected function pStmt_InlineHTML(Stmt\InlineHTML $node): string
{
throw new SecurityException('Inline HTML is not allowed');
}

protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node): string
{
throw new SecurityException('Halt compiler is not allowed');
}

protected function pClassCommon(Stmt\Class_ $node, $afterClassToken): string
{
throw new SecurityException('Class is not allowed');
}

protected function pStmt_Namespace(Stmt\Namespace_ $node): string
{
$this->securityManager->setCurrentNamespace($node->name !== null ? (string)$node->name : null);

return parent::pStmt_Namespace( $node );
}

protected function pStmt_Use(Stmt\Use_ $node): string
{
foreach ($node->uses as $use) {
if ($node->type === Stmt\Use_::TYPE_NORMAL) {
$this->securityManager->addClassAlias((string)$use->name, $use->alias !== null ? (string)$use->alias : null);
}
elseif ($node->type === Stmt\Use_::TYPE_FUNCTION) {
$this->securityManager->addFunctionAlias((string)$use->name, (string)($use->alias ?? $use->name));
}
}

return parent::pStmt_Use($node);
}

protected function pStmt_GroupUse(Stmt\GroupUse $node): string
{
foreach ($node->uses as $use) {
if ($node->type === Stmt\Use_::TYPE_NORMAL) {
$this->securityManager->addClassAlias((string)$use->name, $use->alias !== null ? (string)$use->alias : null);
}
elseif ($node->type === Stmt\Use_::TYPE_FUNCTION) {
$this->securityManager->addFunctionAlias((string)$use->name, (string)($use->alias ?? $use->name));
}
}

return parent::pStmt_GroupUse($node);
}

}

0 comments on commit 07b13d7

Please sign in to comment.