Summary
| Attribute |
Value |
| Vendor / Org |
NV (Nikita Vasilyev) |
| Product |
cssom |
| Component |
lib/CSSStyleDeclaration.js |
| Affected Versions |
<= 0.5.0 (all published versions; verified on 0.3.1 and 0.5.0) |
| Severity |
High |
| CVSS 3.1 Score |
7.5 (High) |
| CVSS 3.1 Vector |
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE |
CWE-1284 (Improper Validation of Specified Quantity in Input) |
| Affected File |
lib/CSSStyleDeclaration.js:41-56 (write), :112-124 (serialization loop) |
Description
Improper validation of a CSS declaration name in cssom <= 0.5.0 lets a remote unauthenticated
caller abort the Node.js process via a 20-byte stylesheet passed to CSSOM.parse().
CSSStyleDeclaration.setProperty writes each declaration name straight onto the instance with
this[name] = value, in the same namespace as the object's own length field that tracks the
declaration count. A declaration literally named length overwrites that count with an
attacker-chosen number. The cssText serializer then loops for (i=0; i < this.length; ++i) and
pre-sizes an array to the poisoned bound, so a{length:2000000000} makes the library try to build
a two-billion-entry array and V8 aborts with an uncatchable out-of-memory error. The attacker
turns the ability to submit a tiny CSS string into a reliable crash of the shared service that
parses it.
Impact
A remote unauthenticated caller who can submit CSS to a service that parses it with cssom can
terminate the process with a fixed 20-byte string. Because the failure is a V8 fatal OOM rather
than a thrown error, try/catch around parse() does not save the consumer, so the service
crashes for every concurrent user. This is a denial-of-service caused by an input-validation flaw
in a specific internal field, distinct from generic "large input parses slowly" behavior: the
input is tiny and the amplification comes from an overwritten control variable. No existing CVE
covers cssom (OSV.dev and NVD return no records for the package).
Remediation
Recommended Fix
In setProperty, reject or divert declaration names that collide with internal fields, and store
declarations in a dedicated null-prototype map instead of the instance namespace:
setProperty: function(name, value, priority) {
if (name === "length" || name === "parentRule" || name === "_importants") {
return; // never let a declaration name overwrite an internal field
}
// ... existing logic ...
}
Additionally coerce the serialization bound so a poisoned value cannot drive allocation:
for (var i = 0, length = this.length >>> 0; i < length; ++i) { ... }
This is an in-repo change that protects consumers using the documented CSSOM.parse /
CSSStyleDeclaration API correctly. It does not reduce to "callers should sanitize their CSS":
the crash is triggered by grammatically ordinary CSS that the parser is expected to accept, and
no documented precondition tells callers to strip a declaration named length.
Workaround
Consumers can cap input length and run CSSOM.parse in a worker/child process with a memory
limit so an OOM abort does not take down the main process. This does not prevent the crash of the
worker.
References
Summary
lib/CSSStyleDeclaration.js<= 0.5.0(all published versions; verified on 0.3.1 and 0.5.0)CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Hlib/CSSStyleDeclaration.js:41-56(write),:112-124(serialization loop)Description
Improper validation of a CSS declaration name in cssom
<= 0.5.0lets a remote unauthenticatedcaller abort the Node.js process via a 20-byte stylesheet passed to
CSSOM.parse().CSSStyleDeclaration.setPropertywrites each declaration name straight onto the instance withthis[name] = value, in the same namespace as the object's ownlengthfield that tracks thedeclaration count. A declaration literally named
lengthoverwrites that count with anattacker-chosen number. The
cssTextserializer then loopsfor (i=0; i < this.length; ++i)andpre-sizes an array to the poisoned bound, so
a{length:2000000000}makes the library try to builda two-billion-entry array and V8 aborts with an uncatchable out-of-memory error. The attacker
turns the ability to submit a tiny CSS string into a reliable crash of the shared service that
parses it.
Impact
A remote unauthenticated caller who can submit CSS to a service that parses it with cssom can
terminate the process with a fixed 20-byte string. Because the failure is a V8 fatal OOM rather
than a thrown error,
try/catcharoundparse()does not save the consumer, so the servicecrashes for every concurrent user. This is a denial-of-service caused by an input-validation flaw
in a specific internal field, distinct from generic "large input parses slowly" behavior: the
input is tiny and the amplification comes from an overwritten control variable. No existing CVE
covers cssom (OSV.dev and NVD return no records for the package).
Remediation
Recommended Fix
In
setProperty, reject or divert declaration names that collide with internal fields, and storedeclarations in a dedicated null-prototype map instead of the instance namespace:
Additionally coerce the serialization bound so a poisoned value cannot drive allocation:
This is an in-repo change that protects consumers using the documented
CSSOM.parse/CSSStyleDeclarationAPI correctly. It does not reduce to "callers should sanitize their CSS":the crash is triggered by grammatically ordinary CSS that the parser is expected to accept, and
no documented precondition tells callers to strip a declaration named
length.Workaround
Consumers can cap input length and run
CSSOM.parsein a worker/child process with a memorylimit so an OOM abort does not take down the main process. This does not prevent the crash of the
worker.
References
lib/CSSStyleDeclaration.js:41-56and:112-124in cssom 0.5.0.