From f9cb83f24f795ad85e54a4a3a73b9dea90b23165 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 12:37:13 +0530 Subject: [PATCH 01/12] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index b59c616e8..448263456 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -39,8 +39,8 @@ path = "./lib/commons-io-2.18.0.jar" [[platform.java21.dependency]] groupId = "org.apache.commons" artifactId = "commons-lang3" -version = "3.17.0" -path = "./lib/commons-lang3-3.17.0.jar" +version = "3.18.0" +path = "./lib/commons-lang3-3.18.0.jar" [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" From cc10b1866789cda05e3092dad85863e5a22dad68 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 21:28:32 +0530 Subject: [PATCH 02/12] Fix improperly formatted FTP URL for jail break ftp servers --- ballerina/tests/client_endpoint_test.bal | 28 +++++++++++++++ .../io/ballerina/stdlib/ftp/util/FtpUtil.java | 35 ++++++++++++++----- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index 9ded6c685..9bf376de5 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -25,6 +25,7 @@ string nonFittingFilePath = "/home/in/test4.txt"; string newFilePath = "/home/in/test2.txt"; string appendFilePath = "tests/resources/datafiles/file1.txt"; string putFilePath = "tests/resources/datafiles/file2.txt"; +string relativePath = "rel-put.txt"; // Create the config to access anonymous mock FTP server ClientConfiguration anonConfig = { @@ -237,6 +238,33 @@ function testFtpUserDirIsRootTrue() returns error? { } } +@test:Config { + dependsOn: [testListFiles] +} +public function testPutRelativePath_userDirIsRootTrue() returns error? { + Error? putRes = (ftpUserHomeRootClientEp)->put(relativePath, "hello-jailed-rel"); + if putRes is Error { + test:assertFail("PUT(relative, no slash) failed on userDirIsRoot=true: " + putRes.message()); + } + + stream|Error getRes = (ftpUserHomeRootClientEp)->get(relativePath); + if getRes is stream { + test:assertTrue(check matchStreamContent(getRes, "hello-jailed-rel"), + msg = "Unexpected content from GET(relative) after PUT on userDirIsRoot=true"); + io:Error? closeErr = getRes.close(); + if closeErr is io:Error { + test:assertFail("Error closing relative GET stream: " + closeErr.message()); + } + } else { + test:assertFail("GET(relative) failed on userDirIsRoot=true: " + getRes.message()); + } + + Error? delRes = (ftpUserHomeRootClientEp)->delete(relativePath); + if delRes is Error { + log:printWarn("Cleanup delete failed for " + relativePath + ": " + delRes.message()); + } +} + @test:Config { dependsOn: [testPutCompressedFileContent] } diff --git a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java index e5072c8a3..1ae5cc0af 100644 --- a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java @@ -109,17 +109,36 @@ public static String createUrl(BMap config) throws BallerinaFtpException { return createUrl(protocol, host, port, username, password, filePath); } - private static String createUrl(String protocol, String host, int port, String username, String password, - String filePath) throws BallerinaFtpException { - String userInfo = username + ":" + password; - URI uri; + private static String createUrl(String protocolScheme, String serverHost, int serverPort, String credentialUsername, + String credentialPassword, String requestedPath) throws BallerinaFtpException { + final String userInfo = buildUserInfo(credentialUsername, credentialPassword); + final String normalizedPath = normalizeFtpPath(requestedPath); try { - uri = new URI(protocol, userInfo, host, port, filePath, null, null); + URI uri = new URI(protocolScheme, userInfo, serverHost, serverPort, normalizedPath, + null, null); + return uri.toString(); } catch (URISyntaxException e) { - throw new BallerinaFtpException("Error occurred while constructing a URI from host: " + host + - ", port: " + port + ", username: " + username + " and basePath: " + filePath + e.getMessage(), e); + throw new BallerinaFtpException("Error occurred while constructing a URI from host: " + serverHost + + ", port: " + serverPort + ", username: " + credentialUsername + " and basePath: " + + requestedPath + e.getMessage(), e); } - return uri.toString(); + } + + private static String buildUserInfo(String username, String password) { + if (username == null || username.isBlank()) { + return null; + } + return (password == null) ? username : username + ":" + password; + } + + private static String normalizeFtpPath(String rawPath) { + if (rawPath == null || rawPath.isEmpty()) { + return "/"; + } + if (rawPath.startsWith("/") || rawPath.startsWith("//")) { + return rawPath; + } + return "/" + rawPath; } public static Map getAuthMap(BMap config) { From 7cd068ec35a9f00a50a71b1baefaa6ffb0e5adc6 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 22:27:22 +0530 Subject: [PATCH 03/12] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/CompilerPlugin.toml | 2 +- ballerina/Dependencies.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index e7cf364b6..70c668b03 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "ftp" -version = "2.14.0" +version = "2.14.1" authors = ["Ballerina"] keywords = ["FTP", "SFTP", "remote file", "file transfer", "client", "service"] repository = "https://github.com/ballerina-platform/module-ballerina-ftp" @@ -45,5 +45,5 @@ path = "./lib/commons-lang3-3.18.0.jar" [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" artifactId = "ftp-native" -version = "2.14.0" -path = "../native/build/libs/ftp-native-2.14.0.jar" +version = "2.14.1" +path = "../native/build/libs/ftp-native-2.14.1-SNAPSHOT.jar" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 5b1c8fbd2..ee7df9870 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "ftp-compiler-plugin" class = "io.ballerina.stdlib.ftp.plugin.FtpCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/ftp-compiler-plugin-2.14.0.jar" +path = "../compiler-plugin/build/libs/ftp-compiler-plugin-2.14.1-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 61c04c3a9..8cb9d8cc2 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -10,7 +10,7 @@ distribution-version = "2201.12.0" [[package]] org = "ballerina" name = "ftp" -version = "2.14.0" +version = "2.14.1" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, From 257e3e8ccf5bdaac2d389593b6e23dca37882208 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 22:35:23 +0530 Subject: [PATCH 04/12] Add test coverage --- ballerina/tests/client_endpoint_test.bal | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index 9bf376de5..7c8ea6643 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -66,9 +66,26 @@ ClientConfiguration sftpConfig = { } }; +// Create the config to access mock SFTP server +ClientConfiguration sftpConfigUserDirRoot = { + protocol: SFTP, + host: "127.0.0.1", + port: 21213, + auth: { + credentials: {username: "wso2", password: "wso2123"}, + privateKey: { + path: "tests/resources/sftp.private.key", + password: "changeit" + }, + preferredMethods: [GSSAPI_WITH_MIC, PUBLICKEY, KEYBOARD_INTERACTIVE, PASSWORD] + }, + userDirIsRoot: true +}; + Client? anonClientEp = (); Client? clientEp = (); Client? sftpClientEp = (); +Client? sftpClientUserDirRootEp = (); Client? ftpUserHomeRootClientEp = (); Listener? callerListener = (); @@ -82,6 +99,7 @@ function initTestEnvironment() returns error? { anonClientEp = check new (anonConfig); clientEp = check new (config); sftpClientEp = check new (sftpConfig); + sftpClientUserDirRootEp = check new (sftpConfigUserDirRoot); ftpUserHomeRootClientEp = check new (ftpUserHomeRootConfig); callerListener = check new (callerListenerConfig); @@ -265,6 +283,33 @@ public function testPutRelativePath_userDirIsRootTrue() returns error? { } } +@test:Config { dependsOn: [testListFiles] } +public function testPutAbsoluteDoubleSlash_userDirIsRootFalse() returns error? { + string absPath = "//home/in/double-abs.txt"; + Error? putRes = (clientEp)->put(absPath, "hello-abs-double-slash"); + if putRes is Error { test:assertFail("PUT(//absolute) failed: " + putRes.message()); } + stream|Error getRes = check (clientEp)->get(absPath); + if getRes is stream { + test:assertTrue(check matchStreamContent(getRes, "hello-abs-double-slash")); + check getRes.close(); + } else { test:assertFail("GET(//absolute) failed: " + getRes.message()); } + check (clientEp)->delete(absPath); +} + +@test:Config { dependsOn: [testListFiles] } +public function testSftpUserDirIsRootTrue_RelativePutGet() returns error? { + stream bStream = ["hello-sftp-rel".toBytes().cloneReadOnly()].toStream(); + Error? putRes = (ftpUserHomeRootClientEp)->put("sftp-rel.txt", bStream); + if putRes is Error { test:assertFail("SFTP relative PUT failed: " + putRes.message()); } + stream|Error getRes = (ftpUserHomeRootClientEp)->get("sftp-rel.txt"); + if getRes is stream { + test:assertTrue(check matchStreamContent(getRes, "hello-sftp-rel")); + check getRes.close(); + } else { test:assertFail("SFTP relative GET failed: " + getRes.message()); } + check (ftpUserHomeRootClientEp)->delete("sftp-rel.txt"); +} + + @test:Config { dependsOn: [testPutCompressedFileContent] } From 223791cede378e943569f276e2adc56f1ea23ec3 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 22:36:58 +0530 Subject: [PATCH 05/12] Refactor comments --- ballerina/tests/client_endpoint_test.bal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index 7c8ea6643..4886e44dc 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -51,7 +51,7 @@ ClientConfiguration ftpUserHomeRootConfig = { userDirIsRoot: true }; -// Create the config to access mock SFTP server +// Create the config to access mock SFTP server with jailed home ClientConfiguration sftpConfig = { protocol: SFTP, host: "127.0.0.1", From a55ecfdd3c3b67a328096dd44a7cb0398ce1cd0f Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 22:37:53 +0530 Subject: [PATCH 06/12] Fix incorrect comments --- ballerina/tests/client_endpoint_test.bal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index 4886e44dc..a1476851b 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -51,7 +51,7 @@ ClientConfiguration ftpUserHomeRootConfig = { userDirIsRoot: true }; -// Create the config to access mock SFTP server with jailed home +// Create the config to access mock SFTP server ClientConfiguration sftpConfig = { protocol: SFTP, host: "127.0.0.1", @@ -66,7 +66,7 @@ ClientConfiguration sftpConfig = { } }; -// Create the config to access mock SFTP server +// Create the config to access mock SFTP server with jailed home ClientConfiguration sftpConfigUserDirRoot = { protocol: SFTP, host: "127.0.0.1", From 37614323ef5197320d8a431c3c37edf26f08ddcd Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Sep 2025 23:26:08 +0530 Subject: [PATCH 07/12] Fix typos --- ballerina/tests/client_endpoint_test.bal | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index a1476851b..1890eb017 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -299,14 +299,14 @@ public function testPutAbsoluteDoubleSlash_userDirIsRootFalse() returns error? { @test:Config { dependsOn: [testListFiles] } public function testSftpUserDirIsRootTrue_RelativePutGet() returns error? { stream bStream = ["hello-sftp-rel".toBytes().cloneReadOnly()].toStream(); - Error? putRes = (ftpUserHomeRootClientEp)->put("sftp-rel.txt", bStream); + Error? putRes = (sftpClientUserDirRootEp)->put("sftp-rel.txt", bStream); if putRes is Error { test:assertFail("SFTP relative PUT failed: " + putRes.message()); } - stream|Error getRes = (ftpUserHomeRootClientEp)->get("sftp-rel.txt"); + stream|Error getRes = (sftpClientUserDirRootEp)->get("sftp-rel.txt"); if getRes is stream { test:assertTrue(check matchStreamContent(getRes, "hello-sftp-rel")); check getRes.close(); } else { test:assertFail("SFTP relative GET failed: " + getRes.message()); } - check (ftpUserHomeRootClientEp)->delete("sftp-rel.txt"); + check (sftpClientUserDirRootEp)->delete("sftp-rel.txt"); } From a54270c1aa48623fbd8caef52e1be803e8094a68 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 24 Sep 2025 00:25:40 +0530 Subject: [PATCH 08/12] Refactor the url creation logic and tests --- ballerina/tests/client_endpoint_test.bal | 33 +++++++++++++++++-- .../io/ballerina/stdlib/ftp/util/FtpUtil.java | 23 +++++-------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/ballerina/tests/client_endpoint_test.bal b/ballerina/tests/client_endpoint_test.bal index 1890eb017..16a4a3615 100644 --- a/ballerina/tests/client_endpoint_test.bal +++ b/ballerina/tests/client_endpoint_test.bal @@ -26,6 +26,8 @@ string newFilePath = "/home/in/test2.txt"; string appendFilePath = "tests/resources/datafiles/file1.txt"; string putFilePath = "tests/resources/datafiles/file2.txt"; string relativePath = "rel-put.txt"; +string relativePathWithSlash = "/rel-path-slash-put.txt"; +string absPath = "//home/in/double-abs.txt"; // Create the config to access anonymous mock FTP server ClientConfiguration anonConfig = { @@ -283,12 +285,38 @@ public function testPutRelativePath_userDirIsRootTrue() returns error? { } } +@test:Config { + dependsOn: [testListFiles] +} +public function testPutRelativePathWithSlash_userDirIsRootTrue() returns error? { + Error? putRes = (ftpUserHomeRootClientEp)->put(relativePathWithSlash, "hello-jailed-rel-with-slash"); + if putRes is Error { + test:assertFail("PUT(relative, with slash) failed on userDirIsRoot=true: " + putRes.message()); + } + + stream|Error getRes = (ftpUserHomeRootClientEp)->get(relativePathWithSlash); + if getRes is stream { + test:assertTrue(check matchStreamContent(getRes, "hello-jailed-rel-with-slash"), + msg = "Unexpected content from GET(relative) after PUT on userDirIsRoot=true"); + io:Error? closeErr = getRes.close(); + if closeErr is io:Error { + test:assertFail("Error closing relative GET stream: " + closeErr.message()); + } + } else { + test:assertFail("GET(relative) failed on userDirIsRoot=true: " + getRes.message()); + } + + Error? delRes = (ftpUserHomeRootClientEp)->delete(relativePathWithSlash); + if delRes is Error { + log:printWarn("Cleanup delete failed for " + relativePathWithSlash + ": " + delRes.message()); + } +} + @test:Config { dependsOn: [testListFiles] } public function testPutAbsoluteDoubleSlash_userDirIsRootFalse() returns error? { - string absPath = "//home/in/double-abs.txt"; Error? putRes = (clientEp)->put(absPath, "hello-abs-double-slash"); if putRes is Error { test:assertFail("PUT(//absolute) failed: " + putRes.message()); } - stream|Error getRes = check (clientEp)->get(absPath); + stream|Error getRes = (clientEp)->get(absPath); if getRes is stream { test:assertTrue(check matchStreamContent(getRes, "hello-abs-double-slash")); check getRes.close(); @@ -309,7 +337,6 @@ public function testSftpUserDirIsRootTrue_RelativePutGet() returns error? { check (sftpClientUserDirRootEp)->delete("sftp-rel.txt"); } - @test:Config { dependsOn: [testPutCompressedFileContent] } diff --git a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java index 1ae5cc0af..ef51ca150 100644 --- a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java @@ -109,28 +109,21 @@ public static String createUrl(BMap config) throws BallerinaFtpException { return createUrl(protocol, host, port, username, password, filePath); } - private static String createUrl(String protocolScheme, String serverHost, int serverPort, String credentialUsername, - String credentialPassword, String requestedPath) throws BallerinaFtpException { - final String userInfo = buildUserInfo(credentialUsername, credentialPassword); - final String normalizedPath = normalizeFtpPath(requestedPath); + private static String createUrl(String protocol, String host, int port, String username, + String password, String filePath) throws BallerinaFtpException { + String userInfo = username + ":" + password; + final String normalizedPath = normalizeFtpPath(filePath); try { - URI uri = new URI(protocolScheme, userInfo, serverHost, serverPort, normalizedPath, + URI uri = new URI(protocol, userInfo, host, port, normalizedPath, null, null); return uri.toString(); } catch (URISyntaxException e) { - throw new BallerinaFtpException("Error occurred while constructing a URI from host: " + serverHost + - ", port: " + serverPort + ", username: " + credentialUsername + " and basePath: " + - requestedPath + e.getMessage(), e); + throw new BallerinaFtpException("Error occurred while constructing a URI from host: " + host + + ", port: " + port + ", username: " + username + " and basePath: " + + filePath + e.getMessage(), e); } } - private static String buildUserInfo(String username, String password) { - if (username == null || username.isBlank()) { - return null; - } - return (password == null) ? username : username + ":" + password; - } - private static String normalizeFtpPath(String rawPath) { if (rawPath == null || rawPath.isEmpty()) { return "/"; From 248bbf971988f3970e877930b2591cb577cd4104 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 24 Sep 2025 00:28:24 +0530 Subject: [PATCH 09/12] Remove unnecessary new lines --- .../java/io/ballerina/stdlib/ftp/util/FtpUtil.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java index ef51ca150..9d2758cab 100644 --- a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java @@ -109,18 +109,16 @@ public static String createUrl(BMap config) throws BallerinaFtpException { return createUrl(protocol, host, port, username, password, filePath); } - private static String createUrl(String protocol, String host, int port, String username, - String password, String filePath) throws BallerinaFtpException { + private static String createUrl(String protocol, String host, int port, String username, String password, + String filePath) throws BallerinaFtpException { String userInfo = username + ":" + password; final String normalizedPath = normalizeFtpPath(filePath); try { - URI uri = new URI(protocol, userInfo, host, port, normalizedPath, - null, null); + URI uri = new URI(protocol, userInfo, host, port, normalizedPath, null, null); return uri.toString(); } catch (URISyntaxException e) { throw new BallerinaFtpException("Error occurred while constructing a URI from host: " + host + - ", port: " + port + ", username: " + username + " and basePath: " + - filePath + e.getMessage(), e); + ", port: " + port + ", username: " + username + " and basePath: " + filePath + e.getMessage(), e); } } From dd69da1225c8534d36f9f86f80fa2c09b1453c63 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 24 Sep 2025 00:51:00 +0530 Subject: [PATCH 10/12] Update the changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 828576a78..290bcd830 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - [Fix the issue where the FTP listener stops working if the file name is the same](https://github.com/ballerina-platform/ballerina-library/issues/8035) +- [Fix the issue where the FTP URL is improperly formatted for the jail-break scnenarios](https://github.com/ballerina-platform/ballerina-library/issues/8267) ## [2.13.1] - 2025-04-23 From 47c47e993bba6ecea3b1772c3c95d277b3e525f0 Mon Sep 17 00:00:00 2001 From: Niveathika Date: Wed, 24 Sep 2025 09:49:17 +0530 Subject: [PATCH 11/12] Remove unnecessary condition --- native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java index 9d2758cab..cd71e2e24 100644 --- a/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/ftp/util/FtpUtil.java @@ -126,7 +126,7 @@ private static String normalizeFtpPath(String rawPath) { if (rawPath == null || rawPath.isEmpty()) { return "/"; } - if (rawPath.startsWith("/") || rawPath.startsWith("//")) { + if (rawPath.startsWith("/")) { return rawPath; } return "/" + rawPath; From 71008728615f4492729326af32707a183a803981 Mon Sep 17 00:00:00 2001 From: Niveathika Date: Wed, 24 Sep 2025 09:50:01 +0530 Subject: [PATCH 12/12] Fix typo --- changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 290bcd830..1952f2d65 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- [Fix the issue where the FTP URL is improperly formatted for the jail-break scnenarios](https://github.com/ballerina-platform/ballerina-library/issues/8267) + + ## [2.14.0] - 2025-08-21 ### Added @@ -17,7 +20,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - [Fix the issue where the FTP listener stops working if the file name is the same](https://github.com/ballerina-platform/ballerina-library/issues/8035) -- [Fix the issue where the FTP URL is improperly formatted for the jail-break scnenarios](https://github.com/ballerina-platform/ballerina-library/issues/8267) ## [2.13.1] - 2025-04-23