Jul 29, 2026·6 min read·70 visits
style-dictionary is vulnerable to prototype pollution via convertTokenData. An incomplete patch in v5.4.4 can be bypassed using constructor.prototype, allowing global runtime pollution.
A critical prototype pollution vulnerability (CVE-2026-54639) exists in style-dictionary versions 4.3.0 through 5.4.3 due to unsafe object traversal in the convertTokenData utility. Although a patch was released in version 5.4.4 targeting '__proto__', a complete bypass is possible using 'constructor.prototype', leading to persistent global prototype pollution and potential remote code execution.
The NPM package style-dictionary serves as a build system to translate design tokens across platforms, including Sass, iOS Swift, and Android XML. The utility function convertTokenData is exposed to allow conversion between flat arrays, objects, and maps. This utility introduces an entry point for processing structure-modifying token properties, elevating the attack surface.
During translation, a flat structure containing malicious token keys can manipulate the global prototype chain. The vulnerability belongs to the class of Improperly Controlled Modification of Object Prototype Attributes (CWE-1321). This vulnerability was introduced in version 4.3.0 and remained present in subsequent releases.
In scenarios where style-dictionary executes dynamically within web applications, CI/CD runners, or continuous deployment pipelines that process user-defined styles, the threat vector shifts from local execution to arbitrary execution. This vulnerability exposes the host runtime to remote exploit opportunities depending on downstream object evaluation practices.
The core flaw resides in lib/utils/convertTokenData.js within the convertToTokenObject utility. When a user instructs the engine to export token data as an object format (output: 'object'), the application processes an array of tokens and reconstructs a nested object based on the property paths in the token's key property.
The logic parses keys by stripping curly braces and splitting the remaining string with a dot (.) separator. The engine then recursively traverses a base object literal obj = {} using the split path segments. If a path segment points to an inherited property (such as __proto__ or constructor), the traversal code fails to safely isolate modifications to own-properties of the object.
// Vulnerable snippet of path resolution
if (slice[k] === undefined) {
slice[k] = {};
}Because __proto__ evaluates to the standard Object.prototype (and is therefore not undefined), the assignment block is skipped, and slice is updated to point directly to Object.prototype. The subsequent loop iteration then assigns values directly to the global prototype, affecting every object instance within the Node.js runtime environment.
The introduction of the vulnerable utility occurred in commit 209085d9782cfc0783c4d983f3f1bb2c515954ec. The original vulnerable structure recursively assigned paths using arbitrary user input without sanitization.
// Vulnerable function in lib/utils/convertTokenData.js (v4.3.0)
function convertToTokenObject(tokenArray) {
const obj = ({});
tokenArray.forEach((token) => {
const { key } = token;
const keyArr = (key).replace('{', '').replace('}', '').split('.');
let slice = obj;
keyArr.forEach((k, i, arr) => {
if (slice[k] === undefined) {
slice[k] = {};
}
if (i === arr.length - 1) {
slice[k] = token;
}
slice = slice[k];
});
});
return obj;
}In version 5.4.4, the library attempted to mitigate the issue via commit 23b5e8dda143441f0d6b8e2b4222e2da98058bc5 by introducing a check: if (key?.includes('__proto__')) { return; }.
This remediation relies on blacklist-based input validation, which is vulnerable to alternative property traversal paths. An attacker can completely bypass this check by supplying a key such as {constructor.prototype.polluted_property}. In this scenario, the string verification fails to trigger the exit condition, while the inner traversal evaluates slice['constructor'] (the Object constructor function) and slice['constructor']['prototype'] (the global Object.prototype), achieving identical prototype modification.
To exploit this vulnerability, an attacker must control or inject content into a design token structure processed by style-dictionary. This typically occurs when applications accept user-defined themes, external configuration packages, or user-submitted asset bundles.
The attack begins by crafting a payload structure where the key field points to a prototype path. The diagram below illustrates how the recursive path resolver traverses from an empty object through the constructor function to reach and modify the global Object.prototype namespace.
This sequence bypasses the __proto__ blacklist guard and injects the attacker-defined token structure directly into the global object prototype. Consequently, any plain JavaScript object subsequently initialized during the application's lifecycle will inherit the malicious properties.
A successful prototype pollution attack in a Node.js runtime has broad security implications. An attacker who is able to inject properties into the standard prototype chain can disrupt application workflow logic, cause Denial of Service (DoS) via crash conditions, or execute arbitrary code.
In Node.js applications, remote code execution (RCE) can be achieved by polluting options passed to sub-process creation functions. If the application or its dependencies spawn shell commands using APIs like child_process.spawn or child_process.fork, parameters such as shell, env, or execArgv can be polluted. This forces the underlying operating system interpreter to execute arbitrary code defined in the payload.
The CVSS v3.1 metric is rated as 8.8 (High) under local execution contexts. However, if style-dictionary functions within an automated SaaS environment or dynamically processes user uploads online, the impact shifts to an unauthenticated Remote Code Execution vulnerability.
To secure applications using style-dictionary, relying on string-based blacklists is insufficient. The most robust remediation involves preventing keys that do not belong to the object's own properties from being recursively resolved, or using null-prototype objects.
The inner resolver must explicitly block the keys __proto__, constructor, and prototype. A structurally sound implementation of the convertToTokenObject utility would resemble the following pattern:
function secureConvertToTokenObject(tokenArray) {
// Create a base object that does not inherit from Object.prototype
const obj = Object.create(null);
tokenArray.forEach((token) => {
const { key } = token;
if (typeof key !== 'string') return;
const keyArr = key.replace('{', '').replace('}', '').split('.');
let slice = obj;
for (let i = 0; i < keyArr.length; i++) {
const k = keyArr[i];
// Enforce strict property checks to block traversal paths
if (k === '__proto__' || k === 'constructor' || k === 'prototype') {
return;
}
if (slice[k] === undefined) {
slice[k] = {};
}
if (i === keyArr.length - 1) {
slice[k] = token;
}
slice = slice[k];
}
});
// Convert back to standard object structure safely if downstream consumption requires it
return Object.assign({}, obj);
}For temporary runtime mitigation where dependency updates are restricted, implement validation schemas using JSON Schema or restrict incoming token keys via matching regex patterns like /("key"\s*:\s*"{.*(?:__proto__|constructor|prototype).*}")/ before passing inputs to the builder.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
style-dictionary Amazon | >= 4.3.0, <= 5.4.3 | 5.4.4 (Incomplete fix) |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1321 |
| Vulnerability Class | Prototype Pollution |
| CVSS v3.1 Score | 8.8 (High) |
| Attack Vector | Local (escalatible to Remote) |
| EPSS Score | 0.00132 (3.12 percentile) |
| Patch Status | Incomplete (v5.4.4 bypass identified) |
| CISA KEV Status | Not listed |
The software initializes or populates an object with user-controlled keys or paths without restricting modifications to the object prototype, allowing attackers to inject or modify properties of the global Object prototype.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.