Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdea3911c1 |
@@ -88,12 +88,8 @@
|
|||||||
-->
|
-->
|
||||||
<PackageVersion Include="NSec.Cryptography" Version="26.4.0" />
|
<PackageVersion Include="NSec.Cryptography" Version="26.4.0" />
|
||||||
<PackageVersion Include="libsodium" Version="1.0.22" />
|
<PackageVersion Include="libsodium" Version="1.0.22" />
|
||||||
<!--
|
<!-- Managed differential oracle for the crypto test suite only. -->
|
||||||
Managed differential oracle for the crypto test suite only. Also what SSH.NET pulls in,
|
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||||
so central transitive pinning makes this pin its floor too: 2.7.0 is what SSH.NET
|
|
||||||
2026.0.0 asks for, and pinning below that is a downgrade error rather than a preference.
|
|
||||||
-->
|
|
||||||
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.7.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Desktop client">
|
<ItemGroup Label="Desktop client">
|
||||||
<!--
|
<!--
|
||||||
@@ -102,14 +98,8 @@
|
|||||||
agent forwarding (needs an upstream change; de-scoped from v1) and being handed a
|
agent forwarding (needs an upstream change; de-scoped from v1) and being handed a
|
||||||
pre-connected Stream — it performs its own socket connect, which is why the relay and
|
pre-connected Stream — it performs its own socket connect, which is why the relay and
|
||||||
ProxyJump both go through a loopback TCP bridge. See docs/adr/.
|
ProxyJump both go through a loopback TCP bridge. See docs/adr/.
|
||||||
|
|
||||||
2026.0.0 is the fix for GHSA-q939-rpr3-3284, a path traversal in ScpClient.Download's
|
|
||||||
recursive mode that trusts server-supplied names (everything up to and including
|
|
||||||
2025.1.0 is affected). Nothing here uses ScpClient — transfers go through SftpClient —
|
|
||||||
so this was an upgrade on principle rather than an exposure, and the release notes
|
|
||||||
list no breaking changes over 2025.1.0.
|
|
||||||
-->
|
-->
|
||||||
<PackageVersion Include="SSH.NET" Version="2026.0.0" />
|
<PackageVersion Include="SSH.NET" Version="2025.1.0" />
|
||||||
<!--
|
<!--
|
||||||
The S3 client, for buckets as a remote in the file browser. First-party, Apache-2.0, and
|
The S3 client, for buckets as a remote in the file browser. First-party, Apache-2.0, and
|
||||||
managed only — no native assets — which is the bar this file sets for anything that gets
|
managed only — no native assets — which is the bar this file sets for anything that gets
|
||||||
|
|||||||
@@ -984,7 +984,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -1044,9 +1044,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -1140,11 +1140,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,12 +87,27 @@
|
|||||||
TextTrimming="CharacterEllipsis" />
|
TextTrimming="CharacterEllipsis" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!--
|
<!--
|
||||||
|
Two readings stacked against the two on the left, and in the same order: what this row
|
||||||
|
IS above, how it is reached below.
|
||||||
|
|
||||||
|
◆ THE VAULT, and only when there is more than one to be in — HostRowViewModel.VaultBadge
|
||||||
|
is empty otherwise, which collapses the line and leaves the kind word centred as it was.
|
||||||
|
The palette searches every readable vault at once, so without it two hosts a team and a
|
||||||
|
person both call "prod-db" are two identical rows, and Enter takes whichever the ranking
|
||||||
|
happened to put first. Trimmed against a cap because the column is Auto-sized: a long
|
||||||
|
vault name would otherwise take its width out of the host name beside it.
|
||||||
|
|
||||||
credential / key / password — never the mock's SSH/SFTP kind column. Every palette
|
credential / key / password — never the mock's SSH/SFTP kind column. Every palette
|
||||||
connect here is SSH, so printing that word would be a constant dressed up as a reading;
|
connect here is SSH, so printing that word would be a constant dressed up as a reading;
|
||||||
see hosts-v5-design-spec.md's deviations and HostRowViewModel.Authentication.
|
see hosts-v5-design-spec.md's deviations and HostRowViewModel.Authentication.
|
||||||
-->
|
-->
|
||||||
<TextBlock Grid.Column="2" Classes="mono qcrow-subtext" Text="{Binding Authentication}"
|
<StackPanel Grid.Column="2" Spacing="1" Margin="10,0,0,0" VerticalAlignment="Center">
|
||||||
FontSize="11" Margin="10,0,0,0" VerticalAlignment="Center" />
|
<TextBlock Classes="mono qcrow-subtext" Text="{Binding VaultBadge}" FontSize="10"
|
||||||
|
HorizontalAlignment="Right" MaxWidth="150"
|
||||||
|
TextTrimming="CharacterEllipsis" IsVisible="{Binding HasVaultBadge}" />
|
||||||
|
<TextBlock Classes="mono qcrow-subtext" Text="{Binding Authentication}"
|
||||||
|
FontSize="11" HorizontalAlignment="Right" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -405,7 +405,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -465,9 +465,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -567,11 +567,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,14 +55,14 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -81,11 +81,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -187,9 +187,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -289,11 +289,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2141,8 +2141,9 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
|||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Ranked rather than merely filtered: a host whose name starts with what was typed comes before one
|
/// Ranked rather than merely filtered: a host whose name starts with what was typed comes before one
|
||||||
/// that merely contains it, and both come before a match found only in the address. Typing three
|
/// that merely contains it, both come before a match found only in the address, and all three come
|
||||||
/// characters of a name people use daily should not put that host third.
|
/// before one found only in the vault's name. Typing three characters of a name people use daily should
|
||||||
|
/// not put that host third. See <see cref="Rank"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void RefreshSearchResults()
|
private void RefreshSearchResults()
|
||||||
{
|
{
|
||||||
@@ -2175,6 +2176,21 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
|||||||
OnPropertyChanged(nameof(HasSearchResults));
|
OnPropertyChanged(nameof(HasSearchResults));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Where one host places against what was typed, or <see cref="int.MaxValue"/> for no match.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// <b>The vault comes last, and only when the row prints it.</b> Its name is the widest of the three
|
||||||
|
/// readings — one word can match every host in a team's vault at once — so a host whose own name or
|
||||||
|
/// address answers the query has to come first, or typing a machine's name would bury it under everybody
|
||||||
|
/// else's.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="HostRowViewModel.HasVaultBadge"/> rather than the name itself, so this matches exactly what
|
||||||
|
/// the row shows: a session holding one vault prints no vault on any row, and matching a name nothing
|
||||||
|
/// displays would answer a query with a list whose reason for existing is invisible — type "personal"
|
||||||
|
/// there and every host comes back for no reason anybody can see.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
private static int Rank(HostRowViewModel host, string query)
|
private static int Rank(HostRowViewModel host, string query)
|
||||||
{
|
{
|
||||||
if (host.Label.StartsWith(query, StringComparison.CurrentCultureIgnoreCase))
|
if (host.Label.StartsWith(query, StringComparison.CurrentCultureIgnoreCase))
|
||||||
@@ -2187,8 +2203,14 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return host.Address.Contains(query, StringComparison.CurrentCultureIgnoreCase)
|
if (host.Address.Contains(query, StringComparison.CurrentCultureIgnoreCase))
|
||||||
? 2
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return host.HasVaultBadge
|
||||||
|
&& host.VaultName.Contains(query, StringComparison.CurrentCultureIgnoreCase)
|
||||||
|
? 3
|
||||||
: int.MaxValue;
|
: int.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -267,9 +267,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -369,11 +369,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,11 +304,10 @@ internal sealed class SshNetConnection(SshClient client, HostKeyPresentation hos
|
|||||||
/// Read at construction rather than lazily: by the time an <see cref="SshNetConnection"/> exists,
|
/// Read at construction rather than lazily: by the time an <see cref="SshNetConnection"/> exists,
|
||||||
/// <see cref="SshNetConnectionFactory.ConnectAsync"/> has already awaited <c>client.ConnectAsync</c>, so
|
/// <see cref="SshNetConnectionFactory.ConnectAsync"/> has already awaited <c>client.ConnectAsync</c>, so
|
||||||
/// <c>ConnectionInfo</c> is already populated and there is no earlier moment reading it would race. SSH.NET
|
/// <c>ConnectionInfo</c> is already populated and there is no earlier moment reading it would race. SSH.NET
|
||||||
/// types the property as nullable all the same — it is null before the key exchange completes — so the
|
/// types the property as a non-nullable <c>string</c>, so this reads straight through rather than coalescing
|
||||||
/// coalesce is here to satisfy that annotation, not because this call site expects to see it. Empty is the
|
/// a null that the library's own contract says cannot occur.
|
||||||
/// value this codebase already uses for a cipher that could not be read; see <c>MainWindowViewModel.NullIfEmpty</c>.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public string Cipher { get; } = client.ConnectionInfo.CurrentServerEncryption ?? string.Empty;
|
public string Cipher { get; } = client.ConnectionInfo.CurrentServerEncryption;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<ISshShellSession> OpenShellAsync(TerminalSize size, CancellationToken cancellationToken)
|
public Task<ISshShellSession> OpenShellAsync(TerminalSize size, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ internal sealed class SshNetSftpSession(SftpClient client, HostKeyPresentation h
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Read at construction, the same way and for the same reason as <c>SshNetConnection.Cipher</c>: this type
|
/// Read at construction, the same way and for the same reason as <c>SshNetConnection.Cipher</c>: this type
|
||||||
/// is only ever built after <c>SshNetConnectionFactory.OpenSftpAsync</c> has awaited <c>client.ConnectAsync</c>,
|
/// is only ever built after <c>SshNetConnectionFactory.OpenSftpAsync</c> has awaited <c>client.ConnectAsync</c>,
|
||||||
/// so <c>ConnectionInfo</c> is already populated by the time there is a session to read it from. The coalesce
|
/// so <c>ConnectionInfo</c> is already populated by the time there is a session to read it from.
|
||||||
/// is there for SSH.NET's nullable annotation, which covers the pre-key-exchange window this never sees.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public string Cipher { get; } = client.ConnectionInfo.CurrentServerEncryption ?? string.Empty;
|
public string Cipher { get; } = client.ConnectionInfo.CurrentServerEncryption;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string HomeDirectory { get; } = homeDirectory;
|
public string HomeDirectory { get; } = homeDirectory;
|
||||||
|
|||||||
@@ -31,11 +31,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -54,9 +54,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
|
|||||||
@@ -37,14 +37,14 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -63,11 +63,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,14 +37,14 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -63,11 +63,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1744,9 +1744,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -1847,11 +1847,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -236,6 +236,51 @@ public sealed class QuickConnectTests : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// The palette searches every vault the session holds a key for, so a row has to say which one it came
|
||||||
|
/// out of. Two machines a team and a person both call <c>prod-db</c> are otherwise two identical rows,
|
||||||
|
/// and Enter takes whichever the ranking happened to put first.
|
||||||
|
/// <para>
|
||||||
|
/// Typed into rather than read off the unfiltered list, because the shared vault's host sorts last — the
|
||||||
|
/// active vault's rows come first — and the list virtualises, so the row this is about might never be
|
||||||
|
/// realised. Narrowing to it also proves the search reaches past the active vault at all.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task AResultSaysWhichVaultItCameOutOf()
|
||||||
|
{
|
||||||
|
await SeedSharedHostAsync();
|
||||||
|
|
||||||
|
await OnThePaletteAsync((palette, window) =>
|
||||||
|
{
|
||||||
|
shell.SearchText = "prod-db";
|
||||||
|
Relayout(window);
|
||||||
|
|
||||||
|
var found = shell.SearchResults.ShouldHaveSingleItem();
|
||||||
|
found.VaultId.ShouldNotBe(session.ActiveVaultId, "the palette reaches past the active vault");
|
||||||
|
|
||||||
|
VisibleTexts(RowFor(palette, found)).ShouldContain("PLATFORM SECRETS", StringComparer.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// The other half of the rule, and the reason the name is a badge rather than a column: a vault named on
|
||||||
|
/// every row of a session that has only one is the same fact repeated, which is noise rather than a
|
||||||
|
/// reading. <c>HostRowViewModel.VaultBadge</c> is empty there, and an empty line has to collapse rather
|
||||||
|
/// than leave a gap above the kind word.
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task AResultNamesNoVaultWhenThereIsOnlyOneToBeIn()
|
||||||
|
{
|
||||||
|
await OnThePaletteAsync((palette, _) =>
|
||||||
|
{
|
||||||
|
var first = shell.SearchResults[0];
|
||||||
|
|
||||||
|
first.HasVaultBadge.ShouldBeFalse("this fixture's session holds the personal vault alone");
|
||||||
|
VisibleTexts(RowFor(palette, first)).ShouldNotContain("PERSONAL", StringComparer.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The palette is a box somebody is expected to start typing into, and for a while it was not: the window
|
/// The palette is a box somebody is expected to start typing into, and for a while it was not: the window
|
||||||
/// focused it from the view model's <c>PropertyChanged</c>, which runs before the binding that reveals the
|
/// focused it from the view model's <c>PropertyChanged</c>, which runs before the binding that reveals the
|
||||||
@@ -307,6 +352,25 @@ public sealed class QuickConnectTests : IAsyncLifetime
|
|||||||
.OfType<ListBoxItem>()
|
.OfType<ListBoxItem>()
|
||||||
.First(item => ReferenceEquals(item.DataContext, host));
|
.First(item => ReferenceEquals(item.DataContext, host));
|
||||||
|
|
||||||
|
/// <summary>What one row actually draws, in order, ignoring the lines that collapsed.</summary>
|
||||||
|
private static List<string> VisibleTexts(Visual row) =>
|
||||||
|
row.GetVisualDescendants()
|
||||||
|
.OfType<TextBlock>()
|
||||||
|
.Where(text => text.IsEffectivelyVisible)
|
||||||
|
.Select(text => text.Text ?? string.Empty)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
/// <summary>Runs the layout pass the application's dispatcher would run after the list changed.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Without it the new rows are in the collection but not in the visual tree, so <see cref="RowFor"/>
|
||||||
|
/// finds nothing to look at.
|
||||||
|
/// </remarks>
|
||||||
|
private static void Relayout(Window window)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
window.UpdateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
private static Point Centre(Visual control, Visual window) =>
|
private static Point Centre(Visual control, Visual window) =>
|
||||||
control.TranslatePoint(new Point(control.Bounds.Width / 2, control.Bounds.Height / 2), window)
|
control.TranslatePoint(new Point(control.Bounds.Width / 2, control.Bounds.Height / 2), window)
|
||||||
?? throw new InvalidOperationException("the control is not in this window's tree");
|
?? throw new InvalidOperationException("the control is not in this window's tree");
|
||||||
@@ -325,4 +389,37 @@ public sealed class QuickConnectTests : IAsyncLifetime
|
|||||||
|
|
||||||
await vault.LoadAsync(Token);
|
await vault.LoadAsync(Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a second, shared vault to the fixture's session and files one host into it.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Straight into the session rather than through <c>VaultsViewModel</c>, which is the technique the
|
||||||
|
/// settings pages' suite uses and for the same reason: creating a vault needs a connection, and this
|
||||||
|
/// shell has none. The key is generated on this machine either way, so what the session ends up holding
|
||||||
|
/// is the same thing a real creation leaves behind — see <see cref="StubTeamServer"/>.
|
||||||
|
/// </remarks>
|
||||||
|
private async Task SeedSharedHostAsync()
|
||||||
|
{
|
||||||
|
using var teamServer = new StubTeamServer();
|
||||||
|
|
||||||
|
var shared = await session.CreateTeamVaultAsync(
|
||||||
|
teamServer.Teams, StubTeamServer.SharedTeamId, "Platform secrets", Token);
|
||||||
|
|
||||||
|
await vault.LoadAsync(Token);
|
||||||
|
|
||||||
|
vault.SelectedTargetVault =
|
||||||
|
vault.TargetVaults.Single(choice => choice.VaultId == shared.VaultId);
|
||||||
|
|
||||||
|
vault.NewHostCommand.Execute(null);
|
||||||
|
vault.EditorLabel = "prod-db";
|
||||||
|
vault.EditorHostname = "db.internal";
|
||||||
|
vault.EditorUsername = "deploy";
|
||||||
|
|
||||||
|
await vault.SaveHostCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
vault.IsEditing.ShouldBeFalse(vault.Status);
|
||||||
|
|
||||||
|
await vault.LoadAsync(Token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -551,7 +551,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -663,9 +663,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"CommunityToolkit.Mvvm": {
|
"CommunityToolkit.Mvvm": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -771,11 +771,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1765,6 +1765,28 @@ public sealed class ShellFlowTests : IAsyncLifetime
|
|||||||
shell.IsTerminalShowing.ShouldBeTrue();
|
shell.IsTerminalShowing.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// The palette searches a host's vault name, and only where the row prints it — which a session holding
|
||||||
|
/// one vault never does. Matching it anyway would answer "personal" with the entire keychain, ranked
|
||||||
|
/// behind nothing and explained by nothing on screen. See <c>MainWindowViewModel.Rank</c>.
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task ThePalette_DoesNotMatchTheVaultNameWhenThereIsOnlyOneVaultToBeIn()
|
||||||
|
{
|
||||||
|
var vault = await ReadyToConnectAsync();
|
||||||
|
|
||||||
|
vault.VaultName.ShouldBe("Personal", "which is the word this test types");
|
||||||
|
|
||||||
|
shell.ToggleSearchCommand.Execute(null);
|
||||||
|
|
||||||
|
shell.SearchResults.ShouldHaveSingleItem()
|
||||||
|
.HasVaultBadge.ShouldBeFalse("one vault is nothing to tell apart");
|
||||||
|
|
||||||
|
shell.SearchText = "personal";
|
||||||
|
|
||||||
|
shell.SearchResults.ShouldBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The rail marks where you are, and a terminal is not one of its destinations. Lighting HOSTS while a
|
/// The rail marks where you are, and a terminal is not one of its destinations. Lighting HOSTS while a
|
||||||
/// terminal fills the window would point at a screen that is not showing — and the selected tab already
|
/// terminal fills the window would point at a screen that is not showing — and the selected tab already
|
||||||
|
|||||||
@@ -766,6 +766,59 @@ public sealed class VaultSharingTests : IAsyncLifetime
|
|||||||
vault.Status.ShouldContain("left alone", Case.Insensitive);
|
vault.Status.ShouldContain("left alone", Case.Insensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// The palette reaches every vault the session holds a key for, so the vault's name is one of the things
|
||||||
|
/// somebody can reasonably type into it: "show me the team's machines" is a question a list spanning two
|
||||||
|
/// vaults invites, and until now the only answer was to know one of the machines by name already.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// The order is the assertion that matters. A vault name is the widest of the three readings — one word
|
||||||
|
/// matches every host in that vault at once — so the host whose own name carries the word has to come
|
||||||
|
/// first, or typing a machine's name would bury it under everybody else's.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task ThePalette_FindsAHostByItsVaultsName_BehindOneCalledThatItself()
|
||||||
|
{
|
||||||
|
await UnlockedAsync();
|
||||||
|
|
||||||
|
var vaults = shell.Vaults;
|
||||||
|
|
||||||
|
await CreateVaultAsync(vaults, "Platform secrets");
|
||||||
|
|
||||||
|
var vault = shell.Vault!;
|
||||||
|
var sharedVaultId = vaults.SelectedVault!.VaultId;
|
||||||
|
|
||||||
|
await vault.LoadAsync(Token);
|
||||||
|
|
||||||
|
vault.NewHostCommand.Execute(null);
|
||||||
|
vault.EditorLabel = "platform-gateway";
|
||||||
|
vault.EditorHostname = "gateway.internal";
|
||||||
|
|
||||||
|
await vault.SaveHostCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
vault.NewHostCommand.Execute(null);
|
||||||
|
vault.EditorSelectedVault =
|
||||||
|
vault.EditorVaultChoices.Single(choice => choice.VaultId == sharedVaultId);
|
||||||
|
vault.EditorLabel = "prod-db";
|
||||||
|
vault.EditorHostname = "db.internal";
|
||||||
|
|
||||||
|
await vault.SaveHostCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
vault.IsEditing.ShouldBeFalse(vault.Status);
|
||||||
|
|
||||||
|
shell.ToggleSearchCommand.Execute(null);
|
||||||
|
shell.SearchText = "platform";
|
||||||
|
|
||||||
|
shell.SearchResults
|
||||||
|
.Select(row => row.Label)
|
||||||
|
.ShouldBe(["platform-gateway", "prod-db"], "a host's own name outranks its vault's");
|
||||||
|
|
||||||
|
shell.SearchResults[1].VaultBadge.ShouldBe(
|
||||||
|
"PLATFORM SECRETS", "and the row says what it was found by");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Two hosts for the run above: one in the personal vault under a group, one already in the destination.
|
/// Two hosts for the run above: one in the personal vault under a group, one already in the destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -540,7 +540,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -652,9 +652,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"CommunityToolkit.Mvvm": {
|
"CommunityToolkit.Mvvm": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -760,11 +760,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -229,7 +229,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AWSSDK.Core": {
|
"AWSSDK.Core": {
|
||||||
@@ -249,9 +249,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -270,11 +270,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,7 +340,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -379,9 +379,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -481,11 +481,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,11 +41,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -306,7 +306,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.terminal": {
|
"dodossh.client.terminal": {
|
||||||
@@ -317,9 +317,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
|
|||||||
@@ -217,7 +217,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.terminal": {
|
"dodossh.client.terminal": {
|
||||||
@@ -228,9 +228,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -249,11 +249,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.transfer": {
|
"dodossh.client.transfer": {
|
||||||
@@ -228,9 +228,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -249,11 +249,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -390,9 +390,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -431,11 +431,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -436,7 +436,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NSec.Cryptography": "[26.4.0, )",
|
"NSec.Cryptography": "[26.4.0, )",
|
||||||
"SSH.NET": "[2026.0.0, )"
|
"SSH.NET": "[2025.1.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dodossh.client.storage": {
|
"dodossh.client.storage": {
|
||||||
@@ -486,9 +486,9 @@
|
|||||||
},
|
},
|
||||||
"BouncyCastle.Cryptography": {
|
"BouncyCastle.Cryptography": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.7.0, )",
|
"requested": "[2.6.2, )",
|
||||||
"resolved": "2.7.0",
|
"resolved": "2.6.2",
|
||||||
"contentHash": "U+12df8UEWHgBi04YVf/Lgi2dy3SItlIYvHjjEVa/BngCQIzDCDRBk50DDByCfDvSbe5pRNFr3b7UrVK2kMcLw=="
|
"contentHash": "7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w=="
|
||||||
},
|
},
|
||||||
"EFCore.NamingConventions": {
|
"EFCore.NamingConventions": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
@@ -599,11 +599,11 @@
|
|||||||
},
|
},
|
||||||
"SSH.NET": {
|
"SSH.NET": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2026.0.0, )",
|
"requested": "[2025.1.0, )",
|
||||||
"resolved": "2026.0.0",
|
"resolved": "2025.1.0",
|
||||||
"contentHash": "Yu9dirPq8l3oaat0+OQ7K0nUf5MmYltpia5UGqsApTG4zTPvBC1cxbNnC3NERij26dUST0A3Ef1QdHSn5ArbWQ==",
|
"contentHash": "jrnbtf0ItVaXAe6jE8X/kSLa6uC+0C+7W1vepcnRQB/rD88qy4IxG7Lf1FIbWmkoc4iVXv0pKrz+Wc6J4ngmHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BouncyCastle.Cryptography": "2.7.0",
|
"BouncyCastle.Cryptography": "2.6.2",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
"Microsoft.Extensions.Logging.Abstractions": "8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user