MDL Shield

Navbar Plus

local_navbarplus

Print Report
Plugin Information

Local plugin that adds admin-configured Font Awesome icon links (with optional language, new-window, CSS-class and ID options) and an optional "reset user tour" icon to the Moodle header navbar via the render_navbar_output callback.

Version:2026100501
Release:v5.3-r2
Reviewed for:5.3
Privacy API
Unit Tests
Behat Tests
Reviewed:2026-11-10
8 files·700 lines
Grade Justification

The plugin is small (one callback in lib.php, an admin settings page, a single upgrade step, a null privacy provider and Behat tests), and I found no security vulnerabilities.

  • The only input is two admin-only settings, gated by $hassiteconfig.
  • All rendered values go through html_writer, which escapes attributes with s(). The icon identifier must match a strict fa- pattern, and link URLs are limited to http, https, mailto or site-relative paths. That blocks javascript: and protocol-relative URLs, even though only an administrator can enter them.
  • The plugin has no endpoints, database writes, file handling or outbound HTTP. Its privacy declaration is accurate.

The only finding is an info-level note about markup: an invalid alt attribute on an anchor and a hand-written icon tag. It has no security or functional impact.

AI Summary

Navbar Plus (local_navbarplus) is a small plugin for Moodle 5.3. One render_navbar_output callback turns an admin-configured textarea into Font Awesome icon links in the header navbar. An optional "reset user tour" icon can also be added. Besides that callback, the plugin has a settings page, one upgrade step that rewrites the stored setting, a null privacy provider and Behat step definitions.

The security posture is clean. Both settings can only be changed by site administrators. Every value printed in the navbar goes through html_writer, so attributes are escaped. The icon identifier must match a fa- pattern. URLs are parsed with \core\url and only http, https, mailto or site-relative paths are accepted, so javascript:, data: and protocol-relative URLs are rejected. There are no endpoints, form handlers, database queries, file operations or outbound requests.

The only finding is an info-level note on the reset-tour link: it sets an invalid alt attribute on an anchor and builds its icon as raw HTML. Neither affects security or behaviour, and no action is needed.

Findings

best practiceInfo
Reset-tour link uses an invalid alt attribute and a hand-built icon tag

The reset-user-tour link sets an alt attribute on an <a> element, which is not a valid attribute for anchors. Browsers and assistive technology ignore it, so it does nothing. The <i> icon is a hard-coded HTML string. Unlike the configured icons a few lines above, it has no aria-hidden and does not go through html_writer. The link still gets its accessible name from title, so this is only a markup and consistency issue. The function also declares global $OUTPUT; but never uses it.

Risk Assessment

No risk. All strings are static or come from language packs, and nothing in them is user-controlled. This is a markup and accessibility tidy-up only.

Context

local_navbarplus_render_navbar_output() runs on every page render. When the resetusertours setting is on, a logged-in non-guest user is on the page and a user tour matches it, the function appends a link with the id resetpagetour, which core's tool_usertours JavaScript listens for.

Identified Code
$itemicon = '<i class="icon fa fa-map fa-fw"></i>';
Suggested Fix
$itemicon = html_writer::tag('i', '', ['class' => 'icon fa fa-map fa-fw', 'aria-hidden' => 'true']);
Identified Code
$attributes = ['alt' => $resetstring, 'title' => $resetstring . ' ' . $resethint,
                    'id' => 'resetpagetour', ];
Suggested Fix
$attributes = ['aria-label' => $resetstring, 'title' => $resetstring . ' ' . $resethint,
                    'id' => 'resetpagetour', ];
Identified Code
global $OUTPUT;
Suggested Fix

Remove the unused global.

Additional AI Notes

Admin-only input with careful URL handling. The inserticonswithlinks textarea is stored as PARAM_RAW, but only site administrators can edit it. On output, every value is escaped by html_writer::attribute() through s(). The URL scheme allow-list in lib.php also handles the case where javascript:1/alert(1) is parsed as a host and port by rejecting any URL that has a host but no scheme.

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