fix: move debug mode to config (#3324)

* fix: move debug mode to config

* fix: also move debug_whitelist to .ini config

* fix: move logic back to Debug class

* docs

* docs

* fix: disable debug mode by default

* fix: restore previous behavior for alerts

* fix: center-align alert text
This commit is contained in:
Dag 2023-06-02 20:22:09 +02:00 committed by GitHub
parent c5cd229445
commit ee498eadf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 121 additions and 158 deletions

View file

@ -183,9 +183,9 @@ Add the bridge name to `whitelist.txt`:
### How to enable debug mode ### How to enable debug mode
Create a file named `DEBUG`: Set in `config.ini.php`:
touch DEBUG enable_debug_mode = true
Learn more in [debug mode](https://rss-bridge.github.io/rss-bridge/For_Developers/Debug_mode.html). Learn more in [debug mode](https://rss-bridge.github.io/rss-bridge/For_Developers/Debug_mode.html).

View file

@ -107,7 +107,7 @@ class DisplayAction implements ActionInterface
&& (time() - $cache_timeout < $mtime) && (time() - $cache_timeout < $mtime)
&& !Debug::isEnabled() && !Debug::isEnabled()
) { ) {
// At this point we found the feed in the cache // At this point we found the feed in the cache and debug mode is disabled
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
// The client wants to know if the feed has changed since its last check // The client wants to know if the feed has changed since its last check
@ -118,7 +118,7 @@ class DisplayAction implements ActionInterface
} }
} }
// Fetch the cached feed from the cache and prepare it // Load the feed from cache and prepare it
$cached = $cache->loadData(); $cached = $cache->loadData();
if (isset($cached['items']) && isset($cached['extraInfos'])) { if (isset($cached['items']) && isset($cached['extraInfos'])) {
foreach ($cached['items'] as $item) { foreach ($cached['items'] as $item) {
@ -127,7 +127,7 @@ class DisplayAction implements ActionInterface
$infos = $cached['extraInfos']; $infos = $cached['extraInfos'];
} }
} else { } else {
// At this point we did NOT find the feed in the cache. So invoke the bridge! // At this point we did NOT find the feed in the cache or debug mode is enabled.
try { try {
$bridge->setDatas($bridge_params); $bridge->setDatas($bridge_params);
$bridge->collectData(); $bridge->collectData();
@ -200,7 +200,7 @@ class DisplayAction implements ActionInterface
$item->setURI(get_current_url()); $item->setURI(get_current_url());
$item->setTimestamp(time()); $item->setTimestamp(time());
// Create a item identifier for feed readers e.g. "staysafetv twitch videos_19389" // Create an item identifier for feed readers e.g. "staysafetv twitch videos_19389"
$item->setUid($bridge->getName() . '_' . $uniqueIdentifier); $item->setUid($bridge->getName() . '_' . $uniqueIdentifier);
$content = render_template(__DIR__ . '/../templates/bridge-error.html.php', [ $content = render_template(__DIR__ . '/../templates/bridge-error.html.php', [

View file

@ -24,6 +24,7 @@ final class FrontpageAction implements ActionInterface
} }
return render(__DIR__ . '/../templates/frontpage.html.php', [ return render(__DIR__ . '/../templates/frontpage.html.php', [
'messages' => [],
'admin_email' => Configuration::getConfig('admin', 'email'), 'admin_email' => Configuration::getConfig('admin', 'email'),
'admin_telegram' => Configuration::getConfig('admin', 'telegram'), 'admin_telegram' => Configuration::getConfig('admin', 'telegram'),
'bridges' => $body, 'bridges' => $body,

View file

@ -43,10 +43,6 @@ class SchweinfurtBuergerinformationenBridge extends BridgeAbstract
foreach ($articleIDs as $articleID) { foreach ($articleIDs as $articleID) {
$this->items[] = $this->generateItemFromArticle($articleID); $this->items[] = $this->generateItemFromArticle($articleID);
if (Debug::isEnabled()) {
break;
}
} }
} }

View file

@ -15,6 +15,13 @@ timezone = "UTC"
; Display a system message to users. ; Display a system message to users.
message = "" message = ""
; Whether to enable debug mode.
enable_debug_mode = false
; Enable debug mode only for these permitted ip addresses
; debug_mode_whitelist[] = 127.0.0.1
; debug_mode_whitelist[] = 192.168.1.10
[http] [http]
timeout = 60 timeout = 60
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0" useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0"

View file

@ -5,23 +5,29 @@ Enabling debug mode on a public server may result in malicious clients retrievin
*** ***
Debug mode enables error reporting and prevents loading data from the cache (data is still written to the cache). Debug mode enables error reporting and prevents loading data from the cache (data is still written to the cache).
To enable debug mode, create a file named 'DEBUG' in the root directory of RSS-Bridge (next to `index.php`). For further security, insert your IP address in the file. You can add multiple addresses, one per line. To enable debug mode, set in `config.ini.php`:
enable_debug_mode = true
Allow only explicit ip addresses:
debug_mode_whitelist[] = 127.0.0.1
debug_mode_whitelist[] = 192.168.1.10
_Notice_: _Notice_:
* An empty file enables debug mode for anyone! * An empty file enables debug mode for anyone!
* The bridge whitelist still applies! (debug mode does **not** enable all bridges) * The bridge whitelist still applies! (debug mode does **not** enable all bridges)
RSS-Bridge will give you a visual feedback when debug mode is enabled: RSS-Bridge will give you a visual feedback when debug mode is enabled.
![twitter bridge](../images/debug_mode.png)
While debug mode is active, RSS-Bridge will write additional data to your servers `error.log`. While debug mode is active, RSS-Bridge will write additional data to your servers `error.log`.
Debug mode is controlled by the static class `Debug`. It provides three core functions: Debug mode is controlled by the static class `Debug`. It provides three core functions:
`Debug::isEnabled()`: Returns `true` if debug mode is enabled. * `Debug::isEnabled()`: Returns `true` if debug mode is enabled.
`Debug::isSecure()`: Returns `true` if your client is on the debug whitelist. * `Debug::log($message)`: Adds a message to `error.log`. It takes one parameter, which can be anything.
`Debug::log($message)`: Adds a message to `error.log`. It takes one parameter, which can be anything. For example: `Debug::log('Hello World!');`
Example: `Debug::log('Hello World!');`
**Notice**: `Debug::log($message)` calls `Debug::isEnabled()` internally. You don't have to do that manually. **Notice**: `Debug::log($message)` calls `Debug::isEnabled()` internally. You don't have to do that manually.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View file

@ -114,6 +114,15 @@ final class Configuration
} }
} }
if (file_exists(__DIR__ . '/../DEBUG')) {
// The debug mode has been moved to config. Preserve existing installs which has this DEBUG file.
self::setConfig('system', 'enable_debug_mode', true);
$debug = trim(file_get_contents(__DIR__ . '/../DEBUG'));
if ($debug) {
self::setConfig('system', 'debug_mode_whitelist', explode("\n", str_replace("\r", '', $debug)));
}
}
if ( if (
!is_string(self::getConfig('system', 'timezone')) !is_string(self::getConfig('system', 'timezone'))
|| !in_array(self::getConfig('system', 'timezone'), timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)) || !in_array(self::getConfig('system', 'timezone'), timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))
@ -121,6 +130,13 @@ final class Configuration
self::throwConfigError('system', 'timezone'); self::throwConfigError('system', 'timezone');
} }
if (!is_bool(self::getConfig('system', 'enable_debug_mode'))) {
self::throwConfigError('system', 'enable_debug_mode', 'Is not a valid Boolean');
}
if (!is_array(self::getConfig('system', 'debug_mode_whitelist') ?: [])) {
self::throwConfigError('system', 'debug_mode_whitelist', 'Is not a valid array');
}
if (!is_string(self::getConfig('proxy', 'url'))) { if (!is_string(self::getConfig('proxy', 'url'))) {
self::throwConfigError('proxy', 'url', 'Is not a valid string'); self::throwConfigError('proxy', 'url', 'Is not a valid string');
} }

View file

@ -1,106 +1,23 @@
<?php <?php
/**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one.
*
* For the full license information, please view the UNLICENSE file distributed
* with this source code.
*
* @package Core
* @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge
*/
/**
* Implements functions for debugging purposes. Debugging can be enabled by
* placing a file named DEBUG in {@see PATH_ROOT}.
*
* The file specifies a whitelist of IP addresses on which debug mode will be
* enabled. An empty file enables debug mode for everyone (highly discouraged
* for public servers!). Each line in the file specifies one client in the
* whitelist. For example:
*
* * `192.168.1.72`
* * `127.0.0.1`
* * `::1`
*
* Notice: If you are running RSS-Bridge on your local machine, you need to add
* localhost (either `127.0.0.1` for IPv4 or `::1` for IPv6) to your whitelist!
*
* Warning: In debug mode your server may display sensitive information! For
* security reasons it is recommended to whitelist only specific IP addresses.
*/
class Debug class Debug
{ {
/** /**
* Indicates if debug mode is enabled. * Convenience function for Configuration::getConfig('system', 'enable_debug_mode')
*
* Do not access this property directly!
* Use {@see Debug::isEnabled()} instead.
*
* @var bool
*/ */
private static $enabled = false; public static function isEnabled(): bool
/**
* Indicates if debug mode is secure.
*
* Do not access this property directly!
* Use {@see Debug::isSecure()} instead.
*
* @var bool
*/
private static $secure = false;
/**
* Returns true if debug mode is enabled
*
* If debug mode is enabled, sets `display_errors = 1` and `error_reporting = E_ALL`
*
* @return bool True if enabled.
*/
public static function isEnabled()
{ {
static $firstCall = true; // Initialized on first call $ip = $_SERVER['REMOTE_ADDR'];
$enableDebugMode = Configuration::getConfig('system', 'enable_debug_mode');
if ($firstCall && file_exists(__DIR__ . '/../DEBUG')) { $debugModeWhitelist = Configuration::getConfig('system', 'debug_mode_whitelist') ?: [];
$debug_whitelist = trim(file_get_contents(__DIR__ . '/../DEBUG')); if ($enableDebugMode && ($debugModeWhitelist === [] || in_array($ip, $debugModeWhitelist))) {
return true;
self::$enabled = empty($debug_whitelist) || in_array(
$_SERVER['REMOTE_ADDR'],
explode("\n", str_replace("\r", '', $debug_whitelist))
);
if (self::$enabled) {
self::$secure = !empty($debug_whitelist);
}
$firstCall = false; // Skip check on next call
} }
return false;
return self::$enabled;
}
/**
* Returns true if debug mode is enabled only for specific IP addresses.
*
* Notice: The security flag is set by {@see Debug::isEnabled()}. If this
* function is called before {@see Debug::isEnabled()}, the default value is
* false!
*
* @return bool True if debug mode is secure
*/
public static function isSecure()
{
return self::$secure;
} }
public static function log($message) public static function log($message)
{ {
if (!self::isEnabled()) {
return;
}
$e = new \Exception(); $e = new \Exception();
$trace = trace_from_exception($e); $trace = trace_from_exception($e);
// Drop the current frame // Drop the current frame

View file

@ -113,9 +113,7 @@ abstract class FeedExpander extends BridgeAbstract
if ($rssContent === false) { if ($rssContent === false) {
$xmlErrors = libxml_get_errors(); $xmlErrors = libxml_get_errors();
foreach ($xmlErrors as $xmlError) { foreach ($xmlErrors as $xmlError) {
if (Debug::isEnabled()) { Logger::debug(trim($xmlError->message));
Debug::log(trim($xmlError->message));
}
} }
if ($xmlErrors) { if ($xmlErrors) {
// Render only the first error into exception message // Render only the first error into exception message

View file

@ -6,9 +6,7 @@ final class Logger
{ {
public static function debug(string $message, array $context = []) public static function debug(string $message, array $context = [])
{ {
if (Debug::isEnabled()) { self::log('DEBUG', $message, $context);
self::log('DEBUG', $message, $context);
}
} }
public static function info(string $message, array $context = []): void public static function info(string $message, array $context = []): void
@ -28,6 +26,11 @@ final class Logger
private static function log(string $level, string $message, array $context = []): void private static function log(string $level, string $message, array $context = []): void
{ {
if (!Debug::isEnabled() && $level === 'DEBUG') {
// Don't log this debug log record because debug mode is disabled
return;
}
if (isset($context['e'])) { if (isset($context['e'])) {
/** @var \Throwable $e */ /** @var \Throwable $e */
$e = $context['e']; $e = $context['e'];
@ -66,7 +69,13 @@ final class Logger
$message, $message,
$context ? Json::encode($context) : '' $context ? Json::encode($context) : ''
); );
// Log to stderr/stdout whatever that is // Log to stderr/stdout whatever that is
// todo: extract to log handler
error_log($text); error_log($text);
// Log to file
// todo: extract to log handler
//file_put_contents('/tmp/rss-bridge.log', $text, FILE_APPEND);
} }
} }

View file

@ -42,6 +42,7 @@ final class RssBridge
); );
Logger::warning($text); Logger::warning($text);
if (Debug::isEnabled()) { if (Debug::isEnabled()) {
// todo: extract to log handler
print sprintf("<pre>%s</pre>\n", e($text)); print sprintf("<pre>%s</pre>\n", e($text));
} }
}); });
@ -59,6 +60,7 @@ final class RssBridge
); );
Logger::error($message); Logger::error($message);
if (Debug::isEnabled()) { if (Debug::isEnabled()) {
// todo: extract to log handler
print sprintf("<pre>%s</pre>\n", e($message)); print sprintf("<pre>%s</pre>\n", e($message));
} }
} }

View file

@ -40,6 +40,7 @@ const WHITELIST_DEFAULT = __DIR__ . '/../whitelist.default.txt';
const REPOSITORY = 'https://github.com/RSS-Bridge/rss-bridge/'; const REPOSITORY = 'https://github.com/RSS-Bridge/rss-bridge/';
// Allow larger files for simple_html_dom // Allow larger files for simple_html_dom
// todo: extract to config (if possible)
const MAX_FILE_SIZE = 10000000; const MAX_FILE_SIZE = 10000000;
// Files // Files

View file

@ -438,14 +438,17 @@ function getSimpleHTMLDOMCached(
$time !== false $time !== false
&& (time() - $duration < $time) && (time() - $duration < $time)
&& !Debug::isEnabled() && !Debug::isEnabled()
) { // Contents within duration ) {
// Contents within duration and debug mode is disabled
$content = $cache->loadData(); $content = $cache->loadData();
} else { // Content not within duration } else {
// Contents not within duration, or debug mode is enabled
$content = getContents( $content = getContents(
$url, $url,
$header ?? [], $header ?? [],
$opts ?? [] $opts ?? []
); );
// todo: fix bad if statement
if ($content !== false) { if ($content !== false) {
$cache->saveData($content); $cache->saveData($content);
} }

View file

@ -1,23 +1,34 @@
<?php <?php
/** /**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and * Render template using base.html.php as base
* Atom feeds for websites that don't have one.
*
* For the full license information, please view the UNLICENSE file distributed
* with this source code.
*
* @package Core
* @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge
*/ */
function render(string $template, array $context = []): string function render(string $template, array $context = []): string
{ {
if ($template === 'base.html.php') { if ($template === 'base.html.php') {
throw new \Exception('Do not render base.html.php into itself'); throw new \Exception('Do not render base.html.php into itself');
} }
$context['system_message'] = Configuration::getConfig('system', 'message'); $context['messages'] = $context['messages'] ?? [];
if (Configuration::getConfig('system', 'message')) {
$context['messages'][] = [
'body' => Configuration::getConfig('system', 'message'),
'level' => 'info',
];
}
if (Debug::isEnabled()) {
$debugModeWhitelist = Configuration::getConfig('system', 'debug_mode_whitelist') ?: [];
if ($debugModeWhitelist === []) {
$context['messages'][] = [
'body' => 'Warning : Debug mode is active from any location, make sure only you can access RSS-Bridge.',
'level' => 'error'
];
} else {
$context['messages'][] = [
'body' => 'Warning : Debug mode is active from your IP address, your requests will bypass the cache.',
'level' => 'warning'
];
}
}
$context['page'] = render_template($template, $context); $context['page'] = render_template($template, $context);
return render_template('base.html.php', $context); return render_template('base.html.php', $context);
} }

View file

@ -71,15 +71,34 @@ header {
text-align: center; text-align: center;
} }
section.warning { .alert-info {
background-color: #ffc600; margin-bottom: 15px;
color: #5f5f5f; color: white;
font-weight: bold;
background-color: rgb(33, 150, 243);
padding: 15px;
border-radius: 4px;
text-align: center;
} }
section.critical-warning { .alert-warning {
background-color: #ffc600;
color: #5f5f5f;
margin-bottom: 15px;
font-weight: bold;
padding: 15px;
border-radius: 4px;
text-align: center;
}
.alert-error {
background-color: #cf3e3e; background-color: #cf3e3e;
font-weight: bold; font-weight: bold;
color: white; color: white;
margin-bottom: 15px;
padding: 15px;
border-radius: 4px;
text-align: center;
} }
select, select,
@ -342,15 +361,6 @@ button {
width: 200px; width: 200px;
} }
.alert {
margin-bottom: 15px;
color: white;
font-weight: bold;
background-color: rgb(33, 150, 243);
padding: 15px;
border-radius: 4px;
}
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
.container { .container {
width: 100%; width: 100%;

View file

@ -17,11 +17,11 @@
</a> </a>
</header> </header>
<?php if ($system_message): ?> <?php foreach ($messages as $message): ?>
<div class="alert"> <div class="alert-<?= raw($message['level'] ?? 'info') ?>">
<?= raw($system_message) ?> <?= raw($message['body']) ?>
</div> </div>
<?php endif; ?> <?php endforeach; ?>
<?= raw($page) ?> <?= raw($page) ?>
</div> </div>

View file

@ -4,20 +4,6 @@
document.addEventListener('DOMContentLoaded', rssbridge_list_search); document.addEventListener('DOMContentLoaded', rssbridge_list_search);
</script> </script>
<?php if (Debug::isEnabled()): ?>
<?php if (!Debug::isSecure()): ?>
<section class="critical-warning">
Warning : Debug mode is active from any location,
make sure only you can access RSS-Bridge.
</section>
<?php else: ?>
<section class="warning">
Warning : Debug mode is active from your IP address,
your requests will bypass the cache.
</section>
<?php endif; ?>
<?php endif; ?>
<section class="searchbar"> <section class="searchbar">
<h3>Search</h3> <h3>Search</h3>
<input <input