mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Merge pull request #6784 from weirdan/forbid-exit-and-die
This commit is contained in:
commit
7f14d09f30
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Psalm\Internal\Analyzer\Statements\Expression;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\Node\Expr\Exit_;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
||||
@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||
use Psalm\Internal\DataFlow\TaintSink;
|
||||
use Psalm\Issue\ForbiddenCode;
|
||||
use Psalm\Issue\ImpureFunctionCall;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Storage\FunctionLikeParameter;
|
||||
@ -21,11 +22,37 @@ class ExitAnalyzer
|
||||
{
|
||||
public static function analyze(
|
||||
StatementsAnalyzer $statements_analyzer,
|
||||
PhpParser\Node\Expr\Exit_ $stmt,
|
||||
Exit_ $stmt,
|
||||
Context $context
|
||||
) : bool {
|
||||
$expr_type = null;
|
||||
|
||||
$config = $statements_analyzer->getProjectAnalyzer()->getConfig();
|
||||
|
||||
$forbidden = null;
|
||||
|
||||
if (isset($config->forbidden_functions['exit'])
|
||||
&& $stmt->getAttribute('kind') === Exit_::KIND_EXIT
|
||||
) {
|
||||
$forbidden = 'exit';
|
||||
} elseif (isset($config->forbidden_functions['die'])
|
||||
&& $stmt->getAttribute('kind') === Exit_::KIND_DIE
|
||||
) {
|
||||
$forbidden = 'die';
|
||||
}
|
||||
|
||||
if ($forbidden) {
|
||||
if (IssueBuffer::accepts(
|
||||
new ForbiddenCode(
|
||||
'You have forbidden the use of ' . $forbidden,
|
||||
new CodeLocation($statements_analyzer, $stmt)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
if ($stmt->expr) {
|
||||
$context->inside_call = true;
|
||||
|
||||
@ -91,7 +118,7 @@ class ExitAnalyzer
|
||||
&& !$context->collect_initializations
|
||||
) {
|
||||
if ($context->mutation_free || $context->external_mutation_free) {
|
||||
$function_name = $stmt->getAttribute('kind') === PhpParser\Node\Expr\Exit_::KIND_DIE ? 'die' : 'exit';
|
||||
$function_name = $stmt->getAttribute('kind') === Exit_::KIND_DIE ? 'die' : 'exit';
|
||||
|
||||
if (IssueBuffer::accepts(
|
||||
new ImpureFunctionCall(
|
||||
|
@ -929,202 +929,6 @@ class ConfigTest extends \Psalm\Tests\TestCase
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testAllowedEchoFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEchoFunctionViaFunctions(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="echo" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEchoFunctionViaFlag(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenEcho');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm forbidEcho="true"></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testAllowedPrintFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
print "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenPrintFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="print" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
print "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testAllowedVarExportFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
$a = [1, 2, 3];
|
||||
var_export($a);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenVarExportFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="var_export" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
$a = [1, 2, 3];
|
||||
var_export($a);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEmptyFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="empty" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
empty(false);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testValidThrowInvalidCatch(): void
|
||||
{
|
||||
$this->expectExceptionMessage('InvalidCatch');
|
||||
|
@ -1,6 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Tests;
|
||||
|
||||
use Psalm\Config;
|
||||
use Psalm\Context;
|
||||
use Psalm\Tests\Internal\Provider;
|
||||
|
||||
use function dirname;
|
||||
use function getcwd;
|
||||
|
||||
class ForbiddenCodeTest extends TestCase
|
||||
{
|
||||
use Traits\InvalidCodeAnalysisTestTrait;
|
||||
@ -62,4 +70,272 @@ class ForbiddenCodeTest extends TestCase
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testAllowedEchoFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEchoFunctionViaFunctions(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="echo" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEchoFunctionViaFlag(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenEcho');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm forbidEcho="true"></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testAllowedPrintFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
print "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenPrintFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="print" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
print "hello";'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testAllowedVarExportFunction(): void
|
||||
{
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm></psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
$a = [1, 2, 3];
|
||||
var_export($a);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenVarExportFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="var_export" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
$a = [1, 2, 3];
|
||||
var_export($a);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenEmptyFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="empty" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
empty(false);'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenExitFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="exit" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
exit(2);
|
||||
'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
public function testForbiddenDieFunction(): void
|
||||
{
|
||||
$this->expectExceptionMessage('ForbiddenCode');
|
||||
$this->expectException(\Psalm\Exception\CodeException::class);
|
||||
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__, 2),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<forbiddenFunctions>
|
||||
<function name="die" />
|
||||
</forbiddenFunctions>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
die(2);
|
||||
'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
|
||||
private function getProjectAnalyzerWithConfig(Config $config): \Psalm\Internal\Analyzer\ProjectAnalyzer
|
||||
{
|
||||
$p = new \Psalm\Internal\Analyzer\ProjectAnalyzer(
|
||||
$config,
|
||||
new \Psalm\Internal\Provider\Providers(
|
||||
$this->file_provider,
|
||||
new Provider\FakeParserCacheProvider()
|
||||
)
|
||||
);
|
||||
|
||||
$p->setPhpVersion('7.4');
|
||||
|
||||
return $p;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user