diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 1fe83521..c3063bc4 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -506,9 +506,9 @@ ] }, "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.2.tgz", + "integrity": "sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw==", "dev": true, "license": "MIT", "engines": { diff --git a/node_modules/basic-ftp/README.md b/node_modules/basic-ftp/README.md index 7203ee62..7306b8cd 100644 --- a/node_modules/basic-ftp/README.md +++ b/node_modules/basic-ftp/README.md @@ -65,9 +65,11 @@ client.ftp.verbose = true ## Client API -`new Client(timeout = 30000)` +`new Client(timeout, options)` -Create a client instance. Configure it with a timeout in milliseconds that will be used for any connection made. Use 0 to disable timeouts, default is 30 seconds. +Create a client instance. Configure it with a timeout in milliseconds that will be used for any connection made. Use 0 to disable timeouts, default is 30 seconds. Options are: + +- `allowSeparateTransferHost (boolean)`, the FTP spec makes it possible for a server to tell the client to use a different IP address for file transfers than for the initial control connection. Today, this feature is very rarely used. Still, the default for this is set to `true` for backwards-compatibility reasons. If you experience any issues with NAT traversal in local networks or want to provide more security and prevent FTP bounce attacks, set this to `false`. `close()` diff --git a/node_modules/basic-ftp/dist/Client.d.ts b/node_modules/basic-ftp/dist/Client.d.ts index c91cce08..549c8262 100644 --- a/node_modules/basic-ftp/dist/Client.d.ts +++ b/node_modules/basic-ftp/dist/Client.d.ts @@ -1,5 +1,3 @@ -/// -/// import { Readable, Writable } from "stream"; import { ConnectionOptions as TLSConnectionOptions } from "tls"; import { FileInfo } from "./FileInfo"; @@ -30,6 +28,9 @@ export interface UploadOptions { /** Final byte position to include in upload from the local file. */ localEndInclusive?: number; } +export interface ClientOptions { + allowSeparateTransferHost: boolean; +} /** * High-level API to interact with an FTP server. */ @@ -46,7 +47,7 @@ export declare class Client { * * @param timeout Timeout in milliseconds, use 0 for no timeout. Optional, default is 30 seconds. */ - constructor(timeout?: number); + constructor(timeout?: number, options?: ClientOptions); /** * Close the client and all open socket connections. * diff --git a/node_modules/basic-ftp/dist/Client.js b/node_modules/basic-ftp/dist/Client.js index d91aa92c..0670c143 100644 --- a/node_modules/basic-ftp/dist/Client.js +++ b/node_modules/basic-ftp/dist/Client.js @@ -20,6 +20,9 @@ const fsStat = (0, util_1.promisify)(fs_1.stat); const fsOpen = (0, util_1.promisify)(fs_1.open); const fsClose = (0, util_1.promisify)(fs_1.close); const fsUnlink = (0, util_1.promisify)(fs_1.unlink); +const defaultClientOptions = { + allowSeparateTransferHost: true +}; const LIST_COMMANDS_DEFAULT = () => ["LIST -a", "LIST"]; const LIST_COMMANDS_MLSD = () => ["MLSD", "LIST -a", "LIST"]; /** @@ -31,10 +34,13 @@ class Client { * * @param timeout Timeout in milliseconds, use 0 for no timeout. Optional, default is 30 seconds. */ - constructor(timeout = 30000) { + constructor(timeout = 30000, options = defaultClientOptions) { this.availableListCommands = LIST_COMMANDS_DEFAULT(); this.ftp = new FtpContext_1.FTPContext(timeout); - this.prepareTransfer = this._enterFirstCompatibleMode([transfer_1.enterPassiveModeIPv6, transfer_1.enterPassiveModeIPv4]); + this.prepareTransfer = this._enterFirstCompatibleMode([ + transfer_1.enterPassiveModeIPv6, + options.allowSeparateTransferHost ? transfer_1.enterPassiveModeIPv4 : transfer_1.enterPassiveModeIPv4_forceControlHostIP + ]); this.parseList = parseList_1.parseList; this._progressTracker = new ProgressTracker_1.ProgressTracker(); } @@ -617,6 +623,12 @@ class Client { async _downloadFromWorkingDir(localDirPath) { await ensureLocalDirectory(localDirPath); for (const file of await this.list()) { + const hasInvalidName = !file.name || (0, path_1.basename)(file.name) !== file.name; + if (hasInvalidName) { + const safeName = JSON.stringify(file.name); + this.ftp.log(`Invalid filename from server listing, will skip file. (${safeName})`); + continue; + } const localPath = (0, path_1.join)(localDirPath, file.name); if (file.isDirectory) { await this.cd(file.name); @@ -697,7 +709,7 @@ class Client { try { const res = await strategy(ftp); ftp.log("Optimal transfer strategy found."); - this.prepareTransfer = strategy; // eslint-disable-line require-atomic-updates + this.prepareTransfer = strategy; return res; } catch (err) { @@ -755,7 +767,7 @@ async function ensureLocalDirectory(path) { try { await fsStat(path); } - catch (err) { + catch (_a) { await fsMkDir(path, { recursive: true }); } } @@ -763,7 +775,7 @@ async function ignoreError(func) { try { return await func(); } - catch (err) { + catch (_a) { // Ignore return undefined; } diff --git a/node_modules/basic-ftp/dist/FtpContext.d.ts b/node_modules/basic-ftp/dist/FtpContext.d.ts index 49c7d4df..29c41caf 100644 --- a/node_modules/basic-ftp/dist/FtpContext.d.ts +++ b/node_modules/basic-ftp/dist/FtpContext.d.ts @@ -1,6 +1,3 @@ -/// -/// -/// import { Socket } from "net"; import { ConnectionOptions as TLSConnectionOptions, TLSSocket } from "tls"; import { StringEncoding } from "./StringEncoding"; diff --git a/node_modules/basic-ftp/dist/FtpContext.js b/node_modules/basic-ftp/dist/FtpContext.js index a272ca45..e6d593a7 100644 --- a/node_modules/basic-ftp/dist/FtpContext.js +++ b/node_modules/basic-ftp/dist/FtpContext.js @@ -175,6 +175,10 @@ class FTPContext { * Send an FTP command without waiting for or handling the result. */ send(command) { + // Reject control character injection attempts. + if (/[\r\n\0]/.test(command)) { + throw new Error(`Invalid command: Contains control characters. (${command})`); + } const containsPassword = command.startsWith("PASS"); const message = containsPassword ? "> PASS ###" : `> ${command}`; this.log(message); diff --git a/node_modules/basic-ftp/dist/ProgressTracker.d.ts b/node_modules/basic-ftp/dist/ProgressTracker.d.ts index fc290301..8dd4e9e9 100644 --- a/node_modules/basic-ftp/dist/ProgressTracker.d.ts +++ b/node_modules/basic-ftp/dist/ProgressTracker.d.ts @@ -1,4 +1,3 @@ -/// import { Socket } from "net"; export type ProgressType = "upload" | "download" | "list"; /** diff --git a/node_modules/basic-ftp/dist/StringWriter.d.ts b/node_modules/basic-ftp/dist/StringWriter.d.ts index a9f85fa0..d12f8782 100644 --- a/node_modules/basic-ftp/dist/StringWriter.d.ts +++ b/node_modules/basic-ftp/dist/StringWriter.d.ts @@ -1,9 +1,7 @@ -/// -/// import { Writable } from "stream"; import { StringEncoding } from "./StringEncoding"; export declare class StringWriter extends Writable { - protected buf: Buffer; + protected buf: Buffer; _write(chunk: Buffer | string | any, _: string, callback: (error: Error | null) => void): void; getText(encoding: StringEncoding): string; } diff --git a/node_modules/basic-ftp/dist/netUtils.d.ts b/node_modules/basic-ftp/dist/netUtils.d.ts index 186cc17a..38e6c88e 100644 --- a/node_modules/basic-ftp/dist/netUtils.d.ts +++ b/node_modules/basic-ftp/dist/netUtils.d.ts @@ -1,5 +1,3 @@ -/// -/// import { Socket } from "net"; import { ConnectionOptions, TLSSocket } from "tls"; /** diff --git a/node_modules/basic-ftp/dist/netUtils.js b/node_modules/basic-ftp/dist/netUtils.js index 4a552fe9..8400226c 100644 --- a/node_modules/basic-ftp/dist/netUtils.js +++ b/node_modules/basic-ftp/dist/netUtils.js @@ -1,6 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.ipIsPrivateV4Address = exports.upgradeSocket = exports.describeAddress = exports.describeTLS = void 0; +exports.describeTLS = describeTLS; +exports.describeAddress = describeAddress; +exports.upgradeSocket = upgradeSocket; +exports.ipIsPrivateV4Address = ipIsPrivateV4Address; const tls_1 = require("tls"); /** * Returns a string describing the encryption on a given socket instance. @@ -12,7 +15,6 @@ function describeTLS(socket) { } return "No encryption"; } -exports.describeTLS = describeTLS; /** * Returns a string describing the remote address of a socket. */ @@ -22,7 +24,6 @@ function describeAddress(socket) { } return `${socket.remoteAddress}:${socket.remotePort}`; } -exports.describeAddress = describeAddress; /** * Upgrade a socket connection with TLS. */ @@ -46,7 +47,6 @@ function upgradeSocket(socket, options) { }); }); } -exports.upgradeSocket = upgradeSocket; /** * Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3. * This will handle IPv4-mapped IPv6 addresses correctly but return false for all other IPv6 addresses. @@ -64,4 +64,3 @@ function ipIsPrivateV4Address(ip = "") { || (octets[0] === 192 && octets[1] === 168) // 192.168.0.0 - 192.168.255.255 || ip === "127.0.0.1"; } -exports.ipIsPrivateV4Address = ipIsPrivateV4Address; diff --git a/node_modules/basic-ftp/dist/parseControlResponse.js b/node_modules/basic-ftp/dist/parseControlResponse.js index 9779a5b9..dc8f1acc 100644 --- a/node_modules/basic-ftp/dist/parseControlResponse.js +++ b/node_modules/basic-ftp/dist/parseControlResponse.js @@ -1,6 +1,10 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.positiveIntermediate = exports.positiveCompletion = exports.isMultiline = exports.isSingleLine = exports.parseControlResponse = void 0; +exports.parseControlResponse = parseControlResponse; +exports.isSingleLine = isSingleLine; +exports.isMultiline = isMultiline; +exports.positiveCompletion = positiveCompletion; +exports.positiveIntermediate = positiveIntermediate; const LF = "\n"; /** * Parse an FTP control response as a collection of messages. A message is a complete @@ -39,29 +43,24 @@ function parseControlResponse(text) { const rest = tokenRegex ? lines.slice(startAt).join(LF) + LF : ""; return { messages, rest }; } -exports.parseControlResponse = parseControlResponse; function isSingleLine(line) { return /^\d\d\d(?:$| )/.test(line); } -exports.isSingleLine = isSingleLine; function isMultiline(line) { return /^\d\d\d-/.test(line); } -exports.isMultiline = isMultiline; /** * Return true if an FTP return code describes a positive completion. */ function positiveCompletion(code) { return code >= 200 && code < 300; } -exports.positiveCompletion = positiveCompletion; /** * Return true if an FTP return code describes a positive intermediate response. */ function positiveIntermediate(code) { return code >= 300 && code < 400; } -exports.positiveIntermediate = positiveIntermediate; function isNotBlank(str) { return str.trim() !== ""; } diff --git a/node_modules/basic-ftp/dist/parseList.js b/node_modules/basic-ftp/dist/parseList.js index ee71287f..6f8396fb 100644 --- a/node_modules/basic-ftp/dist/parseList.js +++ b/node_modules/basic-ftp/dist/parseList.js @@ -15,15 +15,25 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( }) : function(o, v) { o["default"] = v; }); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseList = void 0; +exports.parseList = parseList; const dosParser = __importStar(require("./parseListDOS")); const unixParser = __importStar(require("./parseListUnix")); const mlsdParser = __importStar(require("./parseListMLSD")); @@ -67,4 +77,3 @@ function parseList(rawList) { .filter((info) => info !== undefined); return parser.transformList(files); } -exports.parseList = parseList; diff --git a/node_modules/basic-ftp/dist/parseListDOS.js b/node_modules/basic-ftp/dist/parseListDOS.js index 15c0bc1a..d878b099 100644 --- a/node_modules/basic-ftp/dist/parseListDOS.js +++ b/node_modules/basic-ftp/dist/parseListDOS.js @@ -1,6 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.transformList = exports.parseLine = exports.testLine = void 0; +exports.testLine = testLine; +exports.parseLine = parseLine; +exports.transformList = transformList; const FileInfo_1 = require("./FileInfo"); /** * This parser is based on the FTP client library source code in Apache Commons Net provided @@ -20,7 +22,6 @@ const RE_LINE = new RegExp("(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma| function testLine(line) { return /^\d{2}/.test(line) && RE_LINE.test(line); } -exports.testLine = testLine; /** * Parse a single line of a DOS-style directory listing. */ @@ -46,8 +47,6 @@ function parseLine(line) { file.rawModifiedAt = groups[1] + " " + groups[2]; return file; } -exports.parseLine = parseLine; function transformList(files) { return files; } -exports.transformList = transformList; diff --git a/node_modules/basic-ftp/dist/parseListMLSD.js b/node_modules/basic-ftp/dist/parseListMLSD.js index 4085a33b..afa31232 100644 --- a/node_modules/basic-ftp/dist/parseListMLSD.js +++ b/node_modules/basic-ftp/dist/parseListMLSD.js @@ -1,6 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseMLSxDate = exports.transformList = exports.parseLine = exports.testLine = void 0; +exports.testLine = testLine; +exports.parseLine = parseLine; +exports.transformList = transformList; +exports.parseMLSxDate = parseMLSxDate; const FileInfo_1 = require("./FileInfo"); function parseSize(value, info) { info.size = parseInt(value, 10); @@ -113,7 +116,6 @@ function splitStringOnce(str, delimiter) { function testLine(line) { return /^\S+=\S+;/.test(line) || line.startsWith(" "); } -exports.testLine = testLine; /** * Parse single line as MLSD listing, see specification at https://tools.ietf.org/html/rfc3659#section-7. */ @@ -140,7 +142,6 @@ function parseLine(line) { } return info; } -exports.parseLine = parseLine; function transformList(files) { // Create a map of all files that are not symbolic links by their unique ID const nonLinksByID = new Map(); @@ -168,7 +169,6 @@ function transformList(files) { } return resolvedFiles; } -exports.transformList = transformList; /** * Parse date as specified in https://tools.ietf.org/html/rfc3659#section-2.3. * @@ -185,4 +185,3 @@ function parseMLSxDate(fact) { +fact.slice(15, 18) // Milliseconds )); } -exports.parseMLSxDate = parseMLSxDate; diff --git a/node_modules/basic-ftp/dist/parseListUnix.js b/node_modules/basic-ftp/dist/parseListUnix.js index 5d5a804d..7bf53501 100644 --- a/node_modules/basic-ftp/dist/parseListUnix.js +++ b/node_modules/basic-ftp/dist/parseListUnix.js @@ -1,6 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.transformList = exports.parseLine = exports.testLine = void 0; +exports.testLine = testLine; +exports.parseLine = parseLine; +exports.transformList = transformList; const FileInfo_1 = require("./FileInfo"); const JA_MONTH = "\u6708"; const JA_DAY = "\u65e5"; @@ -77,7 +79,6 @@ const RE_LINE = new RegExp("([bcdelfmpSs-])" // file type function testLine(line) { return RE_LINE.test(line); } -exports.testLine = testLine; /** * Parse a single line of a Unix-style directory listing. */ @@ -135,11 +136,9 @@ function parseLine(line) { } return file; } -exports.parseLine = parseLine; function transformList(files) { return files; } -exports.transformList = transformList; function parseMode(r, w, x) { let value = 0; if (r !== "-") { diff --git a/node_modules/basic-ftp/dist/transfer.d.ts b/node_modules/basic-ftp/dist/transfer.d.ts index 4ec447dd..4dbd3b54 100644 --- a/node_modules/basic-ftp/dist/transfer.d.ts +++ b/node_modules/basic-ftp/dist/transfer.d.ts @@ -1,4 +1,3 @@ -/// import { Writable, Readable } from "stream"; import { FTPContext, FTPResponse } from "./FtpContext"; import { ProgressTracker, ProgressType } from "./ProgressTracker"; @@ -15,6 +14,12 @@ export declare function parseEpsvResponse(message: string): number; * Prepare a data socket using passive mode over IPv4. */ export declare function enterPassiveModeIPv4(ftp: FTPContext): Promise; +/** + * Prepare a data socket using passive mode over IPv4. Ignore the IP provided by the PASV response, + * and use the control host IP. This is the same behaviour as with the more modern variant EPSV. Use + * this to fix issues around NAT or provide more security by preventing FTP bounce attacks. + */ +export declare function enterPassiveModeIPv4_forceControlHostIP(ftp: FTPContext): Promise; /** * Parse a PASV response. */ diff --git a/node_modules/basic-ftp/dist/transfer.js b/node_modules/basic-ftp/dist/transfer.js index 1d8379db..6a12b213 100644 --- a/node_modules/basic-ftp/dist/transfer.js +++ b/node_modules/basic-ftp/dist/transfer.js @@ -1,6 +1,13 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.downloadTo = exports.uploadFrom = exports.connectForPassiveTransfer = exports.parsePasvResponse = exports.enterPassiveModeIPv4 = exports.parseEpsvResponse = exports.enterPassiveModeIPv6 = void 0; +exports.enterPassiveModeIPv6 = enterPassiveModeIPv6; +exports.parseEpsvResponse = parseEpsvResponse; +exports.enterPassiveModeIPv4 = enterPassiveModeIPv4; +exports.enterPassiveModeIPv4_forceControlHostIP = enterPassiveModeIPv4_forceControlHostIP; +exports.parsePasvResponse = parsePasvResponse; +exports.connectForPassiveTransfer = connectForPassiveTransfer; +exports.uploadFrom = uploadFrom; +exports.downloadTo = downloadTo; const netUtils_1 = require("./netUtils"); const stream_1 = require("stream"); const tls_1 = require("tls"); @@ -21,7 +28,6 @@ async function enterPassiveModeIPv6(ftp) { await connectForPassiveTransfer(controlHost, port, ftp); return res; } -exports.enterPassiveModeIPv6 = enterPassiveModeIPv6; /** * Parse an EPSV response. Returns only the port as in EPSV the host of the control connection is used. */ @@ -38,7 +44,6 @@ function parseEpsvResponse(message) { } return port; } -exports.parseEpsvResponse = parseEpsvResponse; /** * Prepare a data socket using passive mode over IPv4. */ @@ -59,7 +64,24 @@ async function enterPassiveModeIPv4(ftp) { await connectForPassiveTransfer(target.host, target.port, ftp); return res; } -exports.enterPassiveModeIPv4 = enterPassiveModeIPv4; +/** + * Prepare a data socket using passive mode over IPv4. Ignore the IP provided by the PASV response, + * and use the control host IP. This is the same behaviour as with the more modern variant EPSV. Use + * this to fix issues around NAT or provide more security by preventing FTP bounce attacks. + */ +async function enterPassiveModeIPv4_forceControlHostIP(ftp) { + const res = await ftp.request("PASV"); + const target = parsePasvResponse(res.message); + if (!target) { + throw new Error("Can't parse PASV response: " + res.message); + } + const controlHost = ftp.socket.remoteAddress; + if (controlHost === undefined) { + throw new Error("Control socket is disconnected, can't get remote address."); + } + await connectForPassiveTransfer(controlHost, target.port, ftp); + return res; +} /** * Parse a PASV response. */ @@ -74,7 +96,6 @@ function parsePasvResponse(message) { port: (parseInt(groups[2], 10) & 255) * 256 + (parseInt(groups[3], 10) & 255) }; } -exports.parsePasvResponse = parsePasvResponse; function connectForPassiveTransfer(host, port, ftp) { return new Promise((resolve, reject) => { let socket = ftp._newSocket(); @@ -116,7 +137,6 @@ function connectForPassiveTransfer(host, port, ftp) { }); }); } -exports.connectForPassiveTransfer = connectForPassiveTransfer; /** * Helps resolving/rejecting transfers. * @@ -241,7 +261,6 @@ function uploadFrom(source, config) { // Ignore all other positive preliminary response codes (< 200) }); } -exports.uploadFrom = uploadFrom; function downloadTo(destination, config) { if (!config.ftp.dataSocket) { throw new Error("Download will be initiated but no data connection is available."); @@ -280,7 +299,6 @@ function downloadTo(destination, config) { // Ignore all other positive preliminary response codes (< 200) }); } -exports.downloadTo = downloadTo; /** * Calls a function immediately if a condition is met or subscribes to an event and calls * it once the event is emitted. diff --git a/node_modules/basic-ftp/package.json b/node_modules/basic-ftp/package.json index 00bde469..daff0df0 100644 --- a/node_modules/basic-ftp/package.json +++ b/node_modules/basic-ftp/package.json @@ -1,6 +1,6 @@ { "name": "basic-ftp", - "version": "5.0.5", + "version": "5.2.2", "description": "FTP client for Node.js, supports FTPS over TLS, IPv6, Async/Await, and Typescript.", "main": "dist/index", "types": "dist/index", @@ -38,12 +38,15 @@ "node": ">=10.0.0" }, "devDependencies": { - "@types/mocha": "10.0.6", - "@types/node": "20.10.4", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", - "eslint": "8.55.0", - "mocha": "10.2.0", - "typescript": "5.3.3" + "@eslint/eslintrc": "3.3.5", + "@eslint/js": "10.0.1", + "@types/mocha": "10.0.10", + "@types/node": "25.5.2", + "@typescript-eslint/eslint-plugin": "8.58.1", + "@typescript-eslint/parser": "8.58.1", + "eslint": "10.2.0", + "globals": "17.4.0", + "mocha": "11.7.5", + "typescript": "5.9.3" } } diff --git a/package-lock.json b/package-lock.json index 54c36580..df668932 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "Chanjo-web-TestingAutomation", "dependencies": { "nodemailer": "^6.9.13" }, @@ -517,9 +516,9 @@ ] }, "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.2.tgz", + "integrity": "sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw==", "dev": true, "license": "MIT", "engines": {