- Severity: HIGH
- CVE: CVE-2026-118779
- Ecosystems: npm
- Packages: node-forge
### 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…
Source: GitHub Security Advisories · 2026-10-30