ZeroPath at Black Hat USA 2026

js-cookie CVE-2026-46625: Per-Instance Prototype Hijack Enables Cookie Attribute Injection — Quick Look with PoC and Patch Analysis

A brief summary of CVE-2026-46625, a high severity prototype pollution flaw in the widely used js-cookie npm package that allows attackers to inject arbitrary cookie attributes. Includes proof of concept details and patch analysis.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-06-10

js-cookie CVE-2026-46625: Per-Instance Prototype Hijack Enables Cookie Attribute Injection — Quick Look with PoC and Patch Analysis
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A prototype pollution flaw in js-cookie, one of the most downloaded cookie handling libraries in the npm ecosystem, allows attackers to inject arbitrary attributes into cookies that developers believed were securely configured. With over 2 billion npm downloads and 9,529 direct dependents, the blast radius of CVE-2026-46625 extends across a significant portion of the client side JavaScript landscape.

What makes this vulnerability particularly interesting is its mechanism: rather than the classic global prototype pollution that mutates Object.prototype and affects every object in the runtime, this is a per-instance prototype hijack that leaves Object.prototype untouched. The polluted object is the specific merged attributes object created during each Cookies.set() call, making it harder to detect through standard pollution checks while remaining reliably exploitable.

Technical Information

Root Cause

The vulnerability lives in the assign() helper function in src/assign.mjs. This function is responsible for merging cookie attribute objects, and it uses a for...in loop with plain property assignment to copy properties from source objects onto a target:

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 }

The for...in loop was retained for compatibility with Internet Explorer 10 and 11, which do not support Object.assign() or Object.getOwnPropertyNames() in the required capacity. This compatibility constraint directly contributed to the vulnerability's existence, as modern property copying approaches using Object.keys() or Object.assign() would not enumerate __proto__ and would not trigger the setter.

When the source object is produced by JSON.parse, a "__proto__" member in the JSON becomes an own enumerable property of the parsed object, distinct from the inherited __proto__ accessor on Object.prototype. The for...in loop enumerates it, and the assignment target["__proto__"] = source["__proto__"] on a fresh target {} invokes the Object.prototype.__proto__ setter. This replaces the target's prototype with the attacker supplied value.

The result: Object.prototype itself remains unmodified, but the merged attributes object now inherits attacker controlled keys through its modified prototype chain.

Exploitation Chain

The attack proceeds in two stages:

  1. Prototype hijack via assign(): An attacker controlled JSON object containing a __proto__ key is passed as the attributes argument to Cookies.set(), Cookies.remove(), Cookies.withAttributes(), or Cookies.withConverter(). The assign() function merges this object using for...in, triggering the __proto__ setter and polluting the merged attributes object's prototype.

  2. Attribute injection via set(): The consuming set() function enumerates the merged attributes object with another for...in loop to build the Set-Cookie string. Every key the attacker placed on the polluted prototype appears as a cookie attribute pair in the resulting string.

The canonical attack pattern involves fetching cookie configuration from a backend API:

fetch('/config').then(r => r.json()).then(attributes => { Cookies.set('session', 'value', attributes); });

If an attacker can influence the JSON response (via a compromised API endpoint, a man in the middle attack on an unencrypted connection, or a backend injection flaw), they can supply a payload such as:

{"__proto__":{"domain":"evil.example","secure":"false","samesite":"None"}}

This causes js-cookie to emit a Set-Cookie string with attacker controlled domain, secure, samesite, expires, and path values, effectively bypassing the developer's intended cookie security constraints.

Attack Vectors

Attack VectorDescriptionPrerequisites
Compromised API responseAttacker modifies JSON returned from backend config endpointBackend compromise or MITM on unencrypted connection
JSON injection via backendAttacker injects __proto__ keys into server stored configurationWrite access to config store or backend injection flaw
Client side JSON tamperingAttacker modifies JSON in transit or via XSS prior to cookie callXSS in same origin or network level attack
Third party script injectionMalicious script calls Cookies.set() with crafted attributesScript inclusion from untrusted source

CVSS v3.1 Breakdown

The CVSS vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N breaks down as follows:

MetricValueRationale
Attack VectorNetwork (AV:N)Exploitable remotely over network
Attack ComplexityLow (AC:L)No special conditions required
Privileges RequiredNone (PR:N)No authentication needed
User InteractionNone (UI:N)Automatic upon vulnerable code path execution
ScopeUnchanged (S:U)Impact limited to the vulnerable component
ConfidentialityNone (C:N)No direct information disclosure
IntegrityHigh (I:H)Cookie attributes can be fully manipulated
AvailabilityNone (A:N)No direct availability impact

The Integrity: High rating reflects the severity of cookie attribute manipulation: an attacker who can set domain to a controlled host, samesite to None, or secure to false can enable session hijacking, cross site request forgery, and cookie exfiltration.

Proof of Concept

A complete, runnable proof of concept is published in the GitHub Security Advisory (GHSA-qjx8-664m-686j) and reproduced in OpenSearch Dashboards issue #12040.

Environment Setup:

mkdir -p /tmp/jscookie-poc && cd /tmp/jscookie-poc npm init -y npm i js-cookie

PoC (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:

node poc.mjs

The expected output demonstrates that the attacker controlled attributes (domain=evil.com, secure=false, samesite=None, expires=-1) are injected into the Set-Cookie string, even though the developer never specified those attributes.

This PoC simulates a Node.js environment by shimming document.cookie, but the vulnerability is equally exploitable in browser contexts where js-cookie is typically deployed.

Patch Information

The vulnerability was fixed by maintainer Klaus Hartl (carhartl) in commit eb3c40e, released as part of version 3.0.7 on May 16, 2026.

The fix is a single line guard inserted into the for...in loop inside src/assign.mjs:

@@ -2,6 +2,7 @@ export default function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] for (var key in source) { + if (key === '__proto__') continue target[key] = source[key] } }

By short circuiting the loop iteration whenever key equals '__proto__', the dangerous setter is never invoked, and the target object's prototype chain remains intact. This is a minimal, targeted fix that preserves backward compatibility, including compatibility with IE 10/11, which the commit message explicitly mentions as a reason the codebase still uses for...in rather than modern alternatives like Object.keys() or Object.assign().

It is worth noting that the GitHub Security Advisory originally suggested a more defensive patch that also filtered 'constructor' and 'prototype' keys, and additionally replaced plain assignment with Object.defineProperty() to avoid triggering setters entirely. However, the maintainer opted for the narrower fix targeting only '__proto__', since that is the only key whose plain assignment behavior triggers the prototype setter in practice.

A corresponding unit test was added in test/tests.js:

QUnit.test( 'sanitization of attributes to prevent prototype pollution from untrusted input', function (assert) { var untrusted = JSON.parse('{"__proto__":{"foo":"bar"}}') assert.strictEqual( Cookies.set('c', 'v', untrusted), 'c=v; path=/', 'should prevent attribute-injection via prototype pollution' ) } )

This test simulates the attack scenario by creating an object via JSON.parse with a __proto__ payload, passing it to Cookies.set(), and verifying the resulting cookie string contains only the default path=/ attribute and none of the attacker injected keys.

The release notes for v3.0.7 explicitly reference CVE-2026-46625, making it straightforward for downstream consumers to identify and adopt the fix.

Affected Systems and Versions

All versions of js-cookie prior to 3.0.7 are affected. Specifically:

  • js-cookie versions <= 3.0.5 are confirmed vulnerable
  • js-cookie version 3.0.7 contains the fix
  • js-cookie version 3.0.8 (published May 29, 2026) is the latest release and carries no known security vulnerabilities

The vulnerability is exploitable in any application that passes externally sourced or attacker influenced JSON objects as the attributes argument to Cookies.set(), Cookies.remove(), Cookies.withAttributes(), or Cookies.withConverter().

Vendor Security History

According to Snyk's vulnerability database, CVE-2026-46625 represents the first publicly disclosed security vulnerability for the js-cookie v3.x line. The predecessor library, jquery-cookie, was retired and superseded by js-cookie, with no known CVE history of its own publicly tracked.

The project maintains a SECURITY.md file in its repository providing guidance on reporting vulnerabilities, indicating a responsible disclosure process is in place. The advisory was published and the patch shipped on the same day (May 16, 2026), demonstrating a rapid response timeline for an open source project maintained by a small team.

For broader context, prototype pollution vulnerabilities continue to affect major JavaScript libraries. Similar flaws have been disclosed in tough-cookie (CVE-2023-26136) and lodash (CVE-2026-2950), both involving __proto__ manipulation through unsafe merge or assign operations and sharing the same CWE-1321 classification. Adobe also released emergency patches in April 2026 following confirmed active exploitation of a critical prototype pollution flaw.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Jira
  • Linear
  • Slack
  • Security Compass
Security magnifying glass visualization