diff options
author | Joas Schilling <nickvergessen@gmx.de> | 2015-07-20 08:39:53 +0200 |
---|---|---|
committer | Joas Schilling <nickvergessen@gmx.de> | 2015-07-20 08:39:53 +0200 |
commit | 0dd1a785fe56177b13885e94cf49031493195f8a (patch) | |
tree | b9f40bde5ba4bdbf8bd3484f01745e3052778a10 /lib | |
parent | 83a13c0635fa5c8ca25a23d3996b62c5f2d82a2b (diff) | |
parent | 3566dcf246d122bbc4010aeb42c5b2d9259f8ea5 (diff) | |
download | nextcloud-server-0dd1a785fe56177b13885e94cf49031493195f8a.tar.gz nextcloud-server-0dd1a785fe56177b13885e94cf49031493195f8a.zip |
Merge pull request #16935 from owncloud/allow-app-check-code-for-deprecated-classes
Allow app check code for deprecated classes
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/app/codechecker/abstractcheck.php | 139 | ||||
-rw-r--r-- | lib/private/app/codechecker/codechecker.php (renamed from lib/private/app/codechecker.php) | 42 | ||||
-rw-r--r-- | lib/private/app/codechecker/deprecationcheck.php | 147 | ||||
-rw-r--r-- | lib/private/app/codechecker/emptycheck.php | 67 | ||||
-rw-r--r-- | lib/private/app/codechecker/icheck.php | 55 | ||||
-rw-r--r-- | lib/private/app/codechecker/nodevisitor.php | 306 | ||||
-rw-r--r-- | lib/private/app/codechecker/privatecheck.php | 86 | ||||
-rw-r--r-- | lib/private/app/codechecker/strongcomparisoncheck.php | 80 | ||||
-rw-r--r-- | lib/private/app/codecheckvisitor.php | 133 |
9 files changed, 889 insertions, 166 deletions
diff --git a/lib/private/app/codechecker/abstractcheck.php b/lib/private/app/codechecker/abstractcheck.php new file mode 100644 index 00000000000..c1c6524e42f --- /dev/null +++ b/lib/private/app/codechecker/abstractcheck.php @@ -0,0 +1,139 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +abstract class AbstractCheck implements ICheck { + /** @var ICheck */ + protected $check; + + /** + * @param ICheck $check + */ + public function __construct(ICheck $check) { + $this->check = $check; + } + + /** + * @param int $errorCode + * @param string $errorObject + * @return string + */ + public function getDescription($errorCode, $errorObject) { + switch ($errorCode) { + case CodeChecker::STATIC_CALL_NOT_ALLOWED: + $functions = $this->getLocalFunctions(); + $functions = array_change_key_case($functions, CASE_LOWER); + if (isset($functions[$errorObject])) { + return $this->getLocalDescription(); + } + // no break; + case CodeChecker::CLASS_EXTENDS_NOT_ALLOWED: + case CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED: + case CodeChecker::CLASS_NEW_NOT_ALLOWED: + case CodeChecker::CLASS_USE_NOT_ALLOWED: + $classes = $this->getLocalClasses(); + $classes = array_change_key_case($classes, CASE_LOWER); + if (isset($classes[$errorObject])) { + return $this->getLocalDescription(); + } + break; + + case CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED: + $constants = $this->getLocalConstants(); + $constants = array_change_key_case($constants, CASE_LOWER); + if (isset($constants[$errorObject])) { + return $this->getLocalDescription(); + } + break; + + case CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED: + $methods = $this->getLocalMethods(); + $methods = array_change_key_case($methods, CASE_LOWER); + if (isset($methods[$errorObject])) { + return $this->getLocalDescription(); + } + break; + } + + return $this->check->getDescription($errorCode, $errorObject); + } + + /** + * @return string + */ + abstract protected function getLocalDescription(); + + /** + * @return array + */ + abstract protected function getLocalClasses(); + + /** + * @return array + */ + abstract protected function getLocalConstants(); + + /** + * @return array + */ + abstract protected function getLocalFunctions(); + + /** + * @return array + */ + abstract protected function getLocalMethods(); + + /** + * @return array E.g.: `'ClassName' => 'oc version',` + */ + public function getClasses() { + return array_merge($this->getLocalClasses(), $this->check->getClasses()); + } + + /** + * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` + */ + public function getConstants() { + return array_merge($this->getLocalConstants(), $this->check->getConstants()); + } + + /** + * @return array E.g.: `'functionName' => 'oc version',` + */ + public function getFunctions() { + return array_merge($this->getLocalFunctions(), $this->check->getFunctions()); + } + + /** + * @return array E.g.: `'ClassName::methodName' => 'oc version',` + */ + public function getMethods() { + return array_merge($this->getLocalMethods(), $this->check->getMethods()); + } + + /** + * @return bool + */ + public function checkStrongComparisons() { + return $this->check->checkStrongComparisons(); + } +} diff --git a/lib/private/app/codechecker.php b/lib/private/app/codechecker/codechecker.php index 3f6cd19d6b4..ef7dc7f3e4d 100644 --- a/lib/private/app/codechecker.php +++ b/lib/private/app/codechecker/codechecker.php @@ -21,14 +21,13 @@ * */ -namespace OC\App; +namespace OC\App\CodeChecker; use OC\Hooks\BasicEmitter; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\Node\Name; use PhpParser\NodeTraverser; -use PhpParser\NodeVisitorAbstract; use PhpParser\Parser; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; @@ -42,43 +41,20 @@ class CodeChecker extends BasicEmitter { const CLASS_IMPLEMENTS_NOT_ALLOWED = 1001; const STATIC_CALL_NOT_ALLOWED = 1002; const CLASS_CONST_FETCH_NOT_ALLOWED = 1003; - const CLASS_NEW_FETCH_NOT_ALLOWED = 1004; + const CLASS_NEW_NOT_ALLOWED = 1004; const OP_OPERATOR_USAGE_DISCOURAGED = 1005; + const CLASS_USE_NOT_ALLOWED = 1006; + const CLASS_METHOD_CALL_NOT_ALLOWED = 1007; /** @var Parser */ private $parser; - /** @var string[] */ - private $blackListedClassNames; + /** @var ICheck */ + protected $checkList; - public function __construct() { + public function __construct(ICheck $checkList) { + $this->checkList = $checkList; $this->parser = new Parser(new Lexer); - $this->blackListedClassNames = [ - // classes replaced by the public api - 'OC_API', - 'OC_App', - 'OC_AppConfig', - 'OC_Avatar', - 'OC_BackgroundJob', - 'OC_Config', - 'OC_DB', - 'OC_Files', - 'OC_Helper', - 'OC_Hook', - 'OC_Image', - 'OC_JSON', - 'OC_L10N', - 'OC_Log', - 'OC_Mail', - 'OC_Preferences', - 'OC_Search_Provider', - 'OC_Search_Result', - 'OC_Request', - 'OC_Response', - 'OC_Template', - 'OC_User', - 'OC_Util', - ]; } /** @@ -138,7 +114,7 @@ class CodeChecker extends BasicEmitter { $code = file_get_contents($file); $statements = $this->parser->parse($code); - $visitor = new CodeCheckVisitor($this->blackListedClassNames); + $visitor = new NodeVisitor($this->checkList); $traverser = new NodeTraverser; $traverser->addVisitor($visitor); diff --git a/lib/private/app/codechecker/deprecationcheck.php b/lib/private/app/codechecker/deprecationcheck.php new file mode 100644 index 00000000000..3b6dc968bb5 --- /dev/null +++ b/lib/private/app/codechecker/deprecationcheck.php @@ -0,0 +1,147 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +class DeprecationCheck extends AbstractCheck implements ICheck { + /** + * @return string + */ + protected function getLocalDescription() { + return 'deprecated'; + } + + /** + * @return array E.g.: `'ClassName' => 'oc version',` + */ + protected function getLocalClasses() { + return [ + 'OCP\Config' => '8.0.0', + 'OCP\Contacts' => '8.1.0', + 'OCP\DB' => '8.1.0', + 'OCP\IHelper' => '8.1.0', + 'OCP\JSON' => '8.1.0', + 'OCP\Response' => '8.1.0', + 'OCP\AppFramework\IApi' => '8.0.0', + ]; + } + + /** + * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` + */ + protected function getLocalConstants() { + return [ + 'OCP::PERMISSION_CREATE' => '8.0.0', + 'OCP::PERMISSION_READ' => '8.0.0', + 'OCP::PERMISSION_UPDATE' => '8.0.0', + 'OCP::PERMISSION_DELETE' => '8.0.0', + 'OCP::PERMISSION_SHARE' => '8.0.0', + 'OCP::PERMISSION_ALL' => '8.0.0', + 'OCP::FILENAME_INVALID_CHARS' => '8.0.0', + ]; + } + + /** + * @return array E.g.: `'functionName' => 'oc version',` + */ + protected function getLocalFunctions() { + return [ + 'OCP::image_path' => '8.0.0', + 'OCP::mimetype_icon' => '8.0.0', + 'OCP::preview_icon' => '8.0.0', + 'OCP::publicPreview_icon' => '8.0.0', + 'OCP::human_file_size' => '8.0.0', + 'OCP::relative_modified_date' => '8.0.0', + 'OCP::simple_file_size' => '8.0.0', + 'OCP::html_select_options' => '8.0.0', + ]; + } + + /** + * @return array E.g.: `'ClassName::methodName' => 'oc version',` + */ + protected function getLocalMethods() { + return [ + 'OCP\App::register' => '8.1.0', + 'OCP\App::addNavigationEntry' => '8.1.0', + 'OCP\App::setActiveNavigationEntry' => '8.1.0', + + 'OCP\AppFramework\Controller::params' => '7.0.0', + 'OCP\AppFramework\Controller::getParams' => '7.0.0', + 'OCP\AppFramework\Controller::method' => '7.0.0', + 'OCP\AppFramework\Controller::getUploadedFile' => '7.0.0', + 'OCP\AppFramework\Controller::env' => '7.0.0', + 'OCP\AppFramework\Controller::cookie' => '7.0.0', + 'OCP\AppFramework\Controller::render' => '7.0.0', + + 'OCP\AppFramework\IAppContainer::getCoreApi' => '8.0.0', + 'OCP\AppFramework\IAppContainer::isLoggedIn' => '8.0.0', + 'OCP\AppFramework\IAppContainer::isAdminUser' => '8.0.0', + 'OCP\AppFramework\IAppContainer::log' => '8.0.0', + + 'OCP\BackgroundJob::addQueuedTask' => '6.0.0', + 'OCP\BackgroundJob::addRegularTask' => '6.0.0', + 'OCP\BackgroundJob::allQueuedTasks' => '6.0.0', + 'OCP\BackgroundJob::allRegularTasks' => '6.0.0', + 'OCP\BackgroundJob::deleteQueuedTask' => '6.0.0', + 'OCP\BackgroundJob::findQueuedTask' => '6.0.0', + 'OCP\BackgroundJob::queuedTaskWhereAppIs' => '6.0.0', + 'OCP\BackgroundJob::registerJob' => '8.1.0', + + 'OCP\Files::tmpFile' => '8.1.0', + 'OCP\Files::tmpFolder' => '8.1.0', + + 'OCP\IAppConfig::getValue' => '8.0.0', + 'OCP\IAppConfig::deleteKey' => '8.0.0', + 'OCP\IAppConfig::getKeys' => '8.0.0', + 'OCP\IAppConfig::setValue' => '8.0.0', + 'OCP\IAppConfig::deleteApp' => '8.0.0', + + 'OCP\ISearch::search' => '8.0.0', + + 'OCP\IServerContainer::getDb' => '8.1.0', + 'OCP\IServerContainer::getHTTPHelper' => '8.1.0', + + 'OCP\User::getUser' => '8.0.0', + 'OCP\User::getUsers' => '8.1.0', + 'OCP\User::getDisplayName' => '8.1.0', + 'OCP\User::getDisplayNames' => '8.1.0', + 'OCP\User::userExists' => '8.1.0', + 'OCP\User::logout' => '8.1.0', + 'OCP\User::checkPassword' => '8.1.0', + + 'OCP\Util::sendMail' => '8.1.0', + 'OCP\Util::formatDate' => '8.0.0', + 'OCP\Util::encryptedFiles' => '8.1.0', + 'OCP\Util::linkToRoute' => '8.1.0', + 'OCP\Util::linkTo' => '8.1.0', + 'OCP\Util::getServerHost' => '8.1.0', + 'OCP\Util::getServerProtocol' => '8.1.0', + 'OCP\Util::getRequestUri' => '8.1.0', + 'OCP\Util::getScriptName' => '8.1.0', + 'OCP\Util::imagePath' => '8.1.0', + 'OCP\Util::isValidFileName' => '8.1.0', + 'OCP\Util::generateRandomBytes' => '8.1.0', + 'OCP\Util::mb_str_replace' => '8.2.0', + 'OCP\Util::mb_substr_replace' => '8.2.0', + ]; + } +} diff --git a/lib/private/app/codechecker/emptycheck.php b/lib/private/app/codechecker/emptycheck.php new file mode 100644 index 00000000000..0e5df55d090 --- /dev/null +++ b/lib/private/app/codechecker/emptycheck.php @@ -0,0 +1,67 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OC\App\CodeChecker; + +class EmptyCheck implements ICheck { + /** + * @param int $errorCode + * @param string $errorObject + * @return string + */ + public function getDescription($errorCode, $errorObject) { + return ''; + } + + /** + * @return array E.g.: `'ClassName' => 'oc version',` + */ + public function getClasses() { + return []; + } + + /** + * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` + */ + public function getConstants() { + return []; + } + + /** + * @return array E.g.: `'functionName' => 'oc version',` + */ + public function getFunctions() { + return []; + } + + /** + * @return array E.g.: `'ClassName::methodName' => 'oc version',` + */ + public function getMethods() { + return []; + } + + /** + * @return bool + */ + public function checkStrongComparisons() { + return false; + } +} diff --git a/lib/private/app/codechecker/icheck.php b/lib/private/app/codechecker/icheck.php new file mode 100644 index 00000000000..a00e0d8fa13 --- /dev/null +++ b/lib/private/app/codechecker/icheck.php @@ -0,0 +1,55 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OC\App\CodeChecker; + +interface ICheck { + /** + * @param int $errorCode + * @param string $errorObject + * @return string + */ + public function getDescription($errorCode, $errorObject); + + /** + * @return array E.g.: `'ClassName' => 'oc version',` + */ + public function getClasses(); + + /** + * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` + */ + public function getConstants(); + + /** + * @return array E.g.: `'functionName' => 'oc version',` + */ + public function getFunctions(); + + /** + * @return array E.g.: `'ClassName::methodName' => 'oc version',` + */ + public function getMethods(); + + /** + * @return bool + */ + public function checkStrongComparisons(); +} diff --git a/lib/private/app/codechecker/nodevisitor.php b/lib/private/app/codechecker/nodevisitor.php new file mode 100644 index 00000000000..a22f852f36a --- /dev/null +++ b/lib/private/app/codechecker/nodevisitor.php @@ -0,0 +1,306 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +use PhpParser\Node; +use PhpParser\Node\Name; +use PhpParser\NodeVisitorAbstract; + +class NodeVisitor extends NodeVisitorAbstract { + /** @var ICheck */ + protected $list; + + /** @var string */ + protected $blackListDescription; + /** @var string[] */ + protected $blackListedClassNames; + /** @var string[] */ + protected $blackListedConstants; + /** @var string[] */ + protected $blackListedFunctions; + /** @var string[] */ + protected $blackListedMethods; + /** @var bool */ + protected $checkEqualOperatorUsage; + /** @var string[] */ + protected $errorMessages; + + /** + * @param ICheck $list + */ + public function __construct(ICheck $list) { + $this->list = $list; + + $this->blackListedClassNames = []; + foreach ($list->getClasses() as $class => $blackListInfo) { + if (is_numeric($class) && is_string($blackListInfo)) { + $class = $blackListInfo; + $blackListInfo = null; + } + + $class = strtolower($class); + $this->blackListedClassNames[$class] = $class; + } + + $this->blackListedConstants = []; + foreach ($list->getConstants() as $constantName => $blackListInfo) { + $constantName = strtolower($constantName); + $this->blackListedConstants[$constantName] = $constantName; + } + + $this->blackListedFunctions = []; + foreach ($list->getFunctions() as $functionName => $blackListInfo) { + $functionName = strtolower($functionName); + $this->blackListedFunctions[$functionName] = $functionName; + } + + $this->blackListedMethods = []; + foreach ($list->getMethods() as $functionName => $blackListInfo) { + $functionName = strtolower($functionName); + $this->blackListedMethods[$functionName] = $functionName; + } + + $this->checkEqualOperatorUsage = $list->checkStrongComparisons(); + + $this->errorMessages = [ + CodeChecker::CLASS_EXTENDS_NOT_ALLOWED => "%s class must not be extended", + CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED => "%s interface must not be implemented", + CodeChecker::STATIC_CALL_NOT_ALLOWED => "Static method of %s class must not be called", + CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED => "Constant of %s class must not not be fetched", + CodeChecker::CLASS_NEW_NOT_ALLOWED => "%s class must not be instantiated", + CodeChecker::CLASS_USE_NOT_ALLOWED => "%s class must not be imported with a use statement", + CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED => "Method of %s class must not be called", + + CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED => "is discouraged", + ]; + } + + /** @var array */ + public $errors = []; + + public function enterNode(Node $node) { + if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\Equal) { + $this->errors[]= [ + 'disallowedToken' => '==', + 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, + 'line' => $node->getLine(), + 'reason' => $this->buildReason('==', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) + ]; + } + if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\NotEqual) { + $this->errors[]= [ + 'disallowedToken' => '!=', + 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, + 'line' => $node->getLine(), + 'reason' => $this->buildReason('!=', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) + ]; + } + if ($node instanceof Node\Stmt\Class_) { + if (!is_null($node->extends)) { + $this->checkBlackList($node->extends->toString(), CodeChecker::CLASS_EXTENDS_NOT_ALLOWED, $node); + } + foreach ($node->implements as $implements) { + $this->checkBlackList($implements->toString(), CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED, $node); + } + } + if ($node instanceof Node\Expr\StaticCall) { + if (!is_null($node->class)) { + if ($node->class instanceof Name) { + $this->checkBlackList($node->class->toString(), CodeChecker::STATIC_CALL_NOT_ALLOWED, $node); + + $this->checkBlackListFunction($node->class->toString(), $node->name, $node); + $this->checkBlackListMethod($node->class->toString(), $node->name, $node); + } + + if ($node->class instanceof Node\Expr\Variable) { + /** + * TODO: find a way to detect something like this: + * $c = "OC_API"; + * $n = $c::call(); + */ + // $this->checkBlackListMethod($node->class->..., $node->name, $node); + } + } + } + if ($node instanceof Node\Expr\MethodCall) { + if (!is_null($node->var)) { + if ($node->var instanceof Node\Expr\Variable) { + /** + * TODO: find a way to detect something like this: + * $c = new OC_API(); + * $n = $c::call(); + * $n = $c->call(); + */ + // $this->checkBlackListMethod($node->var->..., $node->name, $node); + } + } + } + if ($node instanceof Node\Expr\ClassConstFetch) { + if (!is_null($node->class)) { + if ($node->class instanceof Name) { + $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, $node); + } + if ($node->class instanceof Node\Expr\Variable) { + /** + * TODO: find a way to detect something like this: + * $c = "OC_API"; + * $n = $i::ADMIN_AUTH; + */ + } + + $this->checkBlackListConstant($node->class->toString(), $node->name, $node); + } + } + if ($node instanceof Node\Expr\New_) { + if (!is_null($node->class)) { + if ($node->class instanceof Name) { + $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_NEW_NOT_ALLOWED, $node); + } + if ($node->class instanceof Node\Expr\Variable) { + /** + * TODO: find a way to detect something like this: + * $c = "OC_API"; + * $n = new $i; + */ + } + } + } + if ($node instanceof Node\Stmt\UseUse) { + $this->checkBlackList($node->name->toString(), CodeChecker::CLASS_USE_NOT_ALLOWED, $node); + if ($node->alias) { + $this->addUseNameToBlackList($node->name->toString(), $node->alias); + } else { + $this->addUseNameToBlackList($node->name->toString(), $node->name->getLast()); + } + } + } + + /** + * Check whether an alias was introduced for a namespace of a blacklisted class + * + * Example: + * - Blacklist entry: OCP\AppFramework\IApi + * - Name: OCP\AppFramework + * - Alias: OAF + * => new blacklist entry: OAF\IApi + * + * @param string $name + * @param string $alias + */ + private function addUseNameToBlackList($name, $alias) { + $name = strtolower($name); + $alias = strtolower($alias); + + foreach ($this->blackListedClassNames as $blackListedAlias => $blackListedClassName) { + if (strpos($blackListedClassName, $name . '\\') === 0) { + $aliasedClassName = str_replace($name, $alias, $blackListedClassName); + $this->blackListedClassNames[$aliasedClassName] = $blackListedClassName; + } + } + + foreach ($this->blackListedConstants as $blackListedAlias => $blackListedConstant) { + if (strpos($blackListedConstant, $name . '\\') === 0 || strpos($blackListedConstant, $name . '::') === 0) { + $aliasedConstantName = str_replace($name, $alias, $blackListedConstant); + $this->blackListedConstants[$aliasedConstantName] = $blackListedConstant; + } + } + + foreach ($this->blackListedFunctions as $blackListedAlias => $blackListedFunction) { + if (strpos($blackListedFunction, $name . '\\') === 0 || strpos($blackListedFunction, $name . '::') === 0) { + $aliasedFunctionName = str_replace($name, $alias, $blackListedFunction); + $this->blackListedFunctions[$aliasedFunctionName] = $blackListedFunction; + } + } + + foreach ($this->blackListedMethods as $blackListedAlias => $blackListedMethod) { + if (strpos($blackListedMethod, $name . '\\') === 0 || strpos($blackListedMethod, $name . '::') === 0) { + $aliasedMethodName = str_replace($name, $alias, $blackListedMethod); + $this->blackListedMethods[$aliasedMethodName] = $blackListedMethod; + } + } + } + + private function checkBlackList($name, $errorCode, Node $node) { + $lowerName = strtolower($name); + + if (isset($this->blackListedClassNames[$lowerName])) { + $this->errors[]= [ + 'disallowedToken' => $name, + 'errorCode' => $errorCode, + 'line' => $node->getLine(), + 'reason' => $this->buildReason($this->blackListedClassNames[$lowerName], $errorCode) + ]; + } + } + + private function checkBlackListConstant($class, $constantName, Node $node) { + $name = $class . '::' . $constantName; + $lowerName = strtolower($name); + + if (isset($this->blackListedConstants[$lowerName])) { + $this->errors[]= [ + 'disallowedToken' => $name, + 'errorCode' => CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, + 'line' => $node->getLine(), + 'reason' => $this->buildReason($this->blackListedConstants[$lowerName], CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED) + ]; + } + } + + private function checkBlackListFunction($class, $functionName, Node $node) { + $name = $class . '::' . $functionName; + $lowerName = strtolower($name); + + if (isset($this->blackListedFunctions[$lowerName])) { + $this->errors[]= [ + 'disallowedToken' => $name, + 'errorCode' => CodeChecker::STATIC_CALL_NOT_ALLOWED, + 'line' => $node->getLine(), + 'reason' => $this->buildReason($this->blackListedFunctions[$lowerName], CodeChecker::STATIC_CALL_NOT_ALLOWED) + ]; + } + } + + private function checkBlackListMethod($class, $functionName, Node $node) { + $name = $class . '::' . $functionName; + $lowerName = strtolower($name); + + if (isset($this->blackListedMethods[$lowerName])) { + $this->errors[]= [ + 'disallowedToken' => $name, + 'errorCode' => CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED, + 'line' => $node->getLine(), + 'reason' => $this->buildReason($this->blackListedMethods[$lowerName], CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED) + ]; + } + } + + private function buildReason($name, $errorCode) { + if (isset($this->errorMessages[$errorCode])) { + $desc = $this->list->getDescription($errorCode, $name); + return sprintf($this->errorMessages[$errorCode], $desc); + } + + return "$name usage not allowed - error: $errorCode"; + } +} diff --git a/lib/private/app/codechecker/privatecheck.php b/lib/private/app/codechecker/privatecheck.php new file mode 100644 index 00000000000..d6f4eb06981 --- /dev/null +++ b/lib/private/app/codechecker/privatecheck.php @@ -0,0 +1,86 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +class PrivateCheck extends AbstractCheck implements ICheck { + /** + * @return string + */ + protected function getLocalDescription() { + return 'private'; + } + + /** + * @return array + */ + public function getLocalClasses() { + return [ + // classes replaced by the public api + 'OC_API' => '6.0.0', + 'OC_App' => '6.0.0', + 'OC_AppConfig' => '6.0.0', + 'OC_Avatar' => '6.0.0', + 'OC_BackgroundJob' => '6.0.0', + 'OC_Config' => '6.0.0', + 'OC_DB' => '6.0.0', + 'OC_Files' => '6.0.0', + 'OC_Helper' => '6.0.0', + 'OC_Hook' => '6.0.0', + 'OC_Image' => '6.0.0', + 'OC_JSON' => '6.0.0', + 'OC_L10N' => '6.0.0', + 'OC_Log' => '6.0.0', + 'OC_Mail' => '6.0.0', + 'OC_Preferences' => '6.0.0', + 'OC_Search_Provider' => '6.0.0', + 'OC_Search_Result' => '6.0.0', + 'OC_Request' => '6.0.0', + 'OC_Response' => '6.0.0', + 'OC_Template' => '6.0.0', + 'OC_User' => '6.0.0', + 'OC_Util' => '6.0.0', + ]; + } + + /** + * @return array + */ + public function getLocalConstants() { + return []; + } + + /** + * @return array + */ + public function getLocalFunctions() { + return []; + } + + /** + * @return array + */ + public function getLocalMethods() { + return []; + } +} diff --git a/lib/private/app/codechecker/strongcomparisoncheck.php b/lib/private/app/codechecker/strongcomparisoncheck.php new file mode 100644 index 00000000000..7de0fe3e5c3 --- /dev/null +++ b/lib/private/app/codechecker/strongcomparisoncheck.php @@ -0,0 +1,80 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\App\CodeChecker; + +class StrongComparisonCheck implements ICheck { + /** @var ICheck */ + protected $check; + + /** + * @param ICheck $check + */ + public function __construct(ICheck $check) { + $this->check = $check; + } + + /** + * @param int $errorCode + * @param string $errorObject + * @return string + */ + public function getDescription($errorCode, $errorObject) { + return $this->check->getDescription($errorCode, $errorObject); + } + + /** + * @return array + */ + public function getClasses() { + return $this->check->getClasses(); + } + + /** + * @return array + */ + public function getConstants() { + return $this->check->getConstants(); + } + + /** + * @return array + */ + public function getFunctions() { + return $this->check->getFunctions(); + } + + /** + * @return array + */ + public function getMethods() { + return $this->check->getMethods(); + } + + /** + * @return bool + */ + public function checkStrongComparisons() { + return true; + } +} diff --git a/lib/private/app/codecheckvisitor.php b/lib/private/app/codecheckvisitor.php deleted file mode 100644 index e983bd8630b..00000000000 --- a/lib/private/app/codecheckvisitor.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php -/** - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\App; - -use PhpParser\Node; -use PhpParser\Node\Name; -use PhpParser\NodeVisitorAbstract; - -class CodeCheckVisitor extends NodeVisitorAbstract { - - public function __construct($blackListedClassNames) { - $this->blackListedClassNames = array_map('strtolower', $blackListedClassNames); - } - - public $errors = []; - - public function enterNode(Node $node) { - if ($node instanceof Node\Expr\BinaryOp\Equal) { - $this->errors[]= [ - 'disallowedToken' => '==', - 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, - 'line' => $node->getLine(), - 'reason' => $this->buildReason('==', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) - ]; - } - if ($node instanceof Node\Expr\BinaryOp\NotEqual) { - $this->errors[]= [ - 'disallowedToken' => '!=', - 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, - 'line' => $node->getLine(), - 'reason' => $this->buildReason('!=', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) - ]; - } - if ($node instanceof Node\Stmt\Class_) { - if (!is_null($node->extends)) { - $this->checkBlackList($node->extends->toString(), CodeChecker::CLASS_EXTENDS_NOT_ALLOWED, $node); - } - foreach ($node->implements as $implements) { - $this->checkBlackList($implements->toString(), CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED, $node); - } - } - if ($node instanceof Node\Expr\StaticCall) { - if (!is_null($node->class)) { - if ($node->class instanceof Name) { - $this->checkBlackList($node->class->toString(), CodeChecker::STATIC_CALL_NOT_ALLOWED, $node); - } - if ($node->class instanceof Node\Expr\Variable) { - /** - * TODO: find a way to detect something like this: - * $c = "OC_API"; - * $n = $i::call(); - */ - } - } - } - if ($node instanceof Node\Expr\ClassConstFetch) { - if (!is_null($node->class)) { - if ($node->class instanceof Name) { - $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, $node); - } - if ($node->class instanceof Node\Expr\Variable) { - /** - * TODO: find a way to detect something like this: - * $c = "OC_API"; - * $n = $i::ADMIN_AUTH; - */ - } - } - } - if ($node instanceof Node\Expr\New_) { - if (!is_null($node->class)) { - if ($node->class instanceof Name) { - $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_NEW_FETCH_NOT_ALLOWED, $node); - } - if ($node->class instanceof Node\Expr\Variable) { - /** - * TODO: find a way to detect something like this: - * $c = "OC_API"; - * $n = new $i; - */ - } - } - } - } - - private function checkBlackList($name, $errorCode, Node $node) { - if (in_array(strtolower($name), $this->blackListedClassNames)) { - $this->errors[]= [ - 'disallowedToken' => $name, - 'errorCode' => $errorCode, - 'line' => $node->getLine(), - 'reason' => $this->buildReason($name, $errorCode) - ]; - } - } - - private function buildReason($name, $errorCode) { - static $errorMessages= [ - CodeChecker::CLASS_EXTENDS_NOT_ALLOWED => "used as base class", - CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED => "used as interface", - CodeChecker::STATIC_CALL_NOT_ALLOWED => "static method call on private class", - CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED => "used to fetch a const from", - CodeChecker::CLASS_NEW_FETCH_NOT_ALLOWED => "is instanciated", - CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED => "is discouraged" - ]; - - if (isset($errorMessages[$errorCode])) { - return $errorMessages[$errorCode]; - } - - return "$name usage not allowed - error: $errorCode"; - } -} |