fix(ftp): handle empty port parameter and ensure strictly numeric ports - #63161
Open
bahman026 wants to merge 2 commits into
Open
fix(ftp): handle empty port parameter and ensure strictly numeric ports#63161bahman026 wants to merge 2 commits into
bahman026 wants to merge 2 commits into
Conversation
The external storage settings submit an empty string when the Port field is left blank. `??` only substitutes null or missing values, so `""` was assigned to $port and passed to FtpConnection::__construct(), whose $port parameter is typed int. PHP rejects a non-numeric string there, so adding an FTP storage without a port ended in a TypeError and an HTTP 500 - after the configuration had already been saved. Fall back to the default port unless the configured value is numeric, mirroring the guard added for SFTP in nextcloud#58350. Signed-off-by: Bahman Jafarzadeh <bja@adlas.at>
bahman026
requested review from
Altahrim,
leftybournes,
provokateurin and
salmart-dev
and removed request for
a team
August 11, 2026 11:28
Contributor
Author
|
/backport to stable34 |
CarlSchwan
reviewed
Aug 11, 2026
Comment on lines
+16
to
+20
| /** | ||
| * The FTP connection is established lazily, so the constructor can be covered | ||
| * without a reachable FTP server. The backend tests in Storage/FtpTest.php do | ||
| * need one and are run separately. | ||
| */ |
Member
There was a problem hiding this comment.
Suggested change
| /** | |
| * The FTP connection is established lazily, so the constructor can be covered | |
| * without a reachable FTP server. The backend tests in Storage/FtpTest.php do | |
| * need one and are run separately. | |
| */ |
| * without a reachable FTP server. The backend tests in Storage/FtpTest.php do | ||
| * need one and are run separately. | ||
| */ | ||
| class FtpConstructorTest extends TestCase { |
Member
There was a problem hiding this comment.
FtpTest would be more appropriate as class name
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Creating an FTP/FTPS external storage with an empty Port field ends in an HTTP 500.
FTP::__construct()read the port like this:??only substitutesnullor a missing key. The external storage settings submit anempty string when the field is left blank, so
""was assigned to$this->portandpassed on to
FtpConnection::__construct(bool $secure, string $hostname, int $port, ...).PHP rejects a non-numeric string for an
intparameter, so the request died with aTypeError:A numeric string such as
"2121"is coerced normally, so explicitly configured portswere never affected — only an empty (or otherwise non-numeric) value.
Two details make the failure more confusing for users:
UserStoragesController::create()saves the configuration first and calls
updateStorageStatus()afterwards, and thecrash happens in that status check. The user gets a 500 and no visible storage, then
finds the storage after reloading the page.
FTP::getConnection(),MountConfig::getBackendStatus()andStoragesController::updateStorageStatus()all catch\Exception. ATypeErroris an\Error, so it passes through them and surfaces as a hard 500 instead of a"storage not available" status.
SFTP had exactly the same problem and it was fixed in #58350
(reported in #58324, #58293, #58507 and #58833).
FTP.phpnever received the equivalentguard. FTP and SFTP are the only backends that read
['port'].Steps to reproduce
are available (for example global credentials).
TypeErrorshown above.The same steps with SFTP work, thanks to the guard from #58350.
Fix
Fall back to the default port unless the configured value is numeric, mirroring the SFTP
guard:
21stays the default, so nothing changes for existing configurations. Casting withoutthe check would not work, because
(int)""is0rather than21.Tests
apps/files_external/tests/FtpConstructorTest.phpcovers the constructor for a missing,empty,
null, non-numeric, numeric-string and integer port. The FTP connection is onlyopened lazily, so no FTP server is needed. The test is placed in
tests/rather thantests/Storage/becausetests/phpunit-autotest-external.xmlexcludes the latterdirectory, and a test there would not run in CI.
I verified the whole
files_externalsuite as well as theStorage/FtpTest.phpbackendtests against a real FTP server, and confirmed the new test fails without this change.
Side note
The
catch (\Exception $e)blocks mentioned above could be\Throwable, so that thisclass of error degrades to a storage status instead of a 500. I left that out to keep
this change minimal, and I am happy to open a separate pull request for it.
Checklist