Skip to content

Stored XSS via Title/Home Link

Moderate
nodiscc published GHSA-rvrg-mx59-mv8x Jul 31, 2026

Package

Shaarli

Affected versions

<=0.16.3

Patched versions

>=0.16.4

Description

Affected component: Shaarli 0.16.3, setting general.header_link (ConfigureController::save, application/front/controller/admin/ConfigureController.php:78), rendered as the site title link in tpl/default/page.header.html via PageBuilder::initialize (application/render/PageBuilder.php:138).

CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation).

Required privileges: Administrator.

Summary

The "Home link" configuration value (general.header_link) is rendered into the href of the Shaarli title/logo link that appears in the header of every page. The value is stored with HTML escaping but without any URL protocol filtering, so an administrator can store a javascript: URI. Any visitor who clicks the title link then executes attacker-controlled JavaScript.

Bookmark URLs, which are rendered in the same kind of href, are protocol-filtered through whitelist_protocols. The header link is not, so it is the one title-bar destination that still accepts a script URI.

Details

The value is stored with escape() only:

// application/front/controller/admin/ConfigureController.php
78:        $this->container->conf->set('general.header_link', escape($request->getParam('titleLink')));

escape() is htmlspecialchars(..., ENT_COMPAT), which encodes <, >, &, and " but leaves javascript:alert(document.domain) unchanged (it contains none of those characters). No whitelist_protocols is applied.

It is then assigned untouched and emitted directly into the href:

// application/render/PageBuilder.php
137:        if ($this->conf->exists('general.header_link')) {
138:            $this->tpl->assign('titleLink', $this->conf->get('general.header_link'));
<!-- tpl/default/page.header.html (lines 5 and 17) -->
<a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-mobile">
<a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-desktop">

PoC

  1. Sign in as the administrator and open the configuration page (Tools, then "Configure your Shaarli").
  2. In the "Home link" field, enter javascript:alert(document.domain) and save.
  3. Open any page of the instance.
  4. Click the Shaarli title or logo in the top-left. The browser runs the stored script in the Shaarli origin.

Impact

The payload is stored and rendered on every page, so it reaches every visitor of the instance, authenticated or not. A visitor who clicks the title link runs the script in the Shaarli origin.

Remediation

Apply the existing protocol filter to the header link, the same control already used for bookmark URLs:

// application/front/controller/admin/ConfigureController.php
$this->container->conf->set(
    'general.header_link',
    escape(whitelist_protocols($request->getParam('titleLink'), $this->container->conf->get('security.allowed_protocols', [])))
);

whitelist_protocols rewrites any non-allowed scheme (including javascript:) to http://, so the stored value can no longer be a script URI. Relative values such as the default / are preserved (they are returned unchanged by whitelist_protocols).

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
Required
Scope
Changed
Confidentiality
Low
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N

CVE ID

No known CVE

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

Credits