mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-31 10:50:00 -04:00
ZmErrorHandler::redact() matched token=, auth=, pass= and password= only. A client that sends one of them as an array (token[]=x, which Cake logs as token%5B0%5D=x) still puts the value in the log, so the name may now carry [..] or %5B..%5D before the =. An empty value is no longer replaced: token= stays token=, so the log still shows a client that sent no credential. Tests: prefixes (authority, tokenize, password2, pass_hint, token_type, passwd) are not touched, arrays are redacted, empty stays empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 4789df0a0433d51e12e19cd21df5ac18e7c6305f)
85 lines
3.3 KiB
PHP
85 lines
3.3 KiB
PHP
<?php
|
|
// Tests what the API's error handler writes to cake_error.log:
|
|
// ZmErrorHandler::redact() - credential values in a URL or log text
|
|
// ZmErrorHandler::_getMessage() - the logged "Request URL:" must not carry
|
|
// token=, auth=, pass= or password= values
|
|
//
|
|
// Cake's ErrorHandler is stubbed, so this runs from a source checkout:
|
|
// php tests/php/test_api_error_log_redaction.php
|
|
|
|
class App {
|
|
public static function uses($class, $package) {}
|
|
}
|
|
|
|
// What Cake's ErrorHandler::_getMessage() builds: class, message, request URL.
|
|
class ErrorHandler {
|
|
public static $url = '';
|
|
protected static function _getMessage($exception) {
|
|
return '['.get_class($exception).'] '.$exception->getMessage()
|
|
."\nRequest URL: ".static::$url."\nStack Trace:\n#0 {main}";
|
|
}
|
|
}
|
|
|
|
require_once __DIR__.'/../../web/api/app/Lib/Error/ZmErrorHandler.php';
|
|
|
|
class TestErrorHandler extends ZmErrorHandler {
|
|
public static function message($exception) { return static::_getMessage($exception); }
|
|
}
|
|
|
|
$failures = 0;
|
|
$passes = 0;
|
|
|
|
function check($name, $got, $expected) {
|
|
global $failures, $passes;
|
|
if ($got === $expected) {
|
|
$passes++;
|
|
echo "ok - $name\n";
|
|
} else {
|
|
$failures++;
|
|
echo "not ok - $name (got ".var_export($got, true)." expected ".var_export($expected, true).")\n";
|
|
}
|
|
}
|
|
|
|
// ---- ZmErrorHandler::redact() ----
|
|
|
|
check('token value is redacted',
|
|
ZmErrorHandler::redact('/zm/api/monitors.json?token=eyJ0.eyJp.sig'),
|
|
'/zm/api/monitors.json?token=[redacted]');
|
|
check('user is kept, pass is redacted',
|
|
ZmErrorHandler::redact('/zm/api/host/login.json?user=admin&pass=s3cret&x=1'),
|
|
'/zm/api/host/login.json?user=admin&pass=[redacted]&x=1');
|
|
check('auth and password are redacted',
|
|
ZmErrorHandler::redact('/a?auth=abc123&password=pw'),
|
|
'/a?auth=[redacted]&password=[redacted]');
|
|
check('HTML-escaped separator is matched',
|
|
ZmErrorHandler::redact('/a?p=%2Fa&token=eyJ0.x.y'),
|
|
'/a?p=%2Fa&token=[redacted]');
|
|
check('names are matched whole, not as a suffix',
|
|
ZmErrorHandler::redact('/a?mytoken=1&passive=2&bypass=3'),
|
|
'/a?mytoken=1&passive=2&bypass=3');
|
|
check('names are matched whole, not as a prefix',
|
|
ZmErrorHandler::redact('/a?authority=1&tokenize=2&password2=3&pass_hint=4&token_type=5&passwd=6'),
|
|
'/a?authority=1&tokenize=2&password2=3&pass_hint=4&token_type=5&passwd=6');
|
|
check('a name sent as an array is redacted',
|
|
ZmErrorHandler::redact('/a?token[]=eyJ0.x.y&pass%5B0%5D=s3cret&x=1'),
|
|
'/a?token[]=[redacted]&pass%5B0%5D=[redacted]&x=1');
|
|
check('an empty value is left empty',
|
|
ZmErrorHandler::redact('/a?token=&user=u'),
|
|
'/a?token=&user=u');
|
|
check('text without credentials is unchanged',
|
|
ZmErrorHandler::redact('/zm/api/monitors/view/1.json?p=%2Fmonitors'),
|
|
'/zm/api/monitors/view/1.json?p=%2Fmonitors');
|
|
|
|
// ---- the logged message ----
|
|
|
|
ErrorHandler::$url = '/zm/api/monitors/view/9999.json?p=%2Fx&user=viewer&pass=s3cret&token=eyJ0.eyJp.sig';
|
|
$logged = TestErrorHandler::message(new Exception('Invalid monitor'));
|
|
check('logged message has no password', strpos($logged, 's3cret'), false);
|
|
check('logged message has no token', strpos($logged, 'eyJ0'), false);
|
|
check('logged message keeps the path and user',
|
|
strpos($logged, 'Request URL: /zm/api/monitors/view/9999.json?p=%2Fx&user=viewer&pass=[redacted]') !== false, true);
|
|
|
|
echo "\n$passes passed, $failures failed\n";
|
|
exit($failures ? 1 : 0);
|
|
?>
|