#10214·Leaflet

Statement on CVE-2025-69993 (XSS via `bindPopup`)

Author: mournerCreated May 12, 2026Updated Jul 2, 2026
Labelsdocswon't fix

A recent CVE, CVE-2025-69993, claims Leaflet is vulnerable to Cross-Site Scripting because bindPopup() renders its string argument as HTML without sanitization. We are publishing this pinned issue to clarify our position, because the CVE has caused confusion in downstream security audits.

Our position

This is documented, intentional behavior, not a vulnerability in Leaflet.

bindPopup(), bindTooltip(), setContent() and related methods are documented to accept an HTML string, a DOM element, or a function, and to render the string as HTML. In the scenario described by the CVE, the underlying issue (CWE-79) lies with the calling application that passes attacker-controlled input into a documented HTML sink without sanitizing it. A lightweight front-end mapping library cannot, and should not, bundle a heavy HTML sanitizer to defend against application-level misuse of a documented API.

Prior art

This pattern — a documented API that renders HTML, with sanitization treated as the application's responsibility — is the established norm across the JavaScript ecosystem:

  • MapLibre GL JS (the most direct peer to Leaflet) documents Popup.setHTML() as: "This method does not perform HTML filtering or sanitization, and must be used only with trusted content." It provides a separate setText() method for untrusted input. The same applies to Mapbox GL JS.
  • React ships dangerouslySetInnerHTML — the name itself is the warning.
  • Vue ships v-html with the explicit guidance: "Never use v-html on user-provided content."
  • jQuery's .html() sets innerHTML and has been the subject of multiple disputed CVEs over the years on these exact grounds.
  • D3.js's selection.html() sets innerHTML directly, with no security warning in the official documentation.
  • lit-html ships an explicit unsafeHTML directive — again named to signal the contract.

None of these libraries bundles an HTML sanitizer. All treat the boundary between "content the application asked us to render" and "untrusted input the application should have sanitized" as the application's responsibility. Leaflet's bindPopup sits squarely in this same category.

What we have done

To remove any ambiguity, we have updated the API documentation and tutorials. The reference now states explicitly at every relevant entry point (bindPopup, bindTooltip, setContent, openPopup, openTooltip, setPopupContent, setTooltipContent, and the content option) that string content is rendered as HTML, and recommends sanitizing untrusted input with a library like DOMPurify or passing a DOM element built with textContent. The Popup, Tooltip, GeoJSON, and Quick Start documentation pages now include a security note with a safe-pattern code sample. These changes apply to both the 1.x and 2.0 docs on leafletjs.com.

What we have not done, and will not do

  • We will not bundle a sanitizer into Leaflet. The right tool (DOMPurify, sanitize-html, Trusted Types, a CSP, etc.) depends on the application's threat model and content policy.
  • We will not silently break existing applications that intentionally rely on HTML rendering for links, formatting, images, and similar.

A separate, longer-term design discussion about adopting Trusted Types in a future major version is tracked independently and is not driven by this CVE.

What to do if your scanner flags this CVE

  1. Audit whether your application actually passes untrusted input into Leaflet's popup/tooltip APIs. If it doesn't, the CVE does not apply to you.
  2. If it does, sanitize the input or use the DOM-element pattern documented in the Popup reference. This is the correct fix regardless of which library renders the HTML.
  3. Link your security team to this issue as the maintainers' formal position.

CVE dispute

We have formally disputed this CVE with MITRE and requested it be marked DISPUTED in the CVE record. We will update this issue when MITRE responds.

— The Leaflet maintainers