From 12da03ffb23b45c8f62f134894f5fbad8c222561 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Fri, 29 May 2026 16:16:53 +0100 Subject: [PATCH] Fix potential crash when extracting path from URI with no segments The pattern string.Join("", uri.Segments).Substring(1) would throw an ArgumentOutOfRangeException if the joined string was empty. Extract the logic into a GetUriPathBytes helper that strips exactly one leading '/' when present and handles edge cases safely, replacing all 4 inline occurrences. Uses Segments-based logic (not Uri.AbsolutePath) to preserve exact byte-compatibility with existing keychain entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Xamarin.MacDev/Keychain.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Xamarin.MacDev/Keychain.cs b/Xamarin.MacDev/Keychain.cs index d099f6d..1c0b591 100644 --- a/Xamarin.MacDev/Keychain.cs +++ b/Xamarin.MacDev/Keychain.cs @@ -812,7 +812,7 @@ unsafe OSStatus AddInternetPassword (byte [] label, byte [] desc, SecAuthenticat public unsafe void AddInternetPassword (Uri uri, string username, string password) { - byte [] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte [] path = GetUriPathBytes (uri); byte [] passwd = Encoding.UTF8.GetBytes (password); byte [] host = Encoding.UTF8.GetBytes (uri.Host); byte [] user = Encoding.UTF8.GetBytes (username); @@ -848,9 +848,17 @@ public unsafe void AddInternetPassword (Uri uri, string username, string passwor static readonly byte [] WebFormPassword = Encoding.UTF8.GetBytes ("Web form password"); + // Extracts the URI path without the leading '/', returning an empty array for URIs with no path segments. + static byte [] GetUriPathBytes (Uri uri) + { + var joined = string.Join (string.Empty, uri.Segments); + var path = joined.Length > 0 && joined [0] == '/' ? joined.Substring (1) : joined; + return Encoding.UTF8.GetBytes (path); + } + public unsafe void AddInternetPassword (Uri uri, string password) { - byte [] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte [] path = GetUriPathBytes (uri); byte [] user = Encoding.UTF8.GetBytes (Uri.UnescapeDataString (uri.UserInfo)); byte [] passwd = Encoding.UTF8.GetBytes (password); byte [] host = Encoding.UTF8.GetBytes (uri.Host); @@ -920,7 +928,7 @@ static unsafe string GetUsernameFromKeychainItemRef (IntPtr itemRef) public unsafe Tuple FindInternetUserNameAndPassword (Uri uri) { - byte [] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte [] path = GetUriPathBytes (uri); byte [] host = Encoding.UTF8.GetBytes (uri.Host); var auth = GetSecAuthenticationType (uri.Query); var protocol = GetSecProtocolType (uri.Scheme); @@ -968,7 +976,7 @@ public unsafe Tuple FindInternetPassword (string serverName = "" public string FindInternetPassword (Uri uri) { - byte [] path = Encoding.UTF8.GetBytes (string.Join (string.Empty, uri.Segments).Substring (1)); // don't include the leading '/' + byte [] path = GetUriPathBytes (uri); byte [] user = Encoding.UTF8.GetBytes (Uri.UnescapeDataString (uri.UserInfo)); byte [] host = Encoding.UTF8.GetBytes (uri.Host); var auth = GetSecAuthenticationType (uri.Query);