CVE-2020-15152 (JavaScript, published 2020-08-17T22:15Z)
Description: value: ftp-srv is an npm package which is a modern and extensible FTP server designed to be simple yet configurable. In ftp-srv before versions 2.19.6, 3.1.2, and 4.3.4 are vulnerable to Server-Side Request Forgery. The PORT command allows arbitrary IPs which can be used to cause the server to make a connection elsewhere. A possible workaround is blocking the PORT through the configuration. This issue is fixed in version2 2.19.6, 3.1.2, and 4.3.4. More information can be found on the linked advisory.
Severity: MEDIUM (CVSS v3 base score: 9.1)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Repository: https://github.com/autovance/ftp-srv
Files changed:
src/commands/registration/eprt.js
src/commands/registration/epsv.js
src/commands/registration/pasv.js
src/commands/registration/port.js
src/connector/active.js
src/connector/base.js
test/commands/registration/eprt.spec.js
test/commands/registration/epsv.spec.js
test/commands/registration/opts.spec.js
test/connector/active.spec.js
Commit message: fix: disallow PORT connections to alternate hosts

Ensure the data socket that the server connects to from the PORT command is the same IP as the current command socket.

* fix: add error handling to additional connection commands


Vulnerable code:
  handler: function ({command} = {}) {
    .then(() => this.reply(200));
  handler: function () {
      return this.reply(425);
      return this.reply(425);
      this.dataSocket.end(() => socket.destroy());
    return cmdFn()
    return cmdFn()
      expect(mockClient.reply.args[0][0]).to.equal(500);
  let mockConnection = {};
  before(() => {
    active = new ActiveConnector(mockConnection);
  });
  afterEach((done) => {
    server.close(done);
  it('destroys existing connection, then sets up a connection', function () {
    const destroyFnSpy = sandbox.spy(active.dataSocket, 'destroy');
      expect(destroyFnSpy.callCount).to.equal(1);
      expect(active.dataSocket).to.exist;

Fixed code:
  handler: function ({log, command} = {}) {
    .then(() => this.reply(200))
    .catch((err) => {
      log.error(err);
      return this.reply(err.code || 425, err.message);
    });
  handler: function ({log}) {
    })
    .catch((err) => {
      log.error(err);
      return this.reply(err.code || 425, err.message);
      return this.reply(err.code || 425, err.message);
      return this.reply(err.code || 425, err.message);
const ip = require('ip');
const {SocketError} = require('../errors');
      if (!ip.isEqual(this.connection.commandSocket.remoteAddress, host)) {
        throw new SocketError('The given address is not yours', 500);
      }

      this.dataSocket.end(() => socket && socket.destroy());
    return cmdFn({})
    return cmdFn({})
      expect(mockClient.reply.args[0][0]).to.equal(501);
  let mockConnection = {
    commandSocket: {
      remoteAddress: '::ffff:127.0.0.1'
    }
  };
    active = new ActiveConnector(mockConnection);

  afterEach(() => {
    server.close();
    active.end();

  it('rejects alternative host', function () {
    return active.setupConnection('123.45.67.89', PORT)
    .catch((err) => {
      expect(err.code).to.equal(500);
      expect(err.message).to.equal('The given address is not yours');
    })
    .finally(() => {
      expect(active.dataSocket).not.to.exist;
    });
  });
  it('destroys existing connection, then sets up a connection', function () {
      const destroyFnSpy = sandbox.spy(active.dataSocket, 'destroy');

      return active.setupConnection(host, PORT)
      .then(() => {
        expect(destroyFnSpy.callCount).to.equal(1);
        expect(active.dataSocket).to.exist;
      });