Safeguard
Open Source

tough-cookie npm: Security Review and Safe Usage

A security review of the tough-cookie npm package, including the CVE-2023-26136 prototype pollution flaw, the version that fixes it, and how to use it safely.

Priya Mehta
Security Analyst
5 min read

The tough-cookie npm package is a widely used RFC 6265 cookie jar for Node.js HTTP clients, and its most important security item is CVE-2023-26136, a prototype pollution vulnerability fixed in version 4.1.3. If you depend on tough-cookie, directly or transitively, the single most useful thing you can do is confirm you are on 4.1.3 or later. The library itself is well-maintained and sound; the risk is almost entirely about running an old version.

What tough-cookie does

tough-cookie parses, stores, and serializes HTTP cookies according to RFC 6265. It is the cookie-handling layer beneath a large swath of the Node.js ecosystem, request libraries, test tools, and HTTP clients pull it in so they can maintain a session across requests. Because it sits underneath so many other packages, most projects that use it never installed it on purpose; it arrives as a transitive dependency.

Its CookieJar is the central object: it holds cookies keyed by domain and path and decides which cookies to send with a given request. That storage mechanism is where the notable vulnerability lived.

CVE-2023-26136: prototype pollution in CookieJar

CVE-2023-26136 is a prototype pollution vulnerability affecting tough-cookie versions before 4.1.3. It rated as a medium-severity issue.

Prototype pollution is a JavaScript-specific class of bug where an attacker manages to write to Object.prototype, the shared prototype every plain object inherits from. Once polluted, that injected property appears on every object in the program, which can corrupt application logic, cause denial of service, or in some setups escalate further.

In tough-cookie the flaw was reachable when using a CookieJar in rejectPublicSuffixes=false mode. The internal cookie store used a plain object (this.idx = {}) as a map keyed by attacker-influenceable values such as cookie domains, which let a crafted key reach the prototype chain.

The fix, shipped in 4.1.3, is a textbook prototype-pollution remediation: the store now uses Object.create(null), an object with no prototype at all, so there is no prototype chain for a malicious key to poison. Objects created that way cannot be used as a prototype-pollution vector because they inherit nothing.

How to check what you are running

Because tough-cookie is usually transitive, do not just look at your package.json. Ask npm what actually resolved:

npm ls tough-cookie

That prints every path in your dependency tree that pulls in tough-cookie and the version each resolved to. If any of them show a version below 4.1.3, you have exposure to remediate.

To pull the fix in, update the parent packages that depend on tough-cookie. If a stubborn transitive dependency pins an old version, an npm overrides block forces the resolution:

{
  "overrides": {
    "tough-cookie": ">=4.1.3"
  }
}

Use overrides carefully, they can force a version a parent package was not tested against, so run your tests afterward.

Why transitive dependencies make this hard

The tough-cookie case is a clean illustration of why software composition analysis exists. You did not choose tough-cookie, some HTTP library did, and that library may not have updated its own pin promptly. Reading your direct dependencies tells you nothing about which version resolved several levels down. An SCA tool such as Safeguard walks the full transitive tree, matches every resolved version against advisory databases, and flags exactly which dependency path is dragging in the vulnerable <4.1.3 range. That is the difference between "we use tough-cookie" and "package X at depth three resolves tough-cookie 4.0.0, which is vulnerable to CVE-2023-26136."

Safe usage beyond patching

Staying current is most of the job, but two habits help:

Prefer the default rejectPublicSuffixes behavior. The vulnerable path specifically involved disabling public-suffix rejection; there is rarely a good reason to turn it off, and leaving the default in place removes the risky configuration entirely.

Keep tough-cookie in your regular dependency-update cadence rather than treating it as invisible plumbing. A package this deep in the stack is exactly the kind that quietly falls behind. The Safeguard Academy covers setting update policies that reach transitive dependencies, not just the ones you named.

FAQ

What version of tough-cookie fixes CVE-2023-26136?

Version 4.1.3 fixes it. Any version before 4.1.3 is affected by the prototype pollution vulnerability. Upgrade to 4.1.3 or later.

How does the CVE-2023-26136 fix work?

The internal cookie store was changed from a plain object to one created with Object.create(null), which has no prototype chain. Without a prototype to poison, a maliciously crafted cookie key can no longer reach Object.prototype.

I never installed tough-cookie directly, why is it in my project?

It is a common transitive dependency of Node.js HTTP clients and test tools. Run npm ls tough-cookie to see which of your direct dependencies pulls it in and which version resolved.

Is tough-cookie safe to use today?

Yes, on a current version. It is well-maintained and RFC-compliant. The main risk is running an outdated version below 4.1.3; keep it patched and leave rejectPublicSuffixes at its default.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.