Skip to content
Heist/ feed
HighFix available

basic-ftp: Quadratic-time CPU denial of service in Client.list() Unix directory-listing parser (RE_LINE backtracking)

Type: advisorySource: OSV1 source record

## Summary `Client.list()` parses the server's directory listing with the Unix-style parser in `parseListUnix.js`. Its `RE_LINE` regex has two adjacent `(\S+(?:\s\S+)*)` groups (owner name, then group name) followed by a required numeric size group. When a line starts with a valid listing prefix but the tokens after it never satisfy the size and date fields, the engine backtracks over every way of splitting those tokens between the two groups before it can fail, so matching one line costs roughly O(n²) in the line's length. The server whose directory a client lists controls that listing, so it can return one line that pins the Node.js event loop for as long as it likes. `parseList()` picks the parser from the last non-blank line only, then runs it on every line, so a normal line placed last selects the Unix parser and a crafted line earlier hits the quadratic match. ## Proof of concept `npm i basic-ftp && node repro.js`: ```js const net = require("net"), ftp = require("basic-ftp"); const KB = Number(process.env.LINE_KB || 128); const payload = "-rw-r--r-- 1 " + "a ".repeat((KB * 1024 - 13) / 2) + "!"; const listing = payload + "\r\n-rw-r--r-- 1 owner group 42 Jan 1 2020 file.txt\r\n"; const server = net.createServer(c => { c.setEncoding("latin1"); c.write("220 ok\r\n"); let buf = ""; c.on("data", d => { buf += d; let i; while ((i = buf.indexOf("\r\n")) !== -1) { const cmd = buf.slice(0, i).toUpperCase(); buf = buf.slice(i + 2); if (cmd.startsWith("USER")) c.write("331 .\r\n")

Announced Source revision 1
Original source ↗

GHSA-c475-qrg2-pj4r

Primary source · Revision 1

Imported from structured advisory data. Source references identify upstream records or fields; verify affected versions and your deployed configuration.

Security context

Source reference: osv:GHSA-c475-qrg2-pj4r

basic-ftp

Source reference: /affected/0/package/name

Affected versions
Not stated in this record
Fixed versions
6.2.1

Source reference: /affected/0/ranges

Prerequisites
Not stated in this record

Structured affected-version data

Original package identities and version rules from this source.

[
  {
    "ranges": [
      {
        "type": "SEMVER",
        "events": [
          {
            "introduced": "0"
          },
          {
            "fixed": "6.2.1"
          }
        ]
      }
    ],
    "package": {
      "name": "basic-ftp",
      "purl": "pkg:npm/basic-ftp",
      "ecosystem": "npm"
    },
    "database_specific": {
      "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-c475-qrg2-pj4r/GHSA-c475-qrg2-pj4r.json",
      "last_known_affected_version_range": "<= 6.2.0"
    }
  }
]
Related CVEs
CVE-2026-102990

Source reference: /aliases

Publisher severity

high

Source reference: /database_specific/severity

Linked CVE context

These ratings describe the referenced vulnerabilities and products. They are not a severity rating for every application using this dependency.

Event
#60930 · Revision 1
Added to Heist Feed
Publisher date
Read the original announcement