From a093165caf6e865b1706dfec103a2f05452084ff Mon Sep 17 00:00:00 2001 From: "vlad.kidanov" Date: Fri, 17 Jul 2026 15:36:10 +0100 Subject: [PATCH] Bug fixes and improvements WR492688: Issue #32 Fix: the 'static' flag can no longer be set from a raw client value. It is now only true if the request presents a 'statickey' matching an HMAC-SHA256 of the outage id keyed with a per-site secret (infopage::statickey(), lazily generated via set_config()/get_config()). maintenance_static_page::create_from_outage() now sends that computed statickey instead of static=1 when it internally fetches info.php, so the legitimate static-generation path keeps working while external forgery of the flag is no longer possible. WR492688: Issue #33 WR492688: Version bump Bug fixes and improvements WR492688: Issue #32 Fix: the 'static' flag can no longer be set from a raw client value. It is now only true if the request presents a 'statickey' matching an HMAC-SHA256 of the outage id keyed with a per-site secret (infopage::statickey(), lazily generated via set_config()/get_config()). maintenance_static_page::create_from_outage() now sends that computed statickey instead of static=1 when it internally fetches info.php, so the legitimate static-generation path keeps working while external forgery of the flag is no longer possible. WR492688: Issue #33 WR492688: Version bump fix --- classes/local/controllers/infopage.php | 25 +++++++++++++++++-- .../controllers/maintenance_static_page.php | 3 ++- preview.php | 4 +++ version.php | 4 +-- views/info/content.php | 10 +++++++- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/classes/local/controllers/infopage.php b/classes/local/controllers/infopage.php index acfe1266..8e646bc0 100644 --- a/classes/local/controllers/infopage.php +++ b/classes/local/controllers/infopage.php @@ -53,10 +53,14 @@ public function __construct(?array $params = null) { $CFG->svgicons = true; if (is_null($params)) { + $id = optional_param('id', null, PARAM_INT); $params = [ - 'id' => optional_param('id', null, PARAM_INT), + 'id' => $id, 'outage' => null, - 'static' => optional_param('static', false, PARAM_BOOL), + 'static' => !is_null($id) && hash_equals( + self::statickey($id), + optional_param('statickey', '', PARAM_ALPHANUM) + ), ]; } else { $defaults = [ @@ -152,4 +156,21 @@ private function set_parameters(array $params) { $this->outage = $params['outage']; $this->static = $params['static']; } + + /** + * Computes the secret token that proves a request to view an outage's static + * rendering came from this plugin's own static-page generator, not an external + * client forging the request. Used to gate the 'static' flag (see constructor). + * + * @param int $outageid + * @return string + */ + public static function statickey($outageid) { + $secret = get_config('auth_outage', 'staticsecret'); + if (empty($secret)) { + $secret = random_string(64); + set_config('staticsecret', $secret, 'auth_outage'); + } + return hash_hmac('sha256', (string)$outageid, $secret); + } } diff --git a/classes/local/controllers/maintenance_static_page.php b/classes/local/controllers/maintenance_static_page.php index 4a6896dc..644bdf17 100644 --- a/classes/local/controllers/maintenance_static_page.php +++ b/classes/local/controllers/maintenance_static_page.php @@ -56,7 +56,8 @@ public static function create_from_outage($outage) { header('X-Outage-EndTime: ' . $outage->stoptime); } $data = maintenance_static_page_io::file_get_data( - $CFG->wwwroot . '/auth/outage/info.php?auth_outage_hide_warning=1&static=1&id=' . $outage->id + $CFG->wwwroot . '/auth/outage/info.php?auth_outage_hide_warning=1&id=' . $outage->id + . '&statickey=' . infopage::statickey($outage->id) ); $html = $data['contents']; } diff --git a/preview.php b/preview.php index f2c2508c..9a1d5a13 100644 --- a/preview.php +++ b/preview.php @@ -30,7 +30,11 @@ // @codingStandardsIgnoreStart require_once(__DIR__.'/../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); // @codingStandardsIgnoreEnd + +admin_externalpage_setup('auth_outage_manage'); + $id = optional_param('id', null, PARAM_INT); $outage = is_null($id) ? outagedb::get_next_starting() : outagedb::get_by_id($id); if (is_null($outage)) { diff --git a/version.php b/version.php index 1f0950cb..bb2e002f 100644 --- a/version.php +++ b/version.php @@ -28,8 +28,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = "auth_outage"; -$plugin->version = 2026011306; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 2026011305; // Human-readable release information. +$plugin->version = 2026011307; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 2026011307; // Human-readable release information. $plugin->requires = 2025100600; // Moodle 5.1. $plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments! $plugin->supported = [501, 501]; // A range of branch numbers of supported moodle versions. diff --git a/views/info/content.php b/views/info/content.php index 0f44cce1..6dd6a37e 100644 --- a/views/info/content.php +++ b/views/info/content.php @@ -39,7 +39,15 @@ stoptime, get_string('datetimeformat', 'auth_outage')); ?> -
get_description(); ?>
+
+ get_description(), + FORMAT_HTML, + ['context' => context_system::instance()] + ); + ?> +