MDL Shield

Update check

tool_updatecheck

Print Report
Plugin Information

Admin tool that reports available Moodle core and plugin updates through two Checks API status checks, an admin report page, its own NRPE-style CLI script, and optional update notification messages. It works independently of Moodle core's update notification settings.

Version:2026100500
Release:v5.3-r1
Reviewed for:5.3
Privacy API
Unit Tests
Behat Tests
Reviewed:2026-10-25
29 files·5,092 lines
Grade Justification

No security vulnerabilities or code-quality defects were found. Every entry point is restricted to site administrators:

  • The report page uses admin_externalpage_setup() with moodle/site:config, and the state-changing fetch is protected by require_sesskey().
  • The checks can only be reached from the admin-only status report, the require_admin()-gated check web service, or the CLI.
  • The only outbound request is made by core's update checker, which uses the curl wrapper with proxy support and explicit TLS peer and host verification to the hardcoded download.moodle.org endpoint.

Output escaping is applied consistently: s() in check details, Mustache auto-escaping on the report, and plain-text notification messages. There are no custom tables, no raw SQL, no file handling and no bundled libraries. The test-only response override only runs on PHPUnit or Behat sites.

The only finding is an info-level design observation. The plugin deliberately keeps contacting the Moodle update server even when $CFG->disableupdatenotifications is set in config.php. It documents this, and it has no security impact.

AI Summary

tool_updatecheck is a small admin tool (about 1,500 lines of production PHP plus a large PHPUnit/Behat suite). It reads the update information cached by core's update checker, rates it with its own settings, and exposes the result in three places: two Checks API status checks, an admin report page, and a dedicated CLI script for monitoring systems. It can also send update notifications to selected users who hold moodle/site:config.

The security posture is very good. Every web entry point is admin-only, and the one state-changing action (the "Check for available updates now" button) requires a sesskey. All network traffic goes through core's own update checker, which pins TLS verification and uses the site proxy. Update data from the remote server is escaped with s() or Mustache before it is rendered. The plugin extends core's update checker in a careful way, overriding only protected methods and reusing core's response comparison.

One observation is worth reading before deployment. The plugin fetches update information from download.moodle.org once a day even when $CFG->disableupdatenotifications is set in config.php. That request sends the installed plugin list and, where available, the site identifier and country code. This is deliberate and documented, but sites that set that flag to stop outbound calls should know about it.

Findings

best practiceInfo
Update server is contacted even when disableupdatenotifications is set

The plugin's scheduled task fetch_updates and its adhoc task fetch_updates_adhoc call updateinfo::fetch() unconditionally. Core's own check_for_updates_task checks $CFG->disableupdatenotifications first and does nothing when it is set. config-dist.php describes that flag as the way to completely disable the available update notifications feature, and some administrators set it to stop the site contacting download.moodle.org. After this plugin is installed, the site POSTs its Moodle version, its list of installed add-on plugins and, if present, the hashed registration secret and country code to the update server every day, whatever that flag says. The behaviour is intentional and is described in the settings and README, but there is no way to opt out short of disabling the scheduled task.

Risk Assessment

Informational. The traffic goes over verified TLS to Moodle HQ and contains no user data. The concern is that an operator's explicit opt-out is overridden without any notice on the site itself. This only matters for sites that use the flag for data minimisation or egress control.

Context

updateinfo::fetch() creates the plugin's checker subclass, which calls core's checker::fetch() and get_response(). Those POST the parameters built by prepare_request_params() to https://download.moodle.org/api/1.3/updates.php with TLS verification on. Only $CFG->alternativeupdateproviderurl changes the destination.

Identified Code
mtrace('Fetching info about available updates ... ', '');
updateinfo::fetch(true);
mtrace('SUCCESS');
Suggested Fix

Consider making the override explicit, for example with a plugin setting such as ignorecoredisable that defaults to on, or at least show a notice on the report and settings page when $CFG->disableupdatenotifications is set. That way the administrator knowingly accepts the daily outbound request.

Identified Code
updateinfo::fetch(true);
Additional AI Notes

Privacy provider. The plugin implements null_provider but stores user IDs in the notificationsrecipients config setting, using admin_setting_users_with_capability just as core's notifyloginfailures does. It also sends messages through core's message API, where core_message owns the stored data. This matches how core treats similar settings, so it is not reported as a defect. The privacy:metadata string could still mention that recipients are selected by user ID.

Test seam in production code. checker::get_response() returns the testfakeresponse config value instead of making the real request, but only when PHPUNIT_TEST is true or BEHAT_SITE_RUNNING is defined. Core always defines PHPUNIT_TEST (false by default) in lib/setup.php, and BEHAT_SITE_RUNNING is only defined on a Behat test site. On a production site the override is never evaluated.

CLI script. cli/checks.php is adapted from core's admin/cli/checks.php and keeps two unused variables from the original ($len and $messages). This has no functional impact.

This review was generated by an AI system and may contain inaccuracies. Findings should be verified by a human reviewer before acting on them.