Skip to content

Fix/pre v2 bug fixes#52

Merged
sparkeh9 merged 4 commits intomasterfrom
fix/pre-v2-bug-fixes
Feb 28, 2026
Merged

Fix/pre v2 bug fixes#52
sparkeh9 merged 4 commits intomasterfrom
fix/pre-v2-bug-fixes

Conversation

@sparkeh9
Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings February 28, 2026 20:13
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on modernizing the CoreFTP library by upgrading its target .NET framework and all its dependencies to the latest versions. It also addresses several critical bug fixes related to connection stability, error handling during logout and disposal, and robust parsing of FTP server responses. The changes aim to provide a more reliable and up-to-date FTP client, backed by new integration and unit tests to ensure stability.

Highlights

  • Major .NET SDK and Framework Upgrade: The project has been upgraded to target .NET 10.0, along with updating all associated NuGet package dependencies to their latest versions (e.g., Microsoft.Extensions.* to v9.0.0, FluentAssertions to v7.0.0, xunit to v2.9.2). This modernizes the codebase and addresses compatibility issues with older .NET Core 1.0 dependencies.
  • Improved Connection Stability and Error Handling: The LogOutAsync and Dispose methods in FtpClient.cs have been made more robust. They now gracefully handle connection drops or failures during logout and disposal without throwing unhandled exceptions, logging warnings instead. The Disconnect method in FtpControlStream.cs now explicitly closes the socket.
  • Enhanced Working Directory Parsing: A bug in ChangeWorkingDirectoryAsync has been fixed to correctly parse PWD responses from FTP servers that do not enclose the directory path in quotes, preventing potential IndexOutOfRangeException errors.
  • Refactored Stream Reading Logic: The FtpControlStream's ReadLine and ReadByte methods have been refactored for improved efficiency and compatibility across different .NET versions, ensuring more reliable parsing of FTP server responses.
  • Comprehensive Test Coverage: New integration and unit tests have been added to validate the bug fixes for ChangeWorkingDirectoryAsync, LogOutAsync, and Dispose, as well as to ensure the correct behavior of the FtpControlStream's reading methods.
  • Default SSL Protocols Change: The default SslProtocols setting in FtpClientConfiguration has been changed to SslProtocols.None, requiring explicit configuration for SSL/TLS protocols.
Changelog
  • global.json
    • Updated .NET SDK version to 10.0.103.
  • restore_log.txt
    • Added a log file detailing previous NuGet restore errors due to outdated dependencies and target framework incompatibilities, which are now resolved by this PR.
  • src/CoreFtp/CoreFtp.csproj
    • Updated target frameworks from netstandard1.6;net452 to net10.0;net462;netstandard2.0.
    • Updated package references for Microsoft.Extensions.Caching.Abstractions, Microsoft.Extensions.Caching.Memory, and Microsoft.Extensions.Logging.Abstractions to version 9.0.0.
    • Removed outdated framework-specific package references and properties.
    • Updated the package version to 2.0.0.
  • src/CoreFtp/FtpClient.cs
    • Implemented robust error handling in LogOutAsync to catch exceptions during the QUIT command, ensuring Disconnect and IsAuthenticated state updates.
    • Introduced ParseWorkingDirectory method to handle PWD responses without quotes, preventing IndexOutOfRangeException.
    • Modified Dispose method to safely call LogOutAsync if connected, preventing exceptions during client disposal.
  • src/CoreFtp/FtpClientConfiguration.cs
    • Changed the default SslProtocols property to SslProtocols.None.
  • src/CoreFtp/Infrastructure/Stream/FtpControlStream.cs
    • Added a readByteBuffer field for single-byte reads.
    • Overrode ReadByte method for more controlled single-byte reading.
    • Introduced a new Read(Span<byte> buffer) method for efficient byte reading.
    • Updated ReadLine method to utilize the new Read(Span<byte> buffer) for improved stream processing.
    • Added Socket.Close() in the Disconnect method's finally block to ensure explicit socket closure.
  • tests/CoreFtp.Tests.Integration/CoreFtp.Tests.Integration.csproj
    • Updated target framework to net10.0.
    • Updated numerous NuGet package references to their latest versions, including Microsoft.NET.Test.Sdk, xunit, FluentAssertions, and Microsoft.Extensions.* packages.
    • Simplified project properties by removing redundant or outdated configurations.
  • tests/CoreFtp.Tests.Integration/FtpClientTests/ChangeWorkingDirectoryTests.cs
    • Added a new integration test to verify ChangeWorkingDirectoryAsync correctly handles PWD responses that do not contain quoted paths.
  • tests/CoreFtp.Tests.Integration/FtpClientTests/DisposeTests.cs
    • Added a new integration test to ensure the Dispose method does not throw exceptions when LogOutAsync fails due to a dropped connection.
  • tests/CoreFtp.Tests.Integration/FtpClientTests/LogOutTests.cs
    • Added a new integration test to confirm LogOutAsync handles connection timeouts or drops gracefully without throwing exceptions.
  • tests/CoreFtp.Tests.Integration/FtpControlStreamTests/When_reading_control_stream.cs
    • Added new unit tests for FtpControlStream's ReadLine and ReadByte methods, covering various stream states and line endings.
  • tests/CoreFtp.Tests.Integration/Program.cs
    • Updated the Initialise method to use the modern Microsoft.Extensions.Logging.LoggerFactory.Create pattern and set minimum log level.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/build.yml
    • .github/workflows/publish.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request upgrades the project to target newer .NET frameworks (including a preview of .NET 10) and updates all dependencies. It also includes several important bug fixes, such as more robust FTP response parsing, ensuring resources are properly disposed, and making logout operations more resilient to network errors, with new integration tests covering these fixes. However, a security audit identified two significant vulnerabilities in the network stream handling logic: a potential Denial of Service (DoS) due to unbounded memory allocation in ReadLine, and an FTP command injection vulnerability due to missing CRLF sanitization in WriteLineAsync. Both issues must be addressed to ensure the security and stability of the library. Additionally, my review suggests removing a committed log file and improving exception logging in the Dispose method.

Comment on lines +191 to 195
protected async Task WriteLineAsync(string buf)
{
var data = Encoding.GetBytes( $"{buf}\r\n" );
await WriteAsync( data, 0, data.Length, CancellationToken.None );
var data = Encoding.GetBytes($"{buf}\r\n");
await WriteAsync(data, 0, data.Length, CancellationToken.None);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The WriteLineAsync method appends \r\n to the provided buffer and sends it to the server without checking if the buffer itself contains CRLF sequences. This allows an attacker who can control the parameters of FTP commands (e.g., directory names, filenames) to inject arbitrary FTP commands into the stream. This is a classic CRLF injection vulnerability in the context of the FTP protocol.

To remediate this, sanitize all user-supplied inputs to remove or encode CRLF characters before they are used in FTP commands, or validate that the input does not contain these characters.

        protected async Task WriteLineAsync(string buf)
        {
            if (buf.Contains("\r") || buf.Contains("\n"))
                throw new ArgumentException("Command parameters cannot contain CRLF characters", nameof(buf));

            var data = Encoding.GetBytes($"{buf}\r\n");
            await WriteAsync(data, 0, data.Length, CancellationToken.None);
        }

restore_log.txt Outdated
@@ -0,0 +1,64 @@
Determining projects to restore...
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This log file appears to be a build artifact from a failed dotnet restore. Such files should not be committed to the version control system. Please remove this file from the pull request and add *.log or restore_log.txt to your .gitignore file to prevent this from happening in the future.

Comment on lines +197 to 219
protected string ReadLine(Encoding encoding, CancellationToken token)
{
if ( encoding == null )
throw new ArgumentNullException( nameof( encoding ) );
if (encoding == null)
throw new ArgumentNullException(nameof(encoding));

var data = new List<byte>();
var buffer = new byte[ 1 ];
Span<byte> buffer = stackalloc byte[1];
string line = null;

token.ThrowIfCancellationRequested();

while ( Read( buffer, 0, buffer.Length ) > 0 )
while (Read(buffer) > 0)
{
token.ThrowIfCancellationRequested();
data.Add( buffer[ 0 ] );
if ( (char) buffer[ 0 ] != '\n' )
data.Add(buffer[0]);
if ((char)buffer[0] != '\n')
continue;
line = encoding.GetString( data.ToArray() ).Trim( '\r', '\n' );
line = encoding.GetString(data.ToArray()).Trim('\r', '\n');
break;
}

return line;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The ReadLine method reads bytes from the network stream and adds them to a List<byte> until a newline character (\n) is encountered. If a malicious or misconfigured FTP server sends an extremely long line without a newline, the client will continue to allocate memory until it runs out of memory (OOM), leading to a crash. This is a potential Denial of Service (DoS) vulnerability.

To remediate this, implement a maximum line length limit. If the limit is exceeded, throw an exception or truncate the line.

        protected string ReadLine(Encoding encoding, CancellationToken token)
        {
            if (encoding == null)
                throw new ArgumentNullException(nameof(encoding));

            var data = new List<byte>();
            Span<byte> buffer = stackalloc byte[1];
            string line = null;

            token.ThrowIfCancellationRequested();

            const int maxLineLength = 4096;

            while (Read(buffer) > 0)
            {
                token.ThrowIfCancellationRequested();
                data.Add(buffer[0]);
                if ((char)buffer[0] != '\n')
                {
                    if (data.Count > maxLineLength)
                        throw new FtpException("Line length limit exceeded");
                    continue;
                }
                line = encoding.GetString(data.ToArray()).Trim('\r', '\n');
                break;
            }

            return line;
        }

LogOutAsync().GetAwaiter().GetResult();
}
}
catch { }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Swallowing exceptions silently with an empty catch block can hide underlying issues. While Dispose methods should not throw exceptions, it's good practice to log any exceptions that occur during cleanup to aid in debugging. This is especially important here since LogOutAsync can throw exceptions (e.g., from IgnoreStaleData).

            catch (Exception ex)
            {
                Logger?.LogWarning(0, ex, "Exception during logout on dispose.");
            }

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes CoreFtp for a v2 release by upgrading the solution to newer .NET targets/tooling and adding targeted regression tests for recent bug fixes (logout/dispose robustness, PWD parsing, control-stream reads), while removing the older end-to-end integration suite.

Changes:

  • Upgrade CoreFtp + test project to newer target frameworks and update package/tooling versions (including global.json and CI workflows).
  • Fix FTP client robustness around logout/dispose and parsing unquoted PWD responses.
  • Rework tests: remove legacy integration tests and add focused tests using a lightweight fake FTP server and control-stream unit tests.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/CoreFtp.Tests.Integration/Program.cs Updates logging initialization (but still contains Main, which conflicts with test SDK entrypoint).
tests/CoreFtp.Tests.Integration/FtpControlStreamTests/When_reading_control_stream.cs New unit tests for FtpControlStream.ReadLine / ReadByte behavior.
tests/CoreFtp.Tests.Integration/FtpClientTests/LogOutTests.cs New fake-server test covering LogOutAsync on dropped connection.
tests/CoreFtp.Tests.Integration/FtpClientTests/DisposeTests.cs New fake-server test covering Dispose() when logout fails.
tests/CoreFtp.Tests.Integration/FtpClientTests/ChangeWorkingDirectoryTests.cs New fake-server test covering PWD responses without quotes + inline xUnit logger helper.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_using_a_uri_as_hostname.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_sending_a_custom_command.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_renaming_a_node.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_opening_datastream_with_explicit_encryption.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_connecting_to_an_ftp_server.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_connecting_to_a_tls_encrypted_ftp_server.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/When_changing_transfer_type.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/TestBase.cs Removes old shared integration test base.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_uploading_file_to_deep_folder.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_opening_file_write_stream.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_opening_a_file_for_upload.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_opening_a_file_for_download.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_listing_files.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_getting_the_size_of_a_file.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Files/When_deleting_a_file.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_providing_a_base_directory.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_listing_directories.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_deleting_directories.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_creating_directories.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_creating_a_deep_folder_from_root.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_changing_working_directories.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/Directories/When_Encountering_Differing_Directory_Formats.cs Removes legacy integration test.
tests/CoreFtp.Tests.Integration/FtpClientTests/CustomExamples/When_connecting_to_bom_gov_au.cs Removes example test.
tests/CoreFtp.Tests.Integration/CoreFtp.Tests.Integration.csproj Upgrades test project to net10.0 + updates dependencies.
src/CoreFtp/Infrastructure/Stream/FtpControlStream.cs Adds buffered ReadByte + adjusts read path used by ReadLine; improves disconnect behavior.
src/CoreFtp/FtpClientConfiguration.cs Defaults SslProtocols to None (defer to OS defaults).
src/CoreFtp/FtpClient.cs Makes logout/dispose more resilient; adds PWD parsing fallback.
src/CoreFtp/CoreFtp.csproj Updates TFMs + dependencies; bumps version to 2.0.0.
global.json Pins repository to .NET SDK 10.0.103.
.github/workflows/build.yml Updates CI build to install .NET 10.
.github/workflows/publish.yml Adds NuGet publish workflow on version tags.
restore_log.txt Adds restore output log (likely an artifact).
build_log.txt Adds build output log (likely an artifact).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

restore_log.txt Outdated
Comment on lines +1 to +21
Determining projects to restore...
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : warning NU1903: Package 'Microsoft.NETCore.App' 1.0.5 has a known high severity vulnerability, https://github.com/advisories/GHSA-7mfr-774f-w5r9
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : warning NU1903: Package 'Microsoft.NETCore.App' 1.0.5 has a known high severity vulnerability, https://github.com/advisories/GHSA-8884-xcr4-r68p
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package xunit.runner.visualstudio 3.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package xunit.runner.visualstudio 3.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net472 (.NETFramework,Version=v4.7.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net6.0 (.NETCoreApp,Version=v6.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration.FileExtensions 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration.FileExtensions 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration.Json 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration.Json 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restore_log.txt appears to be a local build artifact (absolute paths, transient restore output). It’s usually better not to commit build/restore logs to the repo; consider removing this file (or moving to a docs/troubleshooting location if it’s intentionally retained).

Suggested change
Determining projects to restore...
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : warning NU1903: Package 'Microsoft.NETCore.App' 1.0.5 has a known high severity vulnerability, https://github.com/advisories/GHSA-7mfr-774f-w5r9
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : warning NU1903: Package 'Microsoft.NETCore.App' 1.0.5 has a known high severity vulnerability, https://github.com/advisories/GHSA-8884-xcr4-r68p
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package xunit.runner.visualstudio 3.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package xunit.runner.visualstudio 3.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net472 (.NETFramework,Version=v4.7.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net6.0 (.NETCoreApp,Version=v6.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration.FileExtensions 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration.FileExtensions 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: Package Microsoft.Extensions.Configuration.Json 9.0.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Extensions.Configuration.Json 9.0.0 supports:
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net462 (.NETFramework,Version=v4.6.2)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net8.0 (.NETCoreApp,Version=v8.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)
This file is intentionally *not* a live `dotnet restore` log.
It previously contained transient restore output with machine-specific absolute
paths (for example, `d:\...`) and package compatibility errors. That content
has been removed to avoid committing local build artifacts and environment
details to the repository.
If you need a restore log for troubleshooting:
1. Run `dotnet restore` locally in your working copy.
2. Capture the output to a temporary file, for example:
dotnet restore > restore_log_local.txt
3. Use that temporary file for debugging instead of committing it.
You may repurpose or relocate this file (for example, into `docs/`) if you
want to maintain more detailed, curated troubleshooting notes.
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - net9.0 (.NETCoreApp,Version=v9.0)
d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj : error NU1202: - netstandard2.0 (.NETStandard,Version=v2.0)

Copilot uses AI. Check for mistakes.
build_log.txt Outdated
Comment on lines +1 to +6
Build started 28/02/2026 17:34:03.
1>Project "d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj" on node 1 (Restore target(s)).
1>_GetAllRestoreProjectPathItems:
Determining projects to restore...
1>Project "d:\code\oss\CoreFTP\tests\CoreFtp.Tests.Integration\CoreFtp.Tests.Integration.csproj" (1) is building "d:\code\oss\CoreFTP\src\CoreFtp\CoreFtp.csproj" (2:10) on node 1 (_GenerateProjectRestoreGraph target(s)).
2:10>Project "d:\code\oss\CoreFTP\src\CoreFtp\CoreFtp.csproj" (2:10) is building "d:\code\oss\CoreFTP\src\CoreFtp\CoreFtp.csproj" (2:14) on node 1 (_GenerateProjectRestoreGraphPerFramework target(s)).
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_log.txt looks like a local MSBuild output capture (machine-specific paths and timestamps). If this is only for debugging the PR locally, it shouldn’t be checked in—please remove it (or replace with a short note in the PR description / docs).

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +152

// Quick xunit logger provider
public class XunitLoggerProvider : ILoggerProvider
{
private readonly ITestOutputHelper _output;

public XunitLoggerProvider(ITestOutputHelper output)
{
_output = output;
}

public ILogger CreateLogger(string categoryName) => new XunitLogger(_output);

public void Dispose()
{
}
}

public class XunitLogger : ILogger
{
private readonly ITestOutputHelper _output;

public XunitLogger(ITestOutputHelper output)
{
_output = output;
}

public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => true;

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
_output.WriteLine(formatter(state, exception));
}
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XunitLoggerProvider/XunitLogger are defined inline here but used by other test classes (DisposeTests/LogOutTests). That creates an implicit coupling between test files and also duplicates the existing CoreFtp.Tests.Integration.Logger providers. Consider moving these logger helpers into a dedicated shared file (or reusing the existing XUnitConsole logger implementation) so tests don’t depend on this particular test file being present.

Suggested change
// Quick xunit logger provider
public class XunitLoggerProvider : ILoggerProvider
{
private readonly ITestOutputHelper _output;
public XunitLoggerProvider(ITestOutputHelper output)
{
_output = output;
}
public ILogger CreateLogger(string categoryName) => new XunitLogger(_output);
public void Dispose()
{
}
}
public class XunitLogger : ILogger
{
private readonly ITestOutputHelper _output;
public XunitLogger(ITestOutputHelper output)
{
_output = output;
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
_output.WriteLine(formatter(state, exception));
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +24
.AddXunitConsole( outputHelper, LogLevel.Debug )
.AddDebug( LogLevel.Error );
LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
{
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test project is now being built with an SDK-provided entry point (via Microsoft.NET.Test.Sdk), but this file still defines its own Main(). That results in multiple entry points (CS0017). Consider removing Main from Program (keep Initialise), or otherwise ensure only one entry point is compiled for the test assembly.

Copilot uses AI. Check for mistakes.
}

[Fact]
public async Task ChangeWorkingDirectoryAsync_ThrowsIndexOutOfRangeException_WhenPwdResponseLacksQuotes()
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name says it "ThrowsIndexOutOfRangeException", but the assertions below expect the call to succeed and set WorkingDirectory. Rename the test to reflect the intended behavior (e.g., "does not throw when PWD response lacks quotes") so failures are diagnosable.

Suggested change
public async Task ChangeWorkingDirectoryAsync_ThrowsIndexOutOfRangeException_WhenPwdResponseLacksQuotes()
public async Task ChangeWorkingDirectoryAsync_DoesNotThrow_WhenPwdResponseLacksQuotes()

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +60
await writer.WriteLineAsync("230 Logged in");
await reader.ReadLineAsync(); // FEAT
await writer.WriteLineAsync("211 End");
await reader.ReadLineAsync(); // OPTS UTF8
await writer.WriteLineAsync("200 OK");
await reader.ReadLineAsync(); // TYPE I
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fake server replies to FEAT with only 211 End, which makes DetermineFeaturesAsync() return no features. The client will then not send OPTS UTF8 ON, but the server currently blocks waiting for it (// OPTS UTF8). Update the FEAT response to include a UTF8 line (e.g., 211-Features: + UTF8 + 211 End) or make the server accept the absence of the OPTS command.

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +61
await writer.WriteLineAsync("230 Logged in");
await reader.ReadLineAsync(); // FEAT
await writer.WriteLineAsync("211 End");
await reader.ReadLineAsync(); // OPTS UTF8
await writer.WriteLineAsync("200 OK");
await reader.ReadLineAsync(); // TYPE I
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as DisposeTests: replying to FEAT with only 211 End means the client won't enable UTF8 and therefore won't send OPTS UTF8 ON, but the fake server blocks waiting for it (// OPTS UTF8). Return a feature list including UTF8 or adjust the server script to handle either command sequence.

Copilot uses AI. Check for mistakes.
LogOutAsync().GetAwaiter().GetResult();
}
}
catch { }
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dispose() currently swallows all exceptions from LogOutAsync() with an empty catch { }, which can hide unexpected failures and makes diagnosing disconnect issues difficult. Consider at least logging the exception, and/or applying a short timeout (e.g., using DisconnectTimeoutMilliseconds) so Dispose() can't block for up to the full TimeoutSeconds waiting for a server response.

Suggested change
catch { }
catch (Exception ex)
{
Logger?.LogError( ex, "An error occurred while logging out during FtpClient.Dispose()." );
}

Copilot uses AI. Check for mistakes.
@sparkeh9 sparkeh9 force-pushed the fix/pre-v2-bug-fixes branch from 24e7873 to 7ed6a3d Compare February 28, 2026 20:46
@sparkeh9 sparkeh9 merged commit 10f879d into master Feb 28, 2026
1 check passed
@sparkeh9 sparkeh9 deleted the fix/pre-v2-bug-fixes branch February 28, 2026 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants