Merge pull request #9177 from nextcloud/log-exception-argument-classes
Log classnames of arguments in exception trace
This commit is contained in:
commit
991790686a
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue