CVE-2026-50290 Overview
CVE-2026-50290 is a cross-site scripting (XSS) vulnerability [CWE-79] in SpecifyJS, a declarative TypeScript user interface framework. Versions prior to 0.2.136 implement CSS value sanitization using simple regex patterns that strip expression( and url(javascript: substrings. Attackers can bypass this filter using CSS unicode escapes such as \65xpression(, null bytes, or inline CSS comments like exp/**/ression(. The bypass only affects legacy browsers (Internet Explorer 6 through 10), and SpecifyJS targets modern browsers, which limits real-world impact.
Critical Impact
CSS injection payloads can evade sanitization in legacy browser contexts, enabling script execution through CSS expression() and related legacy vectors.
Affected Products
- SpecifyJS versions prior to 0.2.136
- Applications rendering user-controlled CSS values through SpecifyJS server-side rendering
- Legacy browser clients (IE6–IE10) consuming SpecifyJS-rendered output
Discovery Timeline
- 2026-08-21 - CVE-2026-50290 published to NVD
- 2026-08-21 - Last updated in NVD database
Technical Details for CVE-2026-50290
Vulnerability Analysis
The vulnerability resides in the CSS sanitization logic in core/src/server/render-to-string.ts. The original implementation applied two regex substitutions to strip expression( and url(javascript: from CSS values. This pattern-matching approach operates on the raw input string without normalization. Attackers can construct payloads that decode to the dangerous tokens only after the CSS parser interprets escape sequences or comments.
CSS parsers in legacy browsers resolve unicode escapes like \65 to the character e before tokenization. A payload of \65xpression(alert(1)) bypasses a naive regex looking for the literal string expression(. Similarly, CSS comments /* */ are stripped by the parser but preserved by the sanitizer, allowing exp/**/ression( to slip through. Null byte insertion produces the same bypass class.
Root Cause
The root cause is incomplete input canonicalization. The sanitizer matched patterns against the untransformed input while the CSS parser evaluates a canonicalized form. This mismatch between the sanitizer's view and the parser's view of the input is a classic filter-bypass pattern.
Attack Vector
An attacker supplies a crafted CSS value through any interface where SpecifyJS accepts user-controlled style properties. The malicious value passes the regex filter, is embedded in the rendered output, and executes when a legacy browser evaluates the CSS expression(), behavior:, -moz-binding, or -o-link construct. User interaction is required to load the page.
// Patch from core/src/server/render-to-string.ts
// Before: naive substring stripping
// cssValue = cssValue
// .replace(/expression\s*\(/gi, '')
// .replace(/url\s*\(\s*javascript:/gi, 'url(');
// After: normalize then reject
const normalizedCss = cssValue
.replace(/\\[0-9a-fA-F]{1,6}\s?/g, '_')
.replace(/\/\*[\s\S]*?\*\//g, '');
if (/expression\s*\(|javascript\s*:|behavior\s*:|moz-binding|-o-link/i.test(normalizedCss)) {
cssValue = '';
}
Source: GitHub Commit 25d1fb4
Detection Methods for CVE-2026-50290
Indicators of Compromise
- CSS property values containing backslash-hex escape sequences such as \65, \45, or \75 followed by identifier characters
- CSS values containing inline C-style comments (/* */) inside property tokens
- Rendered HTML style attributes containing the substrings behavior:, -moz-binding, -o-link, or expression(
- Requests carrying null bytes (%00) inside CSS parameter values
Detection Strategies
- Inspect HTTP request bodies and query parameters for CSS unicode escape patterns matching \\[0-9a-fA-F]{1,6} inside style-bearing fields
- Scan server-rendered HTML output for CSS keywords that should never appear in sanitized output, including expression, behavior, and moz-binding
- Enable Content Security Policy reporting to catch inline style evaluation attempts in downstream clients
Monitoring Recommendations
- Log all SpecifyJS render calls that consume externally sourced style values and forward them to a centralized analytics pipeline
- Alert on outbound responses containing legacy CSS execution keywords after sanitization has run
- Track SpecifyJS package versions across deployments to identify hosts still running versions below 0.2.136
How to Mitigate CVE-2026-50290
Immediate Actions Required
- Upgrade SpecifyJS to version 0.2.136 or later across all affected applications
- Audit application code for user-controlled inputs flowing into SpecifyJS style properties
- Enforce a strict Content Security Policy that disallows inline styles and unsafe expressions
Patch Information
The fix is included in SpecifyJS 0.2.136 and documented in GitHub Security Advisory GHSA-93q6-wwjh-jc6h. The updated sanitizer normalizes unicode escapes and strips CSS comments before pattern matching, and it rejects behavior:, -moz-binding, and -o-link in addition to expression( and javascript:. The corresponding code change is available in commit 25d1fb4.
Workarounds
- Reject any style attribute values containing backslash characters at the application input layer
- Deploy a Content Security Policy header that blocks legacy CSS execution vectors
- Block or upgrade legacy browsers (IE6–IE10) that honor the affected CSS constructs
# Upgrade to patched version
npm install specifyjs@0.2.136
# Verify installed version
npm ls specifyjs
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

