diff options
-rw-r--r-- | build/.phan/plugin-checker.php | 22 | ||||
-rw-r--r-- | build/.phan/plugins/SqlInjectionCheckerPlugin.php | 10 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/Log.php | 98 | ||||
-rw-r--r-- | lib/private/Log/ExceptionSerializer.php | 140 | ||||
-rw-r--r-- | lib/private/legacy/app.php | 5 | ||||
-rw-r--r-- | tests/data/app/description-multi-lang.xml | 8 | ||||
-rw-r--r-- | tests/data/app/description-single-lang.xml | 7 | ||||
-rw-r--r-- | tests/lib/AppTest.php | 14 |
10 files changed, 196 insertions, 110 deletions
diff --git a/build/.phan/plugin-checker.php b/build/.phan/plugin-checker.php index 92eb3496ed5..f7946fc2a42 100644 --- a/build/.phan/plugin-checker.php +++ b/build/.phan/plugin-checker.php @@ -20,17 +20,17 @@ */ $expected = <<<EOT -build/.phan/tests/SqlInjectionCheckerTest.php:23 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:35 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:37 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:39 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:41 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:43 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:54 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:61 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:62 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:69 SqlInjectionChecker Potential SQL injection detected -build/.phan/tests/SqlInjectionCheckerTest.php:70 SqlInjectionChecker Potential SQL injection detected +build/.phan/tests/SqlInjectionCheckerTest.php:23 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:35 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:37 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:39 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:41 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:43 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:54 SqlInjectionChecker Potential SQL injection detected - neither a parameter nor a string +build/.phan/tests/SqlInjectionCheckerTest.php:61 SqlInjectionChecker Potential SQL injection detected - method: no child method +build/.phan/tests/SqlInjectionCheckerTest.php:62 SqlInjectionChecker Potential SQL injection detected - method: no child method +build/.phan/tests/SqlInjectionCheckerTest.php:69 SqlInjectionChecker Potential SQL injection detected - method: no child method +build/.phan/tests/SqlInjectionCheckerTest.php:70 SqlInjectionChecker Potential SQL injection detected - method: no child method EOT; diff --git a/build/.phan/plugins/SqlInjectionCheckerPlugin.php b/build/.phan/plugins/SqlInjectionCheckerPlugin.php index 8cfd5ac4752..a9a0b817d5c 100644 --- a/build/.phan/plugins/SqlInjectionCheckerPlugin.php +++ b/build/.phan/plugins/SqlInjectionCheckerPlugin.php @@ -33,10 +33,10 @@ class SqlInjectionCheckerPlugin extends PluginV2 implements AnalyzeNodeCapabili class SqlInjectionCheckerVisitor extends PluginAwareAnalysisVisitor { - private function throwError() { + private function throwError(string $hint) { $this->emit( 'SqlInjectionChecker', - 'Potential SQL injection detected', + 'Potential SQL injection detected - ' . $hint, [], \Phan\Issue::SEVERITY_CRITICAL ); @@ -64,6 +64,8 @@ class SqlInjectionCheckerVisitor extends PluginAwareAnalysisVisitor { 'createNamedParameter', 'createPositionalParameter', 'createParameter', + 'createFunction', + 'func', ]; $functionsToSearch = [ @@ -84,7 +86,7 @@ class SqlInjectionCheckerVisitor extends PluginAwareAnalysisVisitor { // For set actions if(isset($node->children['method']) && in_array($node->children['method'], $functionsToSearch, true) && !is_string($subChild)) { if(!isset($subChild->children['method']) || !in_array($subChild->children['method'], $safeFunctions, true)) { - $this->throwError(); + $this->throwError('method: ' . ($subChild->children['method'] ?? 'no child method')); } } @@ -115,7 +117,7 @@ class SqlInjectionCheckerVisitor extends PluginAwareAnalysisVisitor { // If it is an IParameter or a pure string no error is thrown if((string)$expandedNode !== '\OCP\DB\QueryBuilder\IParameter' && !is_string($secondParameterNode)) { - $this->throwError(); + $this->throwError('neither a parameter nor a string'); } } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 58f4a15dbd6..99023096bcb 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -740,6 +740,7 @@ return array( 'OC\\Log' => $baseDir . '/lib/private/Log.php', 'OC\\Log\\ErrorHandler' => $baseDir . '/lib/private/Log/ErrorHandler.php', 'OC\\Log\\Errorlog' => $baseDir . '/lib/private/Log/Errorlog.php', + 'OC\\Log\\ExceptionSerializer' => $baseDir . '/lib/private/Log/ExceptionSerializer.php', 'OC\\Log\\File' => $baseDir . '/lib/private/Log/File.php', 'OC\\Log\\Rotate' => $baseDir . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => $baseDir . '/lib/private/Log/Syslog.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index b0c04eea220..33533569e90 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -770,6 +770,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Log' => __DIR__ . '/../../..' . '/lib/private/Log.php', 'OC\\Log\\ErrorHandler' => __DIR__ . '/../../..' . '/lib/private/Log/ErrorHandler.php', 'OC\\Log\\Errorlog' => __DIR__ . '/../../..' . '/lib/private/Log/Errorlog.php', + 'OC\\Log\\ExceptionSerializer' => __DIR__ . '/../../..' . '/lib/private/Log/ExceptionSerializer.php', 'OC\\Log\\File' => __DIR__ . '/../../..' . '/lib/private/Log/File.php', 'OC\\Log\\Rotate' => __DIR__ . '/../../..' . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => __DIR__ . '/../../..' . '/lib/private/Log/Syslog.php', diff --git a/lib/private/Log.php b/lib/private/Log.php index 85e439359fa..e1f9eb213be 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -37,6 +37,7 @@ namespace OC; use InterfaSys\LogNormalizer\Normalizer; +use OC\Log\ExceptionSerializer; use OC\Log\File; use OCP\ILogger; use OCP\Support\CrashReport\IRegistry; @@ -68,50 +69,6 @@ class Log implements ILogger { /** @var IRegistry */ private $crashReporters; - protected $methodsWithSensitiveParameters = [ - // Session/User - 'completeLogin', - 'login', - 'checkPassword', - 'checkPasswordNoLogging', - 'loginWithPassword', - 'updatePrivateKeyPassword', - 'validateUserPass', - 'loginWithToken', - '{closure}', - - // TokenProvider - 'getToken', - 'isTokenPassword', - 'getPassword', - 'decryptPassword', - 'logClientIn', - 'generateToken', - 'validateToken', - - // TwoFactorAuth - 'solveChallenge', - 'verifyChallenge', - - // ICrypto - 'calculateHMAC', - 'encrypt', - 'decrypt', - - // LoginController - 'tryLogin', - 'confirmPassword', - - // LDAP - 'bind', - 'areCredentialsValid', - 'invokeLDAPMethod', - - // Encryption - 'storeKeyPair', - 'setupUser', - ]; - /** * @param string $logger The logger that should be used * @param SystemConfig $config the system config object @@ -324,56 +281,6 @@ class Log implements ILogger { return min($this->config->getValue('loglevel', Util::WARN), Util::FATAL); } - private function filterTrace(array $trace) { - $sensitiveValues = []; - $trace = array_map(function (array $traceLine) use (&$sensitiveValues) { - foreach ($this->methodsWithSensitiveParameters as $sensitiveMethod) { - if (strpos($traceLine['function'], $sensitiveMethod) !== false) { - $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']); - $traceLine['args'] = ['*** sensitive parameters replaced ***']; - return $traceLine; - } - } - return $traceLine; - }, $trace); - return array_map(function (array $traceLine) use ($sensitiveValues) { - $traceLine['args'] = $this->removeValuesFromArgs($traceLine['args'], $sensitiveValues); - return $traceLine; - }, $trace); - } - - private function removeValuesFromArgs($args, $values) { - foreach($args as &$arg) { - if (in_array($arg, $values, true)) { - $arg = '*** sensitive parameter replaced ***'; - } else if (is_array($arg)) { - $arg = $this->removeValuesFromArgs($arg, $values); - } - } - return $args; - } - - private function serializeException(\Throwable $exception) { - $data = [ - 'Exception' => get_class($exception), - 'Message' => $exception->getMessage(), - 'Code' => $exception->getCode(), - 'Trace' => $this->filterTrace($exception->getTrace()), - 'File' => $exception->getFile(), - 'Line' => $exception->getLine(), - ]; - - if ($exception instanceof HintException) { - $data['Hint'] = $exception->getHint(); - } - - if ($exception->getPrevious()) { - $data['Previous'] = $this->serializeException($exception->getPrevious()); - } - - return $data; - } - /** * Logs an exception very detailed * @@ -386,7 +293,8 @@ class Log implements ILogger { $app = $context['app'] ?? 'no app in context'; $level = $context['level'] ?? Util::ERROR; - $data = $this->serializeException($exception); + $serializer = new ExceptionSerializer(); + $data = $serializer->serializeException($exception); $data['CustomMessage'] = $context['message'] ?? '--'; $minLevel = $this->getLogLevel($context); diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php new file mode 100644 index 00000000000..be025cb9e52 --- /dev/null +++ b/lib/private/Log/ExceptionSerializer.php @@ -0,0 +1,140 @@ +<?php +/** + * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Log; + +use OC\HintException; + +class ExceptionSerializer { + const methodsWithSensitiveParameters = [ + // Session/User + 'completeLogin', + 'login', + 'checkPassword', + 'checkPasswordNoLogging', + 'loginWithPassword', + 'updatePrivateKeyPassword', + 'validateUserPass', + 'loginWithToken', + '{closure}', + + // TokenProvider + 'getToken', + 'isTokenPassword', + 'getPassword', + 'decryptPassword', + 'logClientIn', + 'generateToken', + 'validateToken', + + // TwoFactorAuth + 'solveChallenge', + 'verifyChallenge', + + // ICrypto + 'calculateHMAC', + 'encrypt', + 'decrypt', + + // LoginController + 'tryLogin', + 'confirmPassword', + + // LDAP + 'bind', + 'areCredentialsValid', + 'invokeLDAPMethod', + + // Encryption + 'storeKeyPair', + 'setupUser', + ]; + + private function filterTrace(array $trace) { + $sensitiveValues = []; + $trace = array_map(function (array $traceLine) use (&$sensitiveValues) { + foreach (self::methodsWithSensitiveParameters as $sensitiveMethod) { + if (strpos($traceLine['function'], $sensitiveMethod) !== false) { + $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']); + $traceLine['args'] = ['*** sensitive parameters replaced ***']; + return $traceLine; + } + } + return $traceLine; + }, $trace); + return array_map(function (array $traceLine) use ($sensitiveValues) { + $traceLine['args'] = $this->removeValuesFromArgs($traceLine['args'], $sensitiveValues); + return $traceLine; + }, $trace); + } + + private function removeValuesFromArgs($args, $values) { + foreach ($args as &$arg) { + if (in_array($arg, $values, true)) { + $arg = '*** sensitive parameter replaced ***'; + } else if (is_array($arg)) { + $arg = $this->removeValuesFromArgs($arg, $values); + } + } + return $args; + } + + private function encodeTrace($trace) { + $filteredTrace = $this->filterTrace($trace); + return array_map(function (array $line) { + $line['args'] = array_map([$this, 'encodeArg'], $line['args']); + return $line; + }, $filteredTrace); + } + + private function encodeArg($arg) { + if (is_object($arg)) { + $data = get_object_vars($arg); + $data['__class__'] = get_class($arg); + return array_map([$this, 'encodeArg'], $data); + } else if (is_array($arg)) { + return array_map([$this, 'encodeArg'], $arg); + } else { + return $arg; + } + } + + public function serializeException(\Throwable $exception) { + $data = [ + 'Exception' => get_class($exception), + 'Message' => $exception->getMessage(), + 'Code' => $exception->getCode(), + 'Trace' => $this->encodeTrace($exception->getTrace()), + 'File' => $exception->getFile(), + 'Line' => $exception->getLine(), + ]; + + if ($exception instanceof HintException) { + $data['Hint'] = $exception->getHint(); + } + + if ($exception->getPrevious()) { + $data['Previous'] = $this->serializeException($exception->getPrevious()); + } + + return $data; + } +}
\ No newline at end of file diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index cabeed1d62d..88d5ce1f135 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -1004,6 +1004,11 @@ class OC_App { } protected static function findBestL10NOption(array $options, string $lang): string { + // only a single option + if (isset($options['@value'])) { + return $options['@value']; + } + $fallback = $similarLangFallback = $englishFallback = false; $lang = strtolower($lang); diff --git a/tests/data/app/description-multi-lang.xml b/tests/data/app/description-multi-lang.xml new file mode 100644 index 00000000000..e7eee3bcb8b --- /dev/null +++ b/tests/data/app/description-multi-lang.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<info> + <id>files_encryption</id> + <name>Server-side Encryption</name> + <description lang="en">English</description> + <description lang="de">German</description> + <licence>AGPL</licence> +</info> diff --git a/tests/data/app/description-single-lang.xml b/tests/data/app/description-single-lang.xml new file mode 100644 index 00000000000..5fb1ba07e8e --- /dev/null +++ b/tests/data/app/description-single-lang.xml @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<info> + <id>files_encryption</id> + <name>Server-side Encryption</name> + <description lang="en">English</description> + <licence>AGPL</licence> +</info> diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php index 04729303847..1334aa62f13 100644 --- a/tests/lib/AppTest.php +++ b/tests/lib/AppTest.php @@ -9,6 +9,7 @@ namespace Test; +use OC\App\InfoParser; use OC\AppConfig; use OCP\IAppConfig; @@ -592,5 +593,18 @@ class AppTest extends \Test\TestCase { public function testParseAppInfo(array $data, array $expected) { $this->assertSame($expected, \OC_App::parseAppInfo($data)); } + + public function testParseAppInfoL10N() { + $parser = new InfoParser(); + $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-multi-lang.xml"); + $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']); + $this->assertEquals('German', \OC_App::parseAppInfo($data, 'de')['description']); + } + + public function testParseAppInfoL10NSingleLanguage() { + $parser = new InfoParser(); + $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-single-lang.xml"); + $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']); + } } |