VYPR
Moderate severityNVD Advisory· Published Jun 12, 2025· Updated Jun 12, 2025

Citizen allows stored XSS in search no result messages

CVE-2025-49576

Description

Citizen is a MediaWiki skin that makes extensions part of the cohesive experience. The citizen-search-noresults-title and citizen-search-noresults-desc system messages are inserted into raw HTML, allowing anybody who can edit those messages to insert arbitrary HTML into the DOM. This vulnerability is fixed in 3.3.1.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2025-49576: XSS in Citizen MediaWiki skin via unsanitized system messages, fixed in 3.3.1.

Vulnerability

CVE-2025-49576 is a stored cross-site scripting (XSS) vulnerability in the Citizen skin for MediaWiki. The skin inserts the system messages citizen-search-noresults-title and citizen-search-noresults-desc directly into raw HTML without proper sanitization [1]. This allows any user with permission to edit these system messages (typically administrators or users with editinterface rights) to inject arbitrary HTML and JavaScript into the page [1].

Exploitation

An attacker with the ability to edit the skin's system messages can craft a payload that executes in the browsers of all users viewing search results with no matches. No additional user interaction is required beyond visiting a page that triggers the search no-results display. The attacker does not need to be authenticated beyond having the editinterface right [1].

Impact

Successful exploitation leads to full XSS, enabling an attacker to perform actions on behalf of the victim, steal session cookies, redirect users to malicious sites, or deface the wiki. Since the payload is stored in the skin's configuration, it affects all visitors until removed [1].

Mitigation

The vulnerability has been patched in version 3.3.1 of the Citizen skin [1]. The fix was implemented in two commits: one that migrated the search typeahead to Mustache templates ([2]) and another that specifically addresses stored XSS vulnerabilities across multiple skin components ([3]). Users should upgrade to Citizen 3.3.1 or later. No workarounds are documented; the only mitigation is to restrict the editinterface permission to trusted users.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
starcitizentools/citizen-skinPackagist
>= 2.31.0, < 3.3.13.3.1

Affected products

2
  • Citizen/Citizenllm-fuzzy
    Range: <=3.3.0
  • StarCitizenTools/mediawiki-skins-Citizenv5
    Range: >= a0296afaedbe1a277337a2d8f1da83cb3a79b9ab, < 93c36ac778397e0e7c46cf7adb1e5d848265f1bd

Patches

2
93c36ac77839

fix(security): 🐛 🔒️ fix various stored XSS system message vulnerabilities

7 files changed · +23 17
  • i18n/en.json+1 1 modified
    @@ -116,6 +116,6 @@
     	"citizen-command-palette-tip-commands": "Type <code>/</code> for all commands",
    
     	"citizen-command-palette-tip-users": "Type <code>@</code> to search for users",
    
     	"citizen-command-palette-tip-namespace": "Type <code>:</code> to search for namespace",
    
    -	"citizen-command-palette-tip-templates": "Type <code>{{</code> to search for templates",
    
    +	"citizen-command-palette-tip-templates": "Type <code><nowiki>{{</nowiki></code> to search for templates",
    
     	"citizen-command-palette-tip-actions": "Type <code>></code> for a list of actions"
    
     }
    
    
  • includes/Components/CitizenComponentUserInfo.php+10 5 modified
    @@ -4,6 +4,7 @@
     
     namespace MediaWiki\Skins\Citizen\Components;
     
    +use MediaWiki\Html\Html;
     use MediaWiki\Language\Language;
     use MediaWiki\MediaWikiServices;
     use MediaWiki\Title\MalformedTitleException;
    @@ -52,11 +53,15 @@ private function getUserRegistration(): ?array {
     			return null;
     		}
     
    -		$html = sprintf(
    -			'<time class="citizen-user-regdate" datetime="%s">%s</time>',
    -			wfTimestamp( TS_ISO_8601, $timestamp ),
    -			// Since this is not accessible by anon, we can use user language
    -			$this->lang->userDate( $timestamp, $this->user )
    +		// Since this is not accessible by anon, we can use user language
    +		$date = $this->lang->userDate( $timestamp, $this->user );
    +		$html = Html::element(
    +			'time',
    +			[
    +				'class' => 'citizen-user-regdate',
    +				'datetime' => wfTimestamp( TS_ISO_8601, $timestamp )
    +			],
    +			$date
     		);
     
     		return [
    
  • resources/skins.citizen.commandPalette/components/CommandPaletteFooter.vue+4 4 modified
    @@ -59,10 +59,10 @@ module.exports = exports = defineComponent( {
     		// TODO: Make this expandable with more tips, probably with a mw hook
     		// TODO: Maybe we should move this to store?
     		const tips = [
    -			mw.message( 'citizen-command-palette-tip-commands' ).plain(),
    -			mw.message( 'citizen-command-palette-tip-users' ).plain(),
    -			mw.message( 'citizen-command-palette-tip-namespace' ).plain(),
    -			mw.message( 'citizen-command-palette-tip-templates' ).plain()
    +			mw.message( 'citizen-command-palette-tip-commands' ).parse(),
    +			mw.message( 'citizen-command-palette-tip-users' ).parse(),
    +			mw.message( 'citizen-command-palette-tip-namespace' ).parse(),
    +			mw.message( 'citizen-command-palette-tip-templates' ).parse()
     		];
     
     		const currentTipIndex = ref( 0 );
    
  • resources/skins.citizen.preferences/addPortlet.polyfill.js+1 1 modified
    @@ -15,7 +15,7 @@ function addDefaultPortlet( portlet ) {
     	if ( label ) {
     		const labelDiv = document.createElement( 'div' );
     		labelDiv.classList.add( 'citizen-menu__heading' );
    -		labelDiv.innerHTML = label.textContent || '';
    +		labelDiv.textContent = label.textContent || '';
     		portlet.insertBefore( labelDiv, label );
     		label.remove();
     	}
    
  • resources/skins.citizen.search/templates/TypeaheadPlaceholder.mustache+4 4 modified
    @@ -1,11 +1,11 @@
    -<div 
    +<div
     	class="citizen-typeahead-placeholder"
     	id="citizen-typeahead-placeholder"
     	{{#hidden}}hidden{{/hidden}}
     >
     	<div class="citizen-typeahead-placeholder-content">
     		{{#icon}}<div class="citizen-typeahead-placeholder-icon citizen-ui-icon mw-ui-icon-wikimedia-{{.}}"></div>{{/icon}}
    -		{{#title}}<div class="citizen-typeahead-placeholder-title">{{{.}}}</div>{{/title}}
    -		{{#description}}<div class="citizen-typeahead-placeholder-description">{{{.}}}</div>{{/description}}
    +		{{#title}}<div class="citizen-typeahead-placeholder-title">{{.}}</div>{{/title}}
    +		{{#description}}<div class="citizen-typeahead-placeholder-description">{{.}}</div>{{/description}}
     	</div>
    -</div>
    \ No newline at end of file
    +</div>
    
  • skin.json+1 0 modified
    @@ -312,6 +312,7 @@
     			"class": "MediaWiki\\ResourceLoader\\CodexModule",
     			"dependencies": [
     				"mediawiki.api",
    +				"mediawiki.jqueryMsg",
     				"mediawiki.language",
     				"mediawiki.page.ready",
     				"mediawiki.storage",
    
  • templates/Menu.mustache+2 2 modified
    @@ -6,8 +6,8 @@
     >
    
     	{{#label}}
    
     	<div class="citizen-menu__heading{{#label-class}} {{.}}{{/label-class}}">
    
    -		{{{.}}}
    
    +		{{.}}
    
     	</div>
    
     	{{/label}}
    
     	{{>MenuContents}}
    
    -</nav>
    \ No newline at end of file
    +</nav>
    
    
a0296afaedbe

feat(search): ✨ migrate typeahead to Mustache template part 2

10 files changed · +69 218
  • resources/skins.citizen.search/components/TypeaheadList.less+1 7 modified
    @@ -1,17 +1,11 @@
    -.citizen-typeahead-group {
    -	margin-top: var( --space-xxs );
    -	margin-bottom: var( --space-xxs );
    -}
    -
     .citizen-typeahead-list {
     	margin: 0;
     	list-style: none;
     
     	.citizen-typeahead-group--chips & {
     		display: flex;
     		gap: var( --space-xxs );
    -		padding-right: var( --space-md );
    -		padding-left: var( --space-md );
    +		padding: var( --space-xs ) var( --space-md );
     		overflow-x: auto;
     		-ms-overflow-style: none;  /* IE and Edge */
     		scrollbar-width: none;  /* Firefox */
    
  • resources/skins.citizen.search/components/TypeaheadPlaceholder.less+26 0 added
    @@ -0,0 +1,26 @@
    +.citizen-typeahead-placeholder {
    +	&-content {
    +		display: flex;
    +		flex-direction: column;
    +		place-items: center;
    +		padding: var( --space-xxl ) var( --space-md );
    +		line-height: var( --line-height-xs );
    +		text-align: center;
    +	}
    +
    +	&-icon {
    +		--size-icon: 4rem;
    +		margin-bottom: var( --space-xs );
    +	}
    +
    +	&-title {
    +		font-size: var( --font-size-large );
    +		font-weight: var( --font-weight-medium );
    +	}
    +
    +	&-description {
    +		margin-top: 2px;
    +		font-size: var( --font-size-small );
    +		color: var( --color-subtle );
    +	}
    +}
    
  • resources/skins.citizen.search/htmlHelper.js+0 93 removed
    @@ -1,93 +0,0 @@
    -/**
    - * @typedef {Object} TypeaheadItemData
    - * @property {string} id
    - * @property {string} type
    - * @property {string} link
    - * @property {string} icon
    - * @property {string} thumbnail
    - * @property {string} title
    - * @property {string} label
    - * @property {string} desc
    - */
    -
    -/**
    - * Return an object containing functions to handle HTML elements for a typeahead component.
    - *
    - * @return {Object} An object with functions for creating, updating, and removing HTML elements for a typeahead component.
    - */
    -function htmlHelper() {
    -	return {
    -		/**
    -		 * Generate menu item HTML using the existing template
    -		 *
    -		 * @param {TypeaheadItemData} data
    -		 * @return {HTMLElement|void}
    -		 */
    -		getItemElement: function ( data ) {
    -			// TODO: Should we use template element or Mustache or just Javascript?
    -			const template = document.getElementById( 'citizen-typeahead-template' );
    -
    -			// Shouldn't happen but just to be safe
    -			if ( !( template instanceof HTMLTemplateElement ) ) {
    -				return;
    -			}
    -
    -			const
    -				fragment = template.content.cloneNode( true ),
    -				item = fragment.querySelector( '.citizen-typeahead__item' );
    -
    -			this.updateItemElement( item, data );
    -			return fragment;
    -		},
    -		/**
    -		 * Update typeahead item element
    -		 *
    -		 * @param {HTMLElement} item
    -		 * @param {TypeaheadItemData} data
    -		 */
    -		updateItemElement: function ( item, data ) {
    -			if ( data.id ) {
    -				item.setAttribute( 'id', data.id );
    -			}
    -			if ( data.type ) {
    -				item.classList.add( `citizen-typeahead__item-${ data.type }` );
    -
    -				if ( data.type !== 'placeholder' ) {
    -					item.setAttribute( 'role', 'option' );
    -				}
    -			}
    -
    -			if ( data.size ) {
    -				item.classList.add( `citizen-typeahead__item-${ data.size }` );
    -			}
    -			if ( data.link ) {
    -				item.querySelector( '.citizen-typeahead__content' ).setAttribute( 'href', data.link );
    -			}
    -			if ( data.icon || data.thumbnail ) {
    -				const thumbnail = item.querySelector( '.citizen-typeahead__thumbnail' );
    -				if ( data.thumbnail ) {
    -					thumbnail.style.backgroundImage = `url('${ data.thumbnail }')`;
    -				} else {
    -					thumbnail.classList.add(
    -						'citizen-typeahead__thumbnail',
    -						'citizen-ui-icon',
    -						`mw-ui-icon-wikimedia-${ data.icon }`
    -					);
    -				}
    -			}
    -			if ( data.title ) {
    -				// Required to use innerHTML because of highlightText
    -				item.querySelector( '.citizen-typeahead__title' ).innerHTML = data.title;
    -			}
    -			if ( data.label ) {
    -				item.querySelector( '.citizen-typeahead__label' ).innerHTML = data.label;
    -			}
    -			if ( data.desc ) {
    -				item.querySelector( '.citizen-typeahead__description' ).textContent = data.desc;
    -			}
    -		}
    -	};
    -}
    -
    -/** @module htmlHelper */
    -module.exports = htmlHelper;
    
  • resources/skins.citizen.search/searchPresults.js+8 7 modified
    @@ -1,5 +1,4 @@
     const config = require( './config.json' );
    -const htmlHelper = require( './htmlHelper.js' )();
     const searchHistory = require( './searchHistory.js' )( config );
     
     function searchPresults() {
    @@ -27,20 +26,22 @@ function searchPresults() {
     			document.getElementById( 'citizen-typeahead-list-history' ).outerHTML = templates.TypeaheadList.render( data, partials ).html();
     			document.getElementById( 'citizen-typeahead-group-history' ).hidden = false;
     		},
    -		render: function ( typeaheadEl, templates ) {
    -			typeaheadEl.querySelector( '.citizen-typeahead__item-placeholder' )?.remove();
    +		render: function ( templates ) {
    +			const placeholderEl = document.getElementById( 'citizen-typeahead-placeholder' );
    +			placeholderEl.innerHTML = '';
    +			placeholderEl.hidden = true;
    +
     			const historyResults = searchHistory.get();
     			if ( historyResults && historyResults.length > 0 ) {
     				this.renderHistory( historyResults, templates );
     			} else {
     				const data = {
     					icon: 'articlesSearch',
    -					type: 'placeholder',
    -					size: 'lg',
     					title: mw.message( 'searchsuggest-search' ).text(),
    -					desc: mw.message( 'citizen-search-empty-desc' ).text()
    +					description: mw.message( 'citizen-search-empty-desc' ).text()
     				};
    -				typeaheadEl.append( htmlHelper.getItemElement( data ) );
    +				placeholderEl.innerHTML = templates.TypeaheadPlaceholder.render( data ).html();
    +				placeholderEl.hidden = false;
     			}
     		},
     		clear: function () {
    
  • resources/skins.citizen.search/searchResults.js+4 6 modified
    @@ -1,5 +1,4 @@
     // const config = require( './config.json' );
    -const htmlHelper = require( './htmlHelper.js' )();
     const searchAction = require( './searchAction.js' )();
     
     /**
    @@ -82,15 +81,13 @@ function searchResults() {
     			const regex = regexCache[ match ];
     			return title.replace( regex, '<span class="citizen-typeahead__highlight">$&</span>' );
     		},
    -		getPlaceholderHTML: function ( queryValue ) {
    +		getPlaceholderHTML: function ( queryValue, templates ) {
     			const data = {
     				icon: 'articleNotFound',
    -				type: 'placeholder',
    -				size: 'lg',
     				title: mw.message( 'citizen-search-noresults-title', queryValue ).text(),
    -				desc: mw.message( 'citizen-search-noresults-desc' ).text()
    +				description: mw.message( 'citizen-search-noresults-desc' ).text()
     			};
    -			return htmlHelper.getItemElement( data );
    +			return templates.TypeaheadPlaceholder.render( data ).html();
     		},
     		getResultsHTML: function ( results, queryValue, templates ) {
     			const items = [];
    @@ -133,6 +130,7 @@ function searchResults() {
     		clear: function () {
     			// TODO: This should not be here
     			document.getElementById( 'citizen-typeahead-list-page' ).innerHTML = '';
    +			document.getElementById( 'citizen-typeahead-group-page' ).hidden = true;
     			searchAction.clear();
     		},
     		init: function () {
    
  • resources/skins.citizen.search/skins.citizen.search.less+0 84 modified
    @@ -2,8 +2,6 @@
     @import '../mixins.less';
     
     .citizen-typeahead {
    -	display: flex;
    -	flex-direction: column;
     	border-bottom-right-radius: var( --border-radius-medium );
     	border-bottom-left-radius: var( --border-radius-medium );
     
    @@ -40,71 +38,6 @@
     		}
     	}
     
    -	&__item {
    -		&--hidden {
    -			display: none;
    -		}
    -
    -		&-lg {
    -			.citizen-typeahead {
    -				&__content {
    -					flex-direction: column;
    -					padding-top: var( --space-xl );
    -					padding-bottom: var( --space-xl );
    -					text-align: center;
    -				}
    -
    -				&__title {
    -					font-size: var( --font-size-medium );
    -					font-weight: var( --font-weight-semibold );
    -				}
    -
    -				&__thumbnail {
    -					margin-bottom: var( --space-sm );
    -				}
    -
    -				&__description {
    -					margin-top: 0.1rem;
    -				}
    -			}
    -		}
    -	}
    -
    -	&__content {
    -		display: flex;
    -		align-items: center;
    -		padding: var( --space-xs ) var( --space-md );
    -		color: var( --color-base );
    -	}
    -
    -	&__thumbnail {
    -		flex-shrink: 0;
    -		width: 100%;
    -		max-width: 60px;
    -		height: 60px;
    -		overflow: hidden;
    -
    -		// Needed the specificity, we should refactor this
    -		.citizen-typeahead &.citizen-ui-icon::before {
    -			background-size: contain;
    -		}
    -	}
    -
    -	&__text {
    -		flex-grow: 1;
    -		overflow: hidden;
    -	}
    -
    -	&__header {
    -		display: flex;
    -		justify-content: space-between;
    -	}
    -
    -	&__title {
    -		flex-shrink: 0;
    -		color: var( --color-emphasized );
    -	}
    -
     	&__highlight {
     		font-weight: var( --font-weight-medium );
     		color: var( --color-subtle );
    @@ -115,30 +48,13 @@
     		color: var( --color-emphasized );
     	}
     
    -	&__label {
    -		display: flex;
    -		gap: var( --space-xxs );
    -		font-size: var( --font-size-small );
    -		color: var( --color-base );
    -	}
    -
     	&__labelItem {
     		display: flex;
     		gap: var( --space-xxs );
     		align-items: center;
    -	}
    -
    -	&__description,
    -	&__labelItem {
     		font-size: var( --font-size-x-small );
     		color: var( --color-subtle );
     	}
    -
    -	&__title,
    -	&__description {
    -		overflow: hidden;
    -		text-overflow: ellipsis;
    -	}
     }
     
     // HACK: Hide default MW search suggestion if it somehow loaded
    
  • resources/skins.citizen.search/templates/TypeaheadElement.mustache+1 15 modified
    @@ -1,18 +1,4 @@
     <div id="searchform-suggestions" class="citizen-typeahead">
     	{{#array-lists}}{{>TypeaheadList}}{{/array-lists}}
    -	{{! Template }}
    -	<template id="citizen-typeahead-template">
    -		<div class="citizen-typeahead__item">
    -			<a href="" class="citizen-typeahead__content">
    -				<div class="citizen-typeahead__thumbnail"></div>
    -				<div class="citizen-typeahead__text">
    -					<div class="citizen-typeahead__header">
    -						<div class="citizen-typeahead__title"></div>
    -						<div class="citizen-typeahead__label"></div>
    -					</div>
    -					<div class="citizen-typeahead__description"></div>
    -				</div>
    -			</a>
    -		</div>
    -	</template>
    +	{{#data-placeholder}}{{>TypeaheadPlaceholder}}{{/data-placeholder}}
     </div>
    \ No newline at end of file
    
  • resources/skins.citizen.search/templates/TypeaheadPlaceholder.mustache+11 0 added
    @@ -0,0 +1,11 @@
    +<div 
    +	class="citizen-typeahead-placeholder"
    +	id="citizen-typeahead-placeholder"
    +	{{#hidden}}hidden{{/hidden}}
    +>
    +	<div class="citizen-typeahead-placeholder-content">
    +		{{#icon}}<div class="citizen-typeahead-placeholder-icon citizen-ui-icon mw-ui-icon-wikimedia-{{.}}"></div>{{/icon}}
    +		{{#title}}<div class="citizen-typeahead-placeholder-title">{{{.}}}</div>{{/title}}
    +		{{#description}}<div class="citizen-typeahead-placeholder-description">{{{.}}}</div>{{/description}}
    +	</div>
    +</div>
    \ No newline at end of file
    
  • resources/skins.citizen.search/typeahead.js+12 5 modified
    @@ -10,6 +10,7 @@ const searchResults = require( './searchResults.js' )();
     const searchQuery = require( './searchQuery.js' )();
     
     const templateTypeaheadElement = require( './templates/TypeaheadElement.mustache' );
    +const templateTypeaheadPlaceholder = require( './templates/TypeaheadPlaceholder.mustache' );
     const templateTypeaheadList = require( './templates/TypeaheadList.mustache' );
     const templateTypeaheadListItem = require( './templates/TypeaheadListItem.mustache' );
     
    @@ -298,18 +299,21 @@ const typeahead = {
     		this.mustacheCompiler = mw.template.getCompiler( 'mustache' );
     		Object.assign( compiledTemplates, {
     			TypeaheadElement: this.mustacheCompiler.compile( templateTypeaheadElement ),
    +			TypeaheadPlaceholder: this.mustacheCompiler.compile( templateTypeaheadPlaceholder ),
     			TypeaheadList: this.mustacheCompiler.compile( templateTypeaheadList ),
     			TypeaheadListItem: this.mustacheCompiler.compile( templateTypeaheadListItem )
     		} );
     
     		const data = {
    +			'data-placeholder': { hidden: true },
     			'array-lists': [
     				{ type: 'action', class: 'citizen-typeahead-group--chips', hidden: true, historyValue: 'query' },
     				{ type: 'history', hidden: true, keyboardNavigation: true },
     				{ type: 'page', hidden: true, keyboardNavigation: true, historyValue: 'title' }
     			]
     		};
     		const partials = {
    +			TypeaheadPlaceholder: compiledTemplates.TypeaheadPlaceholder,
     			TypeaheadList: compiledTemplates.TypeaheadList
     		};
     		this.element = compiledTemplates.TypeaheadElement.render( data, partials ).get()[ 0 ];
    @@ -322,7 +326,7 @@ const typeahead = {
     		searchHistory.init();
     		searchResults.init();
     
    -		searchPresults.render( this.element, compiledTemplates );
    +		searchPresults.render( compiledTemplates );
     		// Init the value in case of undef error
     		typeahead.items.set();
     
    @@ -344,6 +348,8 @@ async function getSuggestions() {
     	const renderSuggestions = ( results ) => {
     		const groupEl = document.getElementById( 'citizen-typeahead-group-page' );
     		const listEl = document.getElementById( 'citizen-typeahead-list-page' );
    +		const placeholderEl = document.getElementById( 'citizen-typeahead-placeholder' );
    +
     		if ( results.length > 0 ) {
     			// TODO: This should be a generic method
     			listEl.outerHTML = searchResults.getResultsHTML(
    @@ -352,13 +358,14 @@ async function getSuggestions() {
     				compiledTemplates
     			);
     			groupEl.hidden = false;
    -			typeahead.element.querySelector( '.citizen-typeahead__item-placeholder' )?.remove();
    +			placeholderEl.innerHTML = '';
    +			placeholderEl.hidden = true;
     		} else {
     			// Update placeholder with no result content
     			listEl.innerHTML = '';
     			groupEl.hidden = true;
    -			typeahead.element.querySelector( '.citizen-typeahead__item-placeholder' )?.remove();
    -			typeahead.element.append( searchResults.getPlaceholderHTML( searchQuery.valueHtml ) );
    +			placeholderEl.innerHTML = searchResults.getPlaceholderHTML( searchQuery.valueHtml, compiledTemplates );
    +			placeholderEl.hidden = false;
     		}
     
     		typeahead.form.setLoadingState( false );
    @@ -404,7 +411,7 @@ function updateTypeaheadItems() {
     		getSuggestions();
     	} else {
     		searchResults.clear();
    -		searchPresults.render( typeahead.element, compiledTemplates );
    +		searchPresults.render( compiledTemplates );
     	}
     	typeahead.items.set();
     }
    
  • skin.json+6 1 modified
    @@ -203,6 +203,7 @@
     			"es6": true,
     			"styles": [
     				"resources/skins.citizen.search/skins.citizen.search.less",
    +				"resources/skins.citizen.search/components/TypeaheadPlaceholder.less",
     				"resources/skins.citizen.search/components/TypeaheadList.less",
     				"resources/skins.citizen.search/components/TypeaheadListItem.less"
     			],
    @@ -218,6 +219,11 @@
     					"file": "resources/skins.citizen.search/templates/TypeaheadElement.mustache",
     					"type": "text"
     				},
    +				{
    +					"name": "resources/skins.citizen.search/templates/TypeaheadPlaceholder.mustache",
    +					"file": "resources/skins.citizen.search/templates/TypeaheadPlaceholder.mustache",
    +					"type": "text"
    +				},
     				{
     					"name": "resources/skins.citizen.search/templates/TypeaheadList.mustache",
     					"file": "resources/skins.citizen.search/templates/TypeaheadList.mustache",
    @@ -228,7 +234,6 @@
     					"file": "resources/skins.citizen.search/templates/TypeaheadListItem.mustache",
     					"type": "text"
     				},
    -				"resources/skins.citizen.search/htmlHelper.js",
     				"resources/skins.citizen.search/fetch.js",
     				"resources/skins.citizen.search/searchAction.js",
     				"resources/skins.citizen.search/searchClient.js",
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.