Exponential ReDoS in devcert
Description
An exponential ReDoS vulnerability in the devcert npm package allows denial of service via crafted domain input to the certificateFor method.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
An exponential ReDoS vulnerability in the devcert npm package allows denial of service via crafted domain input to the certificateFor method.
Vulnerability
The devcert npm package (versions up to and including 1.2.0) contains an exponential ReDoS vulnerability in the VALID_DOMAIN regular expression used to validate domain names in the certificateFor method [1][2][4]. An attacker can supply a specially crafted string that causes the regex to exhibit exponential backtracking, leading to CPU exhaustion. The vulnerable regex was replaced in commit b0763215 with the is-valid-domain library [3].
Exploitation
An attacker needs to be able to supply arbitrary input to the certificateFor method, which may occur if an application passes user-controlled domain names to devcert. The attacker provides a string such as '0' + '000'.repeat(i) + '\\x00' to trigger exponential backtracking [4]. No authentication or special privileges are required beyond the ability to influence the domain argument.
Impact
Successful exploitation results in a denial of service (DoS) condition, where the Node.js process becomes unresponsive due to CPU exhaustion. The impact is limited to availability; no data confidentiality or integrity is compromised [2][4].
Mitigation
The vulnerability is fixed in devcert version 1.2.1, released on or around May 30, 2022 [3][4]. Users should upgrade to version 1.2.1 or later. No workarounds are documented; the fix replaces the vulnerable regex with the is-valid-domain library [3]. The CVE is not listed in CISA's Known Exploited Vulnerabilities catalog as of the publication date.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
devcertnpm | < 1.2.1 | 1.2.1 |
Affected products
2- devcert/devcertv5Range: unspecified
Patches
1b0763215f668switch from vulnerable VALID_DOMAIN regex to is-valid-domain lib (#79)
4 files changed · +35 −11
package.json+2 −2 modified@@ -45,13 +45,13 @@ "eol": "^0.9.1", "get-port": "^3.2.0", "glob": "^7.1.2", + "is-valid-domain": "^0.1.6", "lodash": "^4.17.4", "mkdirp": "^0.5.1", "password-prompt": "^1.0.4", "rimraf": "^2.6.2", "sudo-prompt": "^8.2.0", "tmp": "^0.0.33", "tslib": "^1.10.0" - }, - "optionalDependencies": {} + } }
package-lock.json+31 −0 modified@@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "devcert", "version": "1.2.0", "license": "MIT", "dependencies": { @@ -23,6 +24,7 @@ "eol": "^0.9.1", "get-port": "^3.2.0", "glob": "^7.1.2", + "is-valid-domain": "^0.1.6", "lodash": "^4.17.4", "mkdirp": "^0.5.1", "password-prompt": "^1.0.4", @@ -1825,6 +1827,14 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "node_modules/is-valid-domain": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-valid-domain/-/is-valid-domain-0.1.6.tgz", + "integrity": "sha512-ZKtq737eFkZr71At8NxOFcP9O1K89gW3DkdrGMpp1upr/ueWjj+Weh4l9AI4rN0Gt8W2M1w7jrG2b/Yv83Ljpg==", + "dependencies": { + "punycode": "^2.1.1" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2404,6 +2414,14 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -4637,6 +4655,14 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "is-valid-domain": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-valid-domain/-/is-valid-domain-0.1.6.tgz", + "integrity": "sha512-ZKtq737eFkZr71At8NxOFcP9O1K89gW3DkdrGMpp1upr/ueWjj+Weh4l9AI4rN0Gt8W2M1w7jrG2b/Yv83Ljpg==", + "requires": { + "punycode": "^2.1.1" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -5097,6 +5123,11 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
src/constants.ts+0 −3 modified@@ -6,9 +6,6 @@ import applicationConfigPath = require('application-config-path'); import eol from 'eol'; import {mktmp, numericHash} from './utils'; -export const VALID_IP = /(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}/; -export const VALID_DOMAIN = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.?)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i; - // Platform shortcuts export const isMac = process.platform === 'darwin'; export const isLinux = process.platform === 'linux';
src/index.ts+2 −6 modified@@ -11,13 +11,12 @@ import { domainsDir, rootCAKeyPath, rootCACertPath, - VALID_DOMAIN, - VALID_IP } from './constants'; import currentPlatform from './platforms'; import installCertificateAuthority, { ensureCACertReadable, uninstall } from './certificate-authority'; import generateDomainCertificate from './certificates'; import UI, { UserInterface } from './user-interface'; +import isValidDomain from 'is-valid-domain'; export { uninstall }; const debug = createDebug('devcert'); @@ -69,11 +68,8 @@ type IReturnData<O extends Options = {}> = (IDomainData) & (IReturnCa<O>) & (IRe */ export async function certificateFor<O extends Options>(requestedDomains: string | string[], options: O = {} as O): Promise<IReturnData<O>> { const domains = Array.isArray(requestedDomains) ? requestedDomains : [requestedDomains]; - if (domains.some((d) => VALID_IP.test(d))) { - throw new Error('IP addresses are not supported currently'); - } domains.forEach((domain) => { - if (!VALID_DOMAIN.test(domain)) { + if (!isValidDomain(domain, { subdomain: false, wildcard: false, allowUnicode: true, topLevel: false })) { throw new Error(`"${domain}" is not a valid domain name.`); } });
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-fp36-299x-pwmwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-1929ghsaADVISORY
- github.com/davewasmer/devcert/commit/b0763215f6683271d296fda98f7ef7bcd4a55977ghsaWEB
- research.jfrog.com/vulnerabilities/devcert-redos-xray-211352ghsaWEB
- research.jfrog.com/vulnerabilities/devcert-redos-xray-211352/mitrex_refsource_MISC
News mentions
0No linked articles in our index yet.