GitHub Security Advisory: GHSA-rpmf-866q-6p89
Package: basic-ftp (NPM)
Severity: HIGH (CVSS 7.5)
CWE: CWE-400: Uncontrolled Resource Consumption, CWE-770: Allocation of Resources Without Limits or Throttling
Vulnerable versions: <= 5.3.0
Patched in: 5.3.1

Summary: basic-ftp allows a malicious FTP server to cause client-side denial of service via unbounded multiline control response buffering

Details:
## Summary

`basic-ftp` is vulnerable to client-side denial of service when parsing FTP control-channel multiline responses.

A malicious or compromised FTP server can send an unterminated multiline response during the initial FTP banner phase, before authentication. The client keeps appending attacker-controlled data into `FtpContext._partialResponse` and repeatedly reparses the accumulated buffer without enforcing a maximum control response size.

As a result, an application using `basic-ftp` can remain stuck in `connect()` while memory and CPU usage grow under attacker-controlled input. This can lead to process-level denial of service, container OOM kills, worker restarts, queue backlog, or service degradation in applications that automatically connect to FTP endpoints.

---

## Details

### Root cause

The root cause is that incomplete FTP multiline control responses are buffered without an upper bound.

`FtpContext` stores incomplete control-channel data in `_partialResponse`:


https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L63-L64


Incoming control-channel data is handled in `_onControlSocketData`. The implementation concatenates the previous incomplete response with the new chunk, parses the entire accumulated string, and stores `parsed.rest` back into `_partialResponse`:


https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/FtpContext.ts#L328-L340


The relevant flow is:


completeResponse = this._partialResponse + chunk
parsed = parseControlResponse(completeResponse)
this._partialResponse = parsed.rest


There is no maximum size check before concatenating, before parsing, or before storing `parsed.rest`.

The parser accepts incomplete multiline responses and returns the entire unterminated multiline group as `rest`:


https://github.com/patrickjuchli/basic-ftp/blob/50827c73ca6c1d786c97276e47be8a33d0f2277d/src/parseControlResponse.ts#L15-L43


If a server starts a multiline FTP response:


220-malicious banner starts


but never sends the terminating line:


220 ready


then `parseControlResponse()` treats the accumulated multiline data as incomplete and returns it as `rest`.

Because `_onControlSocketData()` feeds `_partialResponse + chunk` back into the parser on every new data event, the client repeatedly reparses a growing attacker-controlled buffer. This creates both memory growth and increasing parsing work.

### Why this is security-relevant

The vulnerable component is a client library. The attacker does not need to authenticate to the victim system and does not need valid FTP credentials.

The attack occurs automatically when an application using `basic-ftp` connects to a malicious or compromised FTP server. The malicious response is sent as the FTP server banner before login. No additional user interaction is required after the application initiates a normal FTP connection.

This is realistic for applications that use FTP for: