Automated Threat Modeling

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

CVE-2026-46625 is a high-severity prototype pollution flaw in the widely used js-cookie npm package that lets attackers inject arbitrary cookie attributes. This quick look covers the proof of concept and the patch.

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, lets attackers inject arbitrary attributes into cookies that developers believed were securely configured. The package has over 2 billion npm downloads and 9,529 direct dependents, so the blast radius of CVE-2026-46625 covers a significant portion of client-side JavaScript.

The mechanism is the interesting part. Classic global prototype pollution mutates Object.prototype and affects every object in the runtime. This is a per-instance prototype hijack, and it leaves Object.prototype untouched. The polluted object is the specific merged attributes object created during each Cookies.set() call. That makes the bug harder to catch with standard pollution checks, and it is still reliably exploitable.

Technical Information

Root Cause

The vulnerability lives in the assign() helper function in src/assign.mjs. The helper merges cookie attribute objects, and it copies properties from each source object onto a target with a for...in loop and plain property assignment:

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 library kept the for...in loop for compatibility with Internet Explorer 10 and 11, which do not support Object.assign() or Object.getOwnPropertyNames() in the way the code needs. That constraint directly contributed to the bug, because modern property copying with Object.keys() or Object.assign() would neither enumerate __proto__ nor trigger the setter.

When JSON.parse produces the source object, 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, which replaces the target's prototype with the attacker-supplied value.

Object.prototype itself is never modified. The merged attributes object, though, 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(). assign() merges this object with for...in, which fires the __proto__ setter and pollutes the prototype of the merged attributes object.

  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 is an app that fetches its 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 (through 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"}}

js-cookie then emits a Set-Cookie string with attacker-controlled domain, secure, samesite, expires, and path values, bypassing the cookie security constraints the developer intended.

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 before the 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 the network
Attack ComplexityLow (AC:L)No special conditions required
Privileges RequiredNone (PR:N)No authentication needed
User InteractionNone (UI:N)Triggers automatically when the vulnerable code path runs
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 how much cookie attribute manipulation allows. An attacker who can set domain to a host they control, 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 shows the attacker-controlled attributes (domain=evil.com, secure=false, samesite=None, expires=-1) injected into the Set-Cookie string, even though the developer never specified them.

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

Patch Information

Maintainer Klaus Hartl (carhartl) fixed the vulnerability 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] } }

The guard skips the iteration whenever key equals '__proto__', so the dangerous setter never fires and the target object's prototype chain stays intact. The fix is minimal and targeted, and it preserves backward compatibility, including with IE 10/11. The commit message names IE 10/11 support as a reason the codebase still uses for...in instead of modern alternatives like Object.keys() or Object.assign().

The GitHub Security Advisory originally suggested a more defensive patch. It also filtered the 'constructor' and 'prototype' keys, and it replaced plain assignment with Object.defineProperty() so that no setter would fire at all. The maintainer chose the narrower fix that targets only '__proto__', since that is the only key whose plain assignment triggers the prototype setter in practice.

A matching 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' ) } )

The test reproduces the attack. It builds an object with JSON.parse from a __proto__ payload, passes it to Cookies.set(), and checks that the resulting cookie string contains only the default path=/ attribute and none of the attacker-injected keys.

The release notes for v3.0.7 name CVE-2026-46625 directly, so downstream consumers can easily 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 has 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 is the first publicly disclosed security vulnerability for the js-cookie v3.x line. Its predecessor, jquery-cookie, was retired and superseded by js-cookie, and has no known, publicly tracked CVE history of its own.

The repository includes a SECURITY.md file with guidance on reporting vulnerabilities, which indicates a responsible disclosure process is in place. The advisory was published and the patch shipped on the same day (May 16, 2026), a rapid response for an open-source project maintained by a small team.

Prototype pollution keeps turning up in major JavaScript libraries. Similar flaws have been disclosed in tough-cookie (CVE-2023-26136) and lodash (CVE-2026-2950). Both involve __proto__ manipulation through unsafe merge or assign operations, and both share the same CWE-1321 classification. In April 2026, Adobe also released emergency patches after confirmed active exploitation of a critical prototype pollution flaw.

References

Detect & fix
what others miss

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