Skip to content

Commit 5d7e823

Browse files
authored
GCM Release 2.2.2 (#1333)
**Changes since 2.2.1:** - Fix an issue where duplicate "Personal Access Token" GitHub account options are shown when Visual Studio has a GitHub account signed-in (#1325 #1328) - Fix an issue with Azure DevOps Server (TFS) and Windows Integrated Authentication (#1331 #1332) - Fix an issue with OAuth redirects GitHub Enterprise Server (#1329 #1330) - Correctly handle non-ASCII username/passwords with the WPF UI helpers (#1287 #1326)
2 parents 2f5264d + d6035ef commit 5d7e823

File tree

12 files changed

+118
-21
lines changed

12 files changed

+118
-21
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.2.1.0
1+
2.2.2.0

docs/rename.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ project in its own [organization][gcm-org].
88
![Git Credential Manager Core renamed](img/gcmcore-rename.png)
99

1010
At the time, the actual exectuable name was not updated and continued to be
11-
`git-credential-manager-core`. As of [VERSION][rename-ver], the executable has
11+
`git-credential-manager-core`. As of [2.0.877][rename-ver], the executable has
1212
been renamed to `git-credential-manager`, matching the new project name.
1313

1414
## Rename transition
@@ -21,11 +21,11 @@ warning: git-credential-manager-core was renamed to git-credential-manager
2121
warning: see https://aka.ms/gcm/rename for more information
2222
```
2323

24-
Since the executable was renamed in VERSION, GCM has also included symlinks
24+
Since the executable was renamed in 2.0.877, GCM has also included symlinks
2525
using the old name in order to ensure no one's setups would immediately break.
2626

2727
These links will remain until _two_ major Git versions are released after GCM
28-
VERSION, _**at which point the symlinks will no longer be included**_.
28+
2.0.877, _**at which point the symlinks will no longer be included**_.
2929

3030
It is recommended to update your Git configuration to use the new executable
3131
name as soon as possible to prevent any issues in the future.
@@ -159,7 +159,7 @@ or `manager` respectively.
159159
[rename-pr]: https://github.com/git-ecosystem/git-credential-manager/pull/541
160160
[rename-blog]: https://github.blog/2022-04-07-git-credential-manager-authentication-for-everyone/#universal-git-authentication
161161
[gcm-org]: https://github.com/git-ecosystem
162-
[rename-ver]: https://github.com/git-ecosystem/git-credential-manager/releases
162+
[rename-ver]: https://github.com/git-ecosystem/git-credential-manager/releases/tag/v2.0.877
163163
[git-windows]: https://git-scm.com/download/win
164164
[gcm-latest]: https://aka.ms/gcm/latest
165165
[warnings]: #rename-transition

docs/wsl.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ After updating the `WSLENV` environment variable, restart your WSL installation.
6868
If you have installed GCM using the user-only installer (i.e, the `gcmuser-*.exe`
6969
installer and not the system-wide/admin required installer), you need to modify
7070
the above instructions to point to
71-
`/mnt/c/Users/<USERNAME>/AppData/Local/Programs/Git\ Credential\ Manager\ Core/git-credential-manager.exe`
71+
`/mnt/c/Users/<USERNAME>/AppData/Local/Programs/Git\ Credential\ Manager/git-credential-manager.exe`
7272
instead.
7373

7474
## How it works

src/shared/Core.Tests/Interop/Windows/WindowsCredentialManagerTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,70 @@ public void WindowsCredentialManager_IsMatch(
270270
Assert.Equal(expected, actual);
271271
}
272272

273+
[PlatformFact(Platforms.Windows)]
274+
public void WindowsCredentialManager_IsMatch_NoNamespace_NotMatched()
275+
{
276+
var win32Cred = new Win32Credential
277+
{
278+
UserName = "test",
279+
TargetName = $"{WindowsCredentialManager.TargetNameLegacyGenericPrefix}https://example.com"
280+
};
281+
282+
var credManager = new WindowsCredentialManager(TestNamespace);
283+
284+
bool result = credManager.IsMatch("https://example.com", null, win32Cred);
285+
286+
Assert.False(result);
287+
}
288+
289+
[PlatformFact(Platforms.Windows)]
290+
public void WindowsCredentialManager_IsMatch_DifferentNamespace_NotMatched()
291+
{
292+
var win32Cred = new Win32Credential
293+
{
294+
UserName = "test",
295+
TargetName = $"{WindowsCredentialManager.TargetNameLegacyGenericPrefix}:random-namespace:https://example.com"
296+
};
297+
298+
var credManager = new WindowsCredentialManager(TestNamespace);
299+
300+
bool result = credManager.IsMatch("https://example.com", null, win32Cred);
301+
302+
Assert.False(result);
303+
}
304+
305+
[PlatformFact(Platforms.Windows)]
306+
public void WindowsCredentialManager_IsMatch_CaseSensitiveNamespace_NotMatched()
307+
{
308+
var win32Cred = new Win32Credential
309+
{
310+
UserName = "test",
311+
TargetName = $"{WindowsCredentialManager.TargetNameLegacyGenericPrefix}:nAmEsPaCe:https://example.com"
312+
};
313+
314+
var credManager = new WindowsCredentialManager("namespace");
315+
316+
bool result = credManager.IsMatch("https://example.com", null, win32Cred);
317+
318+
Assert.False(result);
319+
}
320+
321+
[PlatformFact(Platforms.Windows)]
322+
public void WindowsCredentialManager_IsMatch_NoNamespaceInQuery_IsMatched()
323+
{
324+
var win32Cred = new Win32Credential
325+
{
326+
UserName = "test",
327+
TargetName = $"{WindowsCredentialManager.TargetNameLegacyGenericPrefix}https://example.com"
328+
};
329+
330+
var credManager = new WindowsCredentialManager();
331+
332+
bool result = credManager.IsMatch("https://example.com", null, win32Cred);
333+
334+
Assert.True(result);
335+
}
336+
273337
[PlatformTheory(Platforms.Windows)]
274338
[InlineData("https://example.com", null, "https://example.com")]
275339
[InlineData("https://example.com", "bob", "https://[email protected]")]

src/shared/Core.Tests/StreamExtensionsTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ public void StreamExtensions_ReadMultiDictionary_CaseInsensitive_ReturnsDictiona
254254
AssertMultiDictionary(new[] { "2" }, "a", output);
255255
}
256256

257+
[Fact]
258+
public void StreamExtensions_ReadMultiDictionary_EmptyString_ReturnsKeyWithEmptyStringValue()
259+
{
260+
string input = "a=\n\n";
261+
262+
var output = ReadStringStream(input, StreamExtensions.ReadMultiDictionary);
263+
264+
Assert.NotNull(output);
265+
Assert.Equal(1, output.Count);
266+
267+
AssertMultiDictionary(new[] { String.Empty, }, "a", output);
268+
}
269+
257270
[Fact]
258271
public void StreamExtensions_ReadMultiDictionary_Spaces_ReturnsCorrectKeysAndValues()
259272
{

src/shared/Core/Authentication/AuthenticationBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ protected internal virtual async Task<IDictionary<string, string>> InvokeHelperA
3535
RedirectStandardInput = true,
3636
RedirectStandardOutput = true,
3737
RedirectStandardError = false, // Do not redirect stderr as tracing might be enabled
38-
UseShellExecute = false
38+
UseShellExecute = false,
39+
StandardOutputEncoding = EncodingEx.UTF8NoBom,
3940
};
4041

4142
Context.Trace.WriteLine($"Starting helper process: {path} {args}");

src/shared/Core/EncodingEx.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Text;
2+
3+
namespace GitCredentialManager;
4+
5+
public static class EncodingEx
6+
{
7+
public static readonly Encoding UTF8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
8+
}

src/shared/Core/Interop/Windows/WindowsCredentialManager.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,19 @@ private WindowsCredential CreateCredentialFromStructure(Win32Credential credenti
274274
return false;
275275
}
276276

277-
// Trim the "LegacyGeneric" prefix Windows adds and any namespace we have been filtered with
277+
// Trim the "LegacyGeneric" prefix Windows adds
278278
string targetName = credential.TargetName.TrimUntilIndexOf(TargetNameLegacyGenericPrefix);
279+
280+
// Only match credentials with the namespace we have been configured with (if any)
279281
if (!string.IsNullOrWhiteSpace(_namespace))
280282
{
281-
targetName = targetName.TrimUntilIndexOf($"{_namespace}:");
283+
string nsPrefix = $"{_namespace}:";
284+
if (!targetName.StartsWith(nsPrefix, StringComparison.Ordinal))
285+
{
286+
return false;
287+
}
288+
289+
targetName = targetName.Substring(nsPrefix.Length);
282290
}
283291

284292
// If the target name matches the service name exactly then return 'match'

src/shared/Core/StandardStreams.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public class StandardStreams : IStandardStreams
2929
{
3030
private const string LineFeed = "\n";
3131

32-
private static readonly Encoding Utf8NoBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
33-
3432
private TextReader _stdIn;
3533
private TextWriter _stdOut;
3634
private TextWriter _stdErr;
@@ -41,7 +39,7 @@ public TextReader In
4139
{
4240
if (_stdIn == null)
4341
{
44-
_stdIn = new StreamReader(Console.OpenStandardInput(), Utf8NoBomEncoding);
42+
_stdIn = new StreamReader(Console.OpenStandardInput(), EncodingEx.UTF8NoBom);
4543
}
4644

4745
return _stdIn;
@@ -54,7 +52,7 @@ public TextWriter Out
5452
{
5553
if (_stdOut == null)
5654
{
57-
_stdOut = new StreamWriter(Console.OpenStandardOutput(), Utf8NoBomEncoding)
55+
_stdOut = new StreamWriter(Console.OpenStandardOutput(), EncodingEx.UTF8NoBom)
5856
{
5957
AutoFlush = true,
6058
NewLine = LineFeed,
@@ -71,7 +69,7 @@ public TextWriter Error
7169
{
7270
if (_stdErr == null)
7371
{
74-
_stdErr = new StreamWriter(Console.OpenStandardError(), Utf8NoBomEncoding)
72+
_stdErr = new StreamWriter(Console.OpenStandardError(), EncodingEx.UTF8NoBom)
7573
{
7674
AutoFlush = true,
7775
NewLine = LineFeed,

src/shared/Core/StreamExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,15 @@ private static void ParseMultiLine(IDictionary<string, IList<string>> dict, stri
248248
// Only allow one value for non-multi/array entries ("key=value")
249249
// and reset the array of a multi-entry if the value is empty ("key[]=<empty>")
250250
bool emptyValue = string.IsNullOrEmpty(value);
251+
251252
if (!multi || emptyValue)
252253
{
253254
list.Clear();
255+
}
254256

255-
if (emptyValue)
256-
{
257-
return;
258-
}
257+
if (multi && emptyValue)
258+
{
259+
return;
259260
}
260261

261262
list.Add(value);

0 commit comments

Comments
 (0)