Zion Click Create
Book a call
← RESEARCH  ·  WRITEUP

CVE-2026-119189: node-forge X.509 pathLenConstraint Not Enforced Without keyUsage

node-forge 1.4.0 and earlier enforced a CA certificate's pathLenConstraint only when that certificate also had a keyUsage extension. A path-length-limited CA without keyUsage could mint subordinate CAs that verifyCertificateChain() accepted. Fixed in 1.4.1.

Summary

node-forge (digitalbazaar/forge) is a widely used JavaScript library for cryptography, X.509 certificates and TLS. In versions 1.4.0 and earlier, pki.verifyCertificateChain() enforced the basicConstraints pathLenConstraint of a CA certificate only when that certificate also had a keyUsage extension. A CA certificate that set pathLenConstraint but left out keyUsage had its path-length limit ignored.

A pathLenConstraint of 0 means a CA may issue end-entity certificates only, never another CA (RFC 5280 §4.2.1.9). In my test, two certificate chains that were identical except for keyUsage on a pathLenConstraint:0 intermediate gave opposite results: with keyUsage the chain was rejected, and without it a rogue sub-CA and the leaf certificate it issued were accepted.

Impact

The holder of a path-length-constrained CA key without keyUsage, or anyone it issued a CA certificate to, could create subordinate CAs whose certificates node-forge accepted as valid, including in forge.tls peer verification. That violates RFC 5280 §4.2.1.9 and §6.1.4.

Public web PKI CAs include keyUsage, so the practical exposure is mainly private, internal, device and mTLS PKIs, where pathLenConstraint is the control used to stop a delegated CA from delegating further.

Root cause

In lib/x509.js, the path-length check inside verifyCertificateChain() was guarded on the presence of keyUsage:

if(error === null && keyUsageExt !== null &&
  'pathLenConstraint' in bcExt) {
  var pathLen = depth - 1;
  if(pathLen > bcExt.pathLenConstraint) {
    error = { message: 'Certificate basicConstraints pathLenConstraint violated.', ... };
  }
}

RFC 5280 §6.1.4 processes max_path_length for every non-self-issued intermediate certificate, with no dependency on keyUsage. This is the same pattern as CVE-2026-33896 (CA checks gated on whether an extension is present). The 1.4.0 security release fixed that sibling bug but left this path in place.

Disclosure and credit

I reported this privately to Digital Bazaar by email on 2026-05-31. It was published as GHSA-h46v-rmh6-fq4f on 2026-10-27, and the CVE-2026-119189 record was published on 2026-10-29. The fix shipped in node-forge 1.4.1. If you run 1.4.0 or earlier, upgrade.

Credit is shared. The node-forge 1.4.1 changelog reads: “Reported by @offset, @eddieran, Zion Boggan, and SongWriterOP GT.” I reported it independently and am one of the four credited reporters.