Aug 12, 2026·8 min read·17 visits
A stored XSS vulnerability in Jazzband tablib prior to 3.10.0 allows remote attackers to execute arbitrary JavaScript in browsers via crafted spreadsheet sheet names processed through HTML export pipelines.
CVE-2026-9318 is a stored cross-site scripting (XSS) vulnerability affecting Jazzband tablib versions prior to 3.10.0. The flaw is located in the HTML export functionality of multi-sheet Databook objects. Due to raw f-string interpolation, unsanitized sheet titles containing malicious script tags are rendered directly as HTML, allowing arbitrary client-side code execution in a victim's browser.
The library tablib is a format-agnostic tabular dataset manager in Python, maintained under the Jazzband organization. It is utilized to import, manipulate, and export data in multiple formats including XLSX, ODS, JSON, YAML, and HTML. Because applications rely on tablib to parse user-uploaded spreadsheets and convert them into other configurations, it exposes an entry point for processing untrusted structural metadata.
The stored cross-site scripting vulnerability, identified as CVE-2026-9318, occurs inside the HTML export pipeline for multi-sheet datasets, represented by Databook objects. When exporting a Databook to HTML, the library does not sanitize user-controlled worksheet names. If an attacker delivers a worksheet containing a script sequence in its metadata, the library parses and interpolates it directly into the generated output stream.
In typical production environments, the generated HTML page is subsequently rendered within a web application's administration console or reporting interface. This design flaw crosses a trust boundary, because worksheet metadata is assumed to contain only plain text layout labels. When a browser loads the exported document, it processes the script element under the hosting domain's security context.
This flaw is classified under CWE-79 (Improper Neutralization of Input During Web Page Generation). It presents a significant stored XSS risk because the spreadsheet does not execute payload strings on the backend, but stores the payload in a passive state until the HTML representation is triggered. Downstream integrations like django-import-export are also exposed to these data injection vectors if they permit spreadsheet upload and render sheet exports dynamically.
The architectural root cause of CVE-2026-9318 resides in the export_book method within the format handler module src/tablib/formats/_html.py. The class utilizes raw string concatenation and Python f-string formatting when constructing headings for individual worksheets. This pattern operates on the unsafe assumption that titles of sheets are sanitized before being assigned to Dataset objects.
During processing, the program executes a loop iterating over each dataset in the databook. The library assigns the value of dset.title to a local variable named title. If no sheet name exists, it assigns a default string representation. The flaw is exposed when the title is compiled into the document output stream: result += f'<{cls.BOOK_ENDINGS}>{title}</{cls.BOOK_ENDINGS}>\n'.
The default value of cls.BOOK_ENDINGS is the HTML tag 'h3'. Since the interpolation uses raw formatting rather than an HTML-safe encoder, any character sequence representing tags, such as <script>, is injected directly into the HTML payload as an operational tag rather than plain text. This directly leads to structural HTML injection.
Furthermore, spreadsheet parsers such as openpyxl or odfpyxl extract tab names straight from compressed XML structures inside modern office files. Because these parsers do not block special HTML characters in worksheet titles, the malicious elements flow into tablib without any validation boundaries. The absence of output sanitization at the terminal step is the core vulnerability.
To understand the vulnerable code path, observe the implementation of export_book in tablib versions preceding 3.10.0:
# Vulnerable version of export_book in src/tablib/formats/_html.py
def export_book(cls, databook):
result = ''
for i, dset in enumerate(databook._datasets):
title = dset.title if dset.title else f'Set {i}'
# Unsanitized concatenation of dset.title into output buffer
result += f'<{cls.BOOK_ENDINGS}>{title}</{cls.BOOK_ENDINGS}>\n'
result += dset.html
result += '\n'The implementation does not perform any escaping operations on title prior to evaluating the formatted string. To remediate this issue, the patch submitted in PR #668 implements safe node instantiation and serialization using Python's xml.etree.ElementTree module:
# Patched version of export_book in src/tablib/formats/_html.py
import xml.etree.ElementTree as ET
def export_book(cls, databook):
result = ''
for i, dset in enumerate(databook._datasets):
title = dset.title if dset.title else f'Set {i}'
# Safe generation using xml.etree.ElementTree
title_el = ET.Element(cls.BOOK_ENDINGS)
title_el.text = title
result += ET.tostring(title_el, method='html', encoding='unicode') + '\n'
result += dset.html
result += '\n'By leveraging ET.Element, the developer configures the DOM element using programmatic parameters where title is isolated strictly inside the .text property of the node. When ET.tostring is executed with the html serialization parameter, the standard library automatically encodes HTML-sensitive metacharacters. Characters such as < and > are translated to < and > respectively, which guarantees they are interpreted by the browser engine as plain text content rather than markup.
Analyzing the fix reveals that it successfully neutralizes the target injection channel without degrading performance or functionality. However, developers must ensure that elements surrounding dset.html do not introduce secondary cross-site scripting vulnerabilities elsewhere in the dataset body parsing pipeline.
Exploitation of CVE-2026-9318 requires two primary phases: an upload of a malicious spreadsheet file, and the execution of an HTML export that renders in an administrative or user context. The initial access vector is low-complexity and requires only standard file-upload privileges, which are commonly granted to end-users of business intelligence portals.
An attacker begins by preparing an ODS or XLSX spreadsheet document. Using standard programmatic tools or directly modifying the document's underlying XML, the attacker renames a sheet tab to include a client-side execution sequence. For example, setting the sheet name to <script>fetch("https://github.com/jazzband/tablib")</script> sets up the execution phase.
When the document is uploaded, the target application uses tablib to parse the workbook and populate a Databook object. The malicious sheet name is mapped directly into dset.title as a standard string. Once the administrative dashboard triggers an HTML render event, the code runs, producing a payload output that the browser compiles and executes.
Below is the sequence of events illustrating this attack lifecycle:
Prerequisites for a successful exploit include an active target rendering the HTML results without secondary escaping mechanisms. Many standard implementations display the raw HTML using template directives such as Django's |safe filter or Jinja2's |safe rendering. This bypasses the framework's native protection mechanisms, resulting in stored cross-site scripting.
The impact of this vulnerability depends on the privilege level of the victim user who views the HTML export. Because HTML pages generated by tablib are often administrative or audit reports, the typical victim is likely a privileged user or system administrator. Consequently, exploitation has a high probability of targeting management sessions.
When the payload executes in the victim's browser, it operates within the context of their authenticated session. If the application does not utilize secure cookie configurations, the script can extract session identifiers or authorization headers from document.cookie or LocalStorage. This allows the attacker to hijack active sessions and assume the victim's identity.
Beyond simple credential harvesting, the script can execute complex administrative actions. This includes initiating unauthorized API requests, modifying account configurations, or creating new administrative profiles within the target system. The scope of impact is classified as 'Changed' (S:C) because the browser sandbox exploitation breaks out of the context of the underlying library and affects the parent web application environment.
While CVSS assigns a medium score of 5.4, the relative danger is higher in enterprise environments where spreadsheet parsing is heavily automated. If the server application integrates other components with the compromised sheets, the integrity of downstream reporting workflows is broken.
The most effective mitigation is upgrading to tablib version 3.10.0 or higher, which completely patches the vulnerable interpolation code. To upgrade the library in standard environments, execute the pip update command or modify your requirement configuration: pip install --upgrade tablib>=3.10.0.
If upgrading immediately is not feasible, implement input sanitization rules at the entry point of the ingestion pipeline. Define a strict whitelist regex for worksheet names, allowing only alphanumeric characters, underscores, hyphens, and spaces. Ensure any title failing this check is sanitized or rejected before the databook object is passed to export format routines.
Deploy a strict Content Security Policy (CSP) header on target application domains. The header Content-Security-Policy: default-src 'self'; script-src 'self' prevents inline execution of scripts, neutralizing payloads delivered via the sheet title. Additionally, set the HttpOnly attribute on session cookies to prevent retrieval by XSS scripts.
Perform regular security audits of template rendering paths. Ensure that exported HTML strings are not marked as safe within templating engines unless they have been explicitly run through an HTML sanitization engine. This prevents developer-level output configuration from re-introducing structural injection issues.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
tablib Jazzband | < 3.10.0 | 3.10.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 5.4 (Medium) |
| EPSS Score | 0.00181 |
| Impact | Arbitrary client-side script execution (Stored XSS) |
| Exploit Status | Proof of Concept (PoC) available |
| KEV Status | Not listed |
The software does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.
An LDAP injection vulnerability exists in the centraldogma-server-auth-shiro module of LY Corporation Central Dogma before version 0.84.0. The search logic dynamically constructs LDAP search filters by interpolating user-provided usernames without escaping RFC 4515 metacharacters. Unauthenticated remote attackers can leverage this flaw to bypass authentication, enumerate directory hierarchies, and access unauthorized resources.
CVE-2026-11746 is a critical vulnerability in Central Dogma Server prior to version 0.84.0, where an embedded ZooKeeper replication secret silently falls back to a publicly known, hard-coded default string ('ch4n63m3'). Remote attackers with access to the replication network can authenticate as legitimate cluster peers, potentially leading to unauthorized data exposure, state manipulation, or complete cluster takeover.
A logical verification flaw in ZITADEL's external JWT Identity Provider validation allows attackers to bypass session expiration checks. If an incoming JWT lacks the 'exp' claim, the system skips validation entirely, creating an indefinitely valid session. This issue has been addressed in versions 3.4.12 and 4.15.2.
CVE-2026-59149 identifies a directory traversal vulnerability in `@mockoon/commons-server`, the backend mock-server library powering the Mockoon application. The flaw occurs in the path containment validation logic used during raw file response generation. An unauthenticated attacker can exploit this weakness to retrieve arbitrary files from sibling directories sharing a common prefix with the designated static base directory.
An in-depth analysis of CVE-2026-59148, a high-severity flaw in Mockoon where unauthenticated administrative endpoints and a wildcard Cross-Origin Resource Sharing (CORS) policy allow remote execution, state poisoning, and credential theft.
An improper authentication vulnerability (CWE-287) in ZITADEL's external identity provider handler before version 4.15.3 allows remote attackers to perform complete account takeover. When auto-linking by email is enabled, ZITADEL verifies that the local target account has a verified email address but fails to verify if the external provider confirmed ownership of that same email. Attackers can exploit this by registering an unverified account with a victim's email address on a permissive external provider, leading to unauthorized account binding and persistent access.