summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/app/codechecker.php130
-rw-r--r--lib/private/app/codecheckvisitor.php111
-rw-r--r--lib/private/defaults.php3
-rw-r--r--lib/private/installer.php60
-rw-r--r--lib/private/memcache/arraycache.php71
-rw-r--r--lib/private/memcache/factory.php2
-rw-r--r--lib/private/security/securerandom.php3
-rw-r--r--lib/private/user.php2
8 files changed, 326 insertions, 56 deletions
diff --git a/lib/private/app/codechecker.php b/lib/private/app/codechecker.php
new file mode 100644
index 00000000000..dbec53579a8
--- /dev/null
+++ b/lib/private/app/codechecker.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Copyright (c) 2015 Thomas Müller <deepdiver@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\App;
+
+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;
+use RecursiveIteratorIterator;
+use RegexIterator;
+use SplFileInfo;
+
+class CodeChecker extends BasicEmitter {
+
+ const CLASS_EXTENDS_NOT_ALLOWED = 1000;
+ 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;
+
+ /** @var Parser */
+ private $parser;
+
+ /** @var string[] */
+ private $blackListedClassNames;
+
+ public function __construct() {
+ $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_Request',
+ 'OC_Response',
+ 'OC_Template',
+ 'OC_User',
+ 'OC_Util',
+ ];
+ }
+
+ /**
+ * @param string $appId
+ * @return array
+ */
+ public function analyse($appId) {
+ $appPath = \OC_App::getAppPath($appId);
+ if ($appPath === false) {
+ throw new \RuntimeException("No app with given id <$appId> known.");
+ }
+
+ return $this->analyseFolder($appPath);
+ }
+
+ /**
+ * @param string $folder
+ * @return array
+ */
+ public function analyseFolder($folder) {
+ $errors = [];
+
+ $excludes = array_map(function($item) use ($folder) {
+ return $folder . '/' . $item;
+ }, ['vendor', '3rdparty', '.git', 'l10n']);
+
+ $iterator = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS);
+ $iterator = new RecursiveCallbackFilterIterator($iterator, function($item) use ($folder, $excludes){
+ /** @var SplFileInfo $item */
+ foreach($excludes as $exclude) {
+ if (substr($item->getPath(), 0, strlen($exclude)) === $exclude) {
+ return false;
+ }
+ }
+ return true;
+ });
+ $iterator = new RecursiveIteratorIterator($iterator);
+ $iterator = new RegexIterator($iterator, '/^.+\.php$/i');
+
+ foreach ($iterator as $file) {
+ /** @var SplFileInfo $file */
+ $this->emit('CodeChecker', 'analyseFileBegin', [$file->getPathname()]);
+ $errors = array_merge($this->analyseFile($file), $errors);
+ $this->emit('CodeChecker', 'analyseFileFinished', [$errors]);
+ }
+
+ return $errors;
+ }
+
+
+ /**
+ * @param string $file
+ * @return array
+ */
+ public function analyseFile($file) {
+ $code = file_get_contents($file);
+ $statements = $this->parser->parse($code);
+
+ $visitor = new CodeCheckVisitor($this->blackListedClassNames);
+ $traverser = new NodeTraverser;
+ $traverser->addVisitor($visitor);
+
+ $traverser->traverse($statements);
+
+ return $visitor->errors;
+ }
+}
diff --git a/lib/private/app/codecheckvisitor.php b/lib/private/app/codecheckvisitor.php
new file mode 100644
index 00000000000..939c905bcf6
--- /dev/null
+++ b/lib/private/app/codecheckvisitor.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Copyright (c) 2015 Thomas Müller <deepdiver@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\App;
+
+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;
+use RecursiveIteratorIterator;
+use RegexIterator;
+use SplFileInfo;
+
+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\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",
+ ];
+
+ if (isset($errorMessages[$errorCode])) {
+ return $errorMessages[$errorCode];
+ }
+
+ return "$name usage not allowed - error: $errorCode";
+ }
+}
diff --git a/lib/private/defaults.php b/lib/private/defaults.php
index dfcd97aedd6..a3902ee80de 100644
--- a/lib/private/defaults.php
+++ b/lib/private/defaults.php
@@ -215,7 +215,8 @@ class OC_Defaults {
if ($this->themeExist('getShortFooter')) {
$footer = $this->theme->getShortFooter();
} else {
- $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank">' .$this->getEntity() . '</a>'.
+ $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' .
+ ' rel="noreferrer">' .$this->getEntity() . '</a>'.
' – ' . $this->getSlogan();
}
diff --git a/lib/private/installer.php b/lib/private/installer.php
index db8f27aeeab..aeac3497fd7 100644
--- a/lib/private/installer.php
+++ b/lib/private/installer.php
@@ -308,7 +308,7 @@ class OC_Installer{
}
$info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true);
// check the code for not allowed calls
- if(!$isShipped && !OC_Installer::checkCode($info['id'], $extractDir)) {
+ if(!$isShipped && !OC_Installer::checkCode($extractDir)) {
OC_Helper::rmdirr($extractDir);
throw new \Exception($l->t("App can't be installed because of not allowed code in the App"));
}
@@ -511,7 +511,7 @@ class OC_Installer{
OC_Appconfig::setValue($app, 'ocsid', $info['ocsid']);
}
- //set remote/public handelers
+ //set remote/public handlers
foreach($info['remote'] as $name=>$path) {
OCP\CONFIG::setAppValue('core', 'remote_'.$name, $app.'/'.$path);
}
@@ -529,58 +529,16 @@ class OC_Installer{
* @param string $folder the folder of the app to check
* @return boolean true for app is o.k. and false for app is not o.k.
*/
- public static function checkCode($appname, $folder) {
- $blacklist=array(
- // 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_Request::',
- 'OC_Response::',
- 'OC_Template::',
- 'OC_User::',
- 'OC_Util::',
- );
+ public static function checkCode($folder) {
// is the code checker enabled?
- if(OC_Config::getValue('appcodechecker', false)) {
- // check if grep is installed
- $grep = \OC_Helper::findBinaryPath('grep');
- if (!$grep) {
- OC_Log::write('core',
- 'grep not installed. So checking the code of the app "'.$appname.'" was not possible',
- OC_Log::ERROR);
- return true;
- }
-
- // iterate the bad patterns
- foreach($blacklist as $bl) {
- $cmd = 'grep --include \\*.php -ri '.escapeshellarg($bl).' '.$folder.'';
- $result = exec($cmd);
- // bad pattern found
- if($result<>'') {
- OC_Log::write('core',
- 'App "'.$appname.'" is using a not allowed call "'.$bl.'". Installation refused.',
- OC_Log::ERROR);
- return false;
- }
- }
- return true;
-
- }else{
+ if(!OC_Config::getValue('appcodechecker', false)) {
return true;
}
+
+ $codeChecker = new \OC\App\CodeChecker();
+ $errors = $codeChecker->analyseFolder($folder);
+
+ return empty($errors);
}
}
diff --git a/lib/private/memcache/arraycache.php b/lib/private/memcache/arraycache.php
new file mode 100644
index 00000000000..9456c0f80c6
--- /dev/null
+++ b/lib/private/memcache/arraycache.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) 2015 Joas Schilling <nickvergessen@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Memcache;
+
+class ArrayCache extends Cache {
+ /** @var array Array with the cached data */
+ protected $cachedData = array();
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get($key) {
+ if ($this->hasKey($key)) {
+ return $this->cachedData[$key];
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function set($key, $value, $ttl = 0) {
+ $this->cachedData[$key] = $value;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function hasKey($key) {
+ return isset($this->cachedData[$key]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function remove($key) {
+ unset($this->cachedData[$key]);
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function clear($prefix = '') {
+ if ($prefix === '') {
+ $this->cachedData = [];
+ return true;
+ }
+
+ foreach ($this->cachedData as $key => $value) {
+ if (strpos($key, $prefix) === 0) {
+ $this->remove($key);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ static public function isAvailable() {
+ return true;
+ }
+}
diff --git a/lib/private/memcache/factory.php b/lib/private/memcache/factory.php
index 1e663eecfe1..e8a91c52269 100644
--- a/lib/private/memcache/factory.php
+++ b/lib/private/memcache/factory.php
@@ -42,7 +42,7 @@ class Factory implements ICacheFactory {
} elseif (Memcached::isAvailable()) {
return new Memcached($prefix);
} else {
- return new Null($prefix);
+ return new ArrayCache($prefix);
}
}
diff --git a/lib/private/security/securerandom.php b/lib/private/security/securerandom.php
index 2402e863fb0..b1169bff289 100644
--- a/lib/private/security/securerandom.php
+++ b/lib/private/security/securerandom.php
@@ -64,8 +64,7 @@ class SecureRandom implements ISecureRandom {
* Generate a random string of specified length.
* @param string $length The length of the generated string
* @param string $characters An optional list of characters to use if no characterlist is
- * specified 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./
- * is used.
+ * specified all valid base64 characters are used.
* @return string
* @throws \Exception If the generator is not initialized.
*/
diff --git a/lib/private/user.php b/lib/private/user.php
index d1fedffcaaf..10457c224f2 100644
--- a/lib/private/user.php
+++ b/lib/private/user.php
@@ -366,7 +366,7 @@ class OC_User {
return $backend->getLogoutAttribute();
}
- return 'href="' . link_to('', 'index.php') . '?logout=true&requesttoken=' . OC_Util::callRegister() . '"';
+ return 'href="' . link_to('', 'index.php') . '?logout=true&requesttoken=' . urlencode(OC_Util::callRegister()) . '"';
}
/**