<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>VulnFeed.org: node-forge</title><link>https://vulnfeed.org/</link><description>Every disclosure, as it lands. Critical-only feed available.</description><language>en</language><atom:link href="https://vulnfeed.org/feed/product/node-forge.xml" rel="self" type="application/rss+xml"/><item><title>CVE-2026-118779: Forge: Regular expression denial of service in PEM decoding (pem.decode)</title><link>https://vulnfeed.org/cve/CVE-2026-118779/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-118779/</guid><pubDate>Fri, 30 Oct 2026 23:02:25 +0000</pubDate><description>### Summary `pem.decode()`, which every node-forge API that parses PEM input uses, split messages with a regular expression that takes polynomial time on input without a matching END line, and parsed encapsulated headers with regular expressions that take quadratic time on long header lines. A remote attacker can block the Node.js event loop for seconds to minutes with a crafted input of a few tens of kilobytes. Fixed in node-forge 1.4.1. ### Details `pem.decode()` in `lib/pem.js` found PEM messages with this regular expression: /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g The optional headers group and the body group are adjacent, lazy, and share characters (whitespace and the base64 alphabet), and the match must end with an END line whose label equals the BEGIN label (`\1`). When no such END line follows, the engine tries every split of the text between the two groups, for every label split and at every BEGIN line in the input. The work grows polynomially with the input length (roughly cubic for repeated BEGIN/END lines with mismatched labels, quadratic for a BEGIN line followed by many newlines). The encapsulated header parsing in the same function had two smaller quadratic cases: trimming each header line with `/\s+$/`, and an unanchored header regex `([\x21-\x7e]+):...` that retries at every position of a long line without a colon. These are reachable when a message does match. All PEM entry points call `pem.decode()`, including `pki.certificateFromPem()`, `pki.privateKeyFromPem()`, `pki.publicKeyFromPem()`, `pki.certificationRequestFromPem()`, `pki.encryptedPrivateKeyFromPem()`, `pki.decryptRsaPrivateKey()` and `pkcs7.messageFromPem()`. The fix replaces the message regular expression with a scanner that indexes the END lines once and matches each BEGIN line in linear time, finding the same messages as before. The one difference: a label containing `-----` (`-----BEGIN A-----B-----`) was matche…</description></item><item><title>CVE-2026-118782: Forge: PKCS#12 MAC forgery and constant-key PBE via zero iteration count</title><link>https://vulnfeed.org/cve/CVE-2026-118782/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-118782/</guid><pubDate>Fri, 30 Oct 2026 23:02:05 +0000</pubDate><description>### Summary forge.pkcs12.pkcs12FromAsn1() accepts a PKCS#12 MAC iteration count of zero (or less) from the input, and the PKCS#12 key derivation function then returns a constant key that does not depend on the password or salt. An attacker who does not know the password can modify a PKCS#12/PFX file and compute a MAC that verifies with any password; PKCS#12 PBE-encrypted private keys prepared the same way decrypt with any password. Fixed in node-forge 1.4.1. ### Details `p12.pkcs12FromAsn1()` in `lib/pkcs12.js` reads the MAC iteration count (`MacData.iterations`) from the input and passes it to the PKCS#12 key derivation function `pki.pbe.generatePkcs12Key()` in `lib/pbe.js` (also exposed as `forge.pkcs12.generateKey()`) without checking that it is at least 1. The key derivation function (RFC 7292 Appendix B.2) runs its hash loop `iter` times. With an iteration count of 0 (or an empty or negative INTEGER), no hashing is done and the function returns the PKCS#12 diversifier ID byte repeated, for example `0x03` repeated 20 times for an HMAC-SHA1 MAC key. The result does not depend on the password or the salt, so anyone can compute a valid MAC over arbitrary PFX contents. The same key derivation function is used by `pki.pbe.getCipherForPKCS12PBE()` for the PKCS#12 password-based encryption schemes `pbeWithSHAAnd3-KeyTripleDES-CBC` and `pbewithSHAAnd40BitRC2-CBC`, whose `pkcs-12PbeParams` iteration count is also read from the input. With an iteration count of 0 or less, the encryption key and IV are constants (`0x01` and `0x02` repeated). This is reached from shrouded key bags and encrypted SafeContents in `pkcs12FromAsn1()`, and from `forge.pki.decryptPrivateKeyInfo()` (for example for `ENCRYPTED PRIVATE KEY` PEM input). RFC 7292 defines `MacData` with `iterations INTEGER DEFAULT 1`; an iteration count only makes sense as 1 or more (RFC 8018 uses `INTEGER (1..MAX)` for the same concept). PBES2 (PBKDF2) encrypted content is not affected. The fix rejects iteration count…</description></item><item><title>CVE-2026-119188: Forge: Denial of service in TLS record processing via a record interleaved with a fragmented handshake message</title><link>https://vulnfeed.org/cve/CVE-2026-119188/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-119188/</guid><pubDate>Fri, 30 Oct 2026 23:02:00 +0000</pubDate><description>### Summary A connection created with `forge.tls.createConnection()` throws an uncaught `TypeError` from `process()` when it receives a record of another content type while waiting for the rest of a handshake message that was split across records. A peer can trigger this with 18 bytes before the handshake, without authentication, which terminates Node.js processes that use the documented socket wiring. Fixed in node-forge 1.4.1. ### Details In `lib/tls.js`, `_readRecord()` handles a record that arrives while a handshake message split across records is incomplete. If the record has any other content type (change_cipher_spec, alert, application_data or heartbeat), forge correctly reports &quot;Invalid fragmented record.&quot; through `c.error()` with a fatal `unexpected_message` alert. That fatal error closes and resets the connection, which sets `c.record` to `null`. `_readRecord()` then continues with `c.record.ready = true` and throws `TypeError: Cannot set properties of null (setting &apos;ready&apos;)` out of `process()`. The code is unchanged since the first release. RFC 5246 section 6.2.1 allows a handshake message to be fragmented across records, and RFC 8446 section 5.1 forbids interleaving handshake messages with other record types. Failing the connection with `unexpected_message` is the intended behavior; the bug is the crash after the error. node-forge reports TLS protocol errors through the `error` callback, so applications do not normally wrap `process()` in `try`/`catch`. When `process()` is called from a Node.js socket `data` handler, as in the documented usage, the exception is uncaught and terminates the process. The fix returns from `_readRecord()` right after the fatal error. The connection is failed, the alert is sent and the `error` callback is called as before, and `process()` no longer throws. ### Patches Fixed in node-forge 1.4.1. The connection still fails with a fatal `unexpected_message` alert and the `error` callback, but `process()` no longer throws. ### Wo…</description></item><item><title>CVE-2026-119187: Forge: Denial of service in TLS record processing via call stack exhaustion</title><link>https://vulnfeed.org/cve/CVE-2026-119187/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-119187/</guid><pubDate>Fri, 30 Oct 2026 23:01:54 +0000</pubDate><description>### Summary Connections created with `forge.tls.createConnection()` processed received TLS records recursively, one stack level per record. A peer can send one chunk of thousands of small records, such as heartbeat records, which are accepted before the handshake, and `process()` throws `RangeError: Maximum call stack size exceeded`, which terminates Node.js processes that use the documented socket wiring. Fixed in node-forge 1.4.1. ### Details In `lib/tls.js`, `c.process()` reads one record and passes it to `_update()`, which calls the handler for the record type from `ctTable`. Every handler (`tls.handleHeartbeat`, `tls.handleApplicationData`, `tls.handleHandshake` for fragmented messages, `tls.handleHelloRequest` and the other handshake handlers) ends by calling `c.process()` again to continue with the next buffered record, or the next message in the same record. Each record therefore adds `process` → `_update` → handler to the call stack. In Node.js 24, about 2,300 eight-byte records (under 20 KB) in one chunk are enough for `process()` to throw `RangeError: Maximum call stack size exceeded`. `ctTable` maps heartbeat records to `tls.handleHeartbeat` in every client and server state, so heartbeat records are processed before the handshake without any extension negotiation. The attack needs no authentication and no valid handshake messages. Both server connections (attacked by a client) and client connections (attacked by the server they connect to) are affected. Other record types recurse the same way: a handshake message fragmented over many records, many HelloRequest messages in one record, or many application data records after the handshake. node-forge reports TLS protocol errors through the `error` callback, so applications do not normally wrap `process()` in `try`/`catch`. When `process()` is called from a Node.js socket `data` handler, as in the documented usage, the exception is uncaught and terminates the process. If the exception is caught, the connect…</description></item><item><title>CVE-2026-119190: Forge: X.509 basicConstraints bypass in verifyCertificateChain() via deprecated extension OIDs 2.5.29.10 and 2.5.29.13</title><link>https://vulnfeed.org/cve/CVE-2026-119190/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-119190/</guid><pubDate>Fri, 30 Oct 2026 23:01:46 +0000</pubDate><description>### Summary node-forge parses certificate extensions with the deprecated OIDs `2.5.29.10` and `2.5.29.13` as `basicConstraints`, and `forge.pki.verifyCertificateChain()` used the first extension with that name. A certificate with a forged deprecated-OID extension (`cA` TRUE) before its genuine `basicConstraints` extension (`cA` FALSE) is accepted as a CA certificate, and the same technique can hide a `pathLenConstraint`. Fixed in node-forge 1.4.1. ### Details `lib/oids.js` maps the deprecated certificate extension OIDs `2.5.29.10` and `2.5.29.13` to the name `basicConstraints`, the same name as the RFC 5280 extension `2.5.29.19`. `certificateExtensionFromAsn1()` in `lib/x509.js` decodes extensions by name, so these extensions are decoded into the same `cA` and `pathLenConstraint` fields. `cert.getExtension()` returns the first extension with a matching name, and `pki.verifyCertificateChain()` looked up `basicConstraints` by name. As a result, a certificate that contains a `2.5.29.10` (or `2.5.29.13`) extension with `cA` TRUE before its genuine `basicConstraints` extension with `cA` FALSE is accepted as a CA certificate. The same technique can hide a CA certificate&apos;s `pathLenConstraint`. A deprecated-OID extension on its own also satisfies the check added for GHSA-2328-f5f3-gj25 (CVE-2026-33896) that `basicConstraints` is present. RFC 5280 section 4.2.1.9 defines basicConstraints only as `id-ce 19` (`2.5.29.19`), and the path validation checks in section 6.1.4 apply to that extension. The fix makes chain verification look up basicConstraints by OID (`cert.getExtension({id: &apos;2.5.29.19&apos;})`). This covers both the `cA` and the `pathLenConstraint` checks. Certificate parsing, the OID table and `cert.getExtension(&apos;basicConstraints&apos;)` are unchanged. #### Patches Fixed in node-forge 1.4.1. Chain verification now uses only the `basicConstraints` extension with OID `2.5.29.19`. #### Workarounds None in node-forge itself. Applications that pass an `options.verify` callback to…</description></item></channel></rss>
