Public Access
Nothing in this repository loaded a .axaml, so the one class of defect this window has actually shipped — a control arranged past the edge of its container, where it cannot be clicked — was the one class nothing could catch. The setup screens rendered sliced once, with their buttons unreachable. The vault column is the next candidate: 340 pixels wide, two lists and two editors, and the only thing keeping it from clipping its own Save button at the window's 520-pixel minimum is a state rule that one editor may be open at a time. That rule was added on the strength of an argument. This adds an Avalonia.Headless project that lays real XAML out at a real size and reports what a user could not reach, and the argument is now a number: with both editors open the column overflows, so the rule is load-bearing rather than defensive. BothEditorsAtOnce_DoNotFit_WhichIsWhyTheRuleExists is the test, and it says what to do if it ever starts passing — the column has room, so delete the rule, not the test. Two findings arrived by measuring rather than by reasoning, and the first one changed the design. MainWindow cannot be shown headlessly at all. Showing it attaches the terminal's NativeWebView, whose Win32 adapter initialises WebView2 on attach, and WebView2 refuses a non-STA thread — which is exactly why Program.Main carries [STAThread] and is written down in that comment. A HeadlessUnitTestSession owns its dispatcher thread and offers no apartment choice, so the whole window is out of reach at any size. That is pinned as a test asserting RPC_E_CHANGED_MODE by HResult rather than by message, so a future Avalonia that makes the adapter lazy will fail it and the harness can be widened. So the column had to become its own control to be measurable, which is the extraction the type-selector rework wanted anyway. Keyboard release moved with it: MainWindow used to call Focus() on HostList by name, and now asks VaultColumn.KeyboardTarget. The window decides that the keyboard should leave the terminal and the column decides where it lands — which is the seam the rework needs, because once the column shows one list at a time, "which list owns the keyboard" is a question only the column can answer. The second finding is the way this kind of test lies quietly. The hint class lived in MainWindow.Styles and carries TextWrapping. A Window's styles reach its whole tree, so nothing about the application depended on where it lived — but a control laid out on its own loses them, and every hint paragraph would have measured as a single line. The harness would have passed while measuring heights that were all too small. The three shared classes now live in App.axaml, which changes no rendering and makes the measurement honest. The detector is calibrated in both directions, because a clipping detector that never fires reads as a guarantee: a deliberately clipped Save button is caught by name, and a list longer than its viewport is exempt. Scrolling is how a list is supposed to handle more rows than fit, and without that exemption the host list would fail the moment it had content. It also mis-fired once and the rule is narrower for it — an empty ListBox is zero pixels tall and correct, so "arranged with no size" now applies only to controls the theme gives a height to. Skia rather than the headless drawing stub, deliberately. The stub's font manager invents glyph metrics, and text height is an input to every stacked panel in this column, so measuring against it would produce numbers that are self-consistent and unrelated to the application. A separate test project rather than more tests in DodoSSH.Client.App.Tests. Avalonia's application, dispatcher and platform are process-global singletons initialised once, and that project's identity is the shell's state machine without Avalonia — the whole reason sign-in is a delegate. The fakes needed to reach a real unlocked vault are shared from DodoSSH.Client.Session.Tests by source link: a project reference would make one test project a library of another, and a copy would be a third implementation of the same decision table drifting from the other two. 855 tests green, 10 of them new. Zero warnings, dotnet format clean. Not done, and this is groundwork rather than the item itself: the type selector. The column still holds both lists at once, so a third item type would still recreate the defect the one-editor rule works around. What is different is that the rework can now be checked instead of eyeballed — including the claim it is being made for, that one editor at a time stops being a runtime rule and becomes a fact about what is in the visual tree. What this harness will never catch is the terminal's native child window compositing over Avalonia content. That is a Win32 property of a real window, no headless surface reproduces it, and it is the reason the WebView is collapsed rather than covered.
163 lines
8.6 KiB
XML
163 lines
8.6 KiB
XML
<Project>
|
|
|
|
<PropertyGroup>
|
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
|
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
|
</PropertyGroup>
|
|
|
|
<!--
|
|
Versions are pinned here for the whole solution. Packages are added per milestone
|
|
rather than all at once, so that every entry is one we have actually verified and
|
|
restored. See docs/adr/ for the choices behind the notable ones.
|
|
-->
|
|
|
|
<ItemGroup Label="ASP.NET Core">
|
|
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
|
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Pinned transitive dependencies">
|
|
<!--
|
|
Microsoft.AspNetCore.OpenApi 10.0.10 resolves Microsoft.OpenApi 2.0.0, which is
|
|
covered by GHSA-v5pm-xwqc-g5wc (high: circular schema references can terminate
|
|
OpenAPI parsing; vulnerable <= 2.7.4, patched in 2.7.5). Pinned forward within the
|
|
2.x major that ASP.NET Core 10 targets. Revisit when the ASP.NET Core package
|
|
itself moves off 2.0.0.
|
|
-->
|
|
<PackageVersion Include="Microsoft.OpenApi" Version="2.11.0" />
|
|
|
|
<!--
|
|
Microsoft.EntityFrameworkCore.Sqlite 10.0.10 resolves SQLitePCLRaw 2.1.11, whose bundled
|
|
SQLite build is covered by GHSA-2m69-gcr7-jv3q (high). 2.1.12 is the fix and is a patch bump
|
|
inside the minor EF asks for, so nothing needs to move. Pinned as a family: the bundle, the
|
|
core, the provider and the native library ship in lockstep and a mixed set is a loader error
|
|
at runtime rather than a build failure.
|
|
|
|
SQLitePCLRaw 3.x exists and is deliberately not used here. EF Core 10 is built against 2.1.x,
|
|
and 3.0 is also where bundle_e_sqlcipher was deprecated — which is one of the reasons the
|
|
local cache does not use SQLCipher at all. See DodoSSH.Client.Storage.
|
|
-->
|
|
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.12" />
|
|
<PackageVersion Include="SQLitePCLRaw.core" Version="2.1.12" />
|
|
<PackageVersion Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.12" />
|
|
<PackageVersion Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.12" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Persistence">
|
|
<!--
|
|
EF Core pinned explicitly. The Npgsql provider asks only for 10.0.4 while
|
|
Microsoft.EntityFrameworkCore.Design pulls 10.0.10, and because Design is
|
|
PrivateAssets=all that higher version does not flow to referencing projects — which
|
|
produces a CS1705 in any test project that references Infrastructure. Pinning here lifts
|
|
every project to one version via central transitive pinning.
|
|
-->
|
|
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.10" />
|
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.10" />
|
|
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
|
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10" />
|
|
<!--
|
|
Verified compatible with EF 10 before adopting; the plan flagged this package as
|
|
historically lagging EF majors. Fallback if it ever blocks an upgrade is explicit
|
|
HasColumnName in every IEntityTypeConfiguration: more code, zero risk.
|
|
-->
|
|
<PackageVersion Include="EFCore.NamingConventions" Version="10.0.1" />
|
|
<!--
|
|
The client's local cache. Plain SQLite, deliberately not SQLCipher: the rows are already
|
|
ciphertext, so an encrypted database file would add a native dependency and a licence
|
|
obligation to protect bytes that are protected already. SQLitePCLRaw's own
|
|
bundle_e_sqlcipher is deprecated as of 3.0 besides. See DodoSSH.Client.Storage.
|
|
-->
|
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Cryptography">
|
|
<!--
|
|
NSec wraps libsodium. Chosen over the BCL because .NET has no X25519 or Ed25519, and
|
|
because ChaCha20Poly1305.IsSupported is false on macOS, which rules out the in-box
|
|
AEAD for a cross-platform client. NSec also holds key material in libsodium's
|
|
guarded, non-swappable memory, which a byte[] cannot do. See docs/crypto.md.
|
|
|
|
26.4.0 targets net9.0; net10.0 consumes it by forward compatibility. Native binaries
|
|
arrive via the libsodium package, pinned here because central transitive pinning
|
|
requires it to be declared.
|
|
-->
|
|
<PackageVersion Include="NSec.Cryptography" Version="26.4.0" />
|
|
<PackageVersion Include="libsodium" Version="1.0.22" />
|
|
<!-- Managed differential oracle for the crypto test suite only. -->
|
|
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Desktop client">
|
|
<!--
|
|
SSH.NET already covers PTY shells, all three auth methods, ed25519/RSA/ECDSA, encrypted
|
|
keys including PuTTY .ppk, SFTP, and local/remote/dynamic forwarding. The gaps are
|
|
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
|
|
ProxyJump both go through a loopback TCP bridge. See docs/adr/.
|
|
-->
|
|
<PackageVersion Include="SSH.NET" Version="2025.1.0" />
|
|
|
|
<!--
|
|
Avalonia 12.1.0, with the WebView control on 12.0.1 — the latest it has shipped. Its
|
|
dependency is Avalonia >= 12.0.0 with no upper bound and it targets net10.0, so the skew
|
|
is fine. Checked rather than assumed, because a control package lagging the core version
|
|
is exactly where a silent runtime mismatch would hide.
|
|
-->
|
|
<PackageVersion Include="Avalonia" Version="12.1.0" />
|
|
<PackageVersion Include="Avalonia.Desktop" Version="12.1.0" />
|
|
<PackageVersion Include="Avalonia.Themes.Fluent" Version="12.1.0" />
|
|
<PackageVersion Include="Avalonia.Fonts.Inter" Version="12.1.0" />
|
|
<PackageVersion Include="Avalonia.Controls.WebView" Version="12.0.1" />
|
|
|
|
<!--
|
|
Lets a test lay out real XAML and measure it, which is the only way this repository can catch a
|
|
control clipped off the bottom of a column — the defect this window has already shipped once. Pinned
|
|
to the core version exactly rather than allowed to drift: the whole value of the harness is that the
|
|
numbers it measures are the numbers the application renders.
|
|
-->
|
|
<PackageVersion Include="Avalonia.Headless" Version="12.1.0" />
|
|
|
|
<!--
|
|
Source-generated MVVM, so there is no reflection and trimming stays viable. ReactiveUI's one
|
|
real advantage is observable composition over streams, and the place that would help — the
|
|
terminal data plane — is Pipelines and channel code rather than view models.
|
|
-->
|
|
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Analyzers">
|
|
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" />
|
|
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="5.6.0" />
|
|
<PackageVersion Include="Meziantou.Analyzer" Version="3.0.134" />
|
|
</ItemGroup>
|
|
|
|
<ItemGroup Label="Testing">
|
|
<!--
|
|
xunit.v3 runs on Microsoft.Testing.Platform, not VSTest. Microsoft.NET.Test.Sdk and
|
|
coverlet.collector are VSTest components: referencing them alongside MTP raises
|
|
MTP0001 and their collector never runs, so neither is referenced.
|
|
|
|
No coverage collector yet. Microsoft.Testing.Extensions.CodeCoverage 18.9.0 pulls
|
|
Microsoft.Testing.Platform.MSBuild 1.9.1, which is built against MTP 1.x and throws
|
|
TypeLoadException on IDataConsumer against the MTP 2.3.x that xunit.v3 3.2.2 brings.
|
|
Coverage gates are an M3 concern (90% on Domain and Authorization); pick a version
|
|
aligned with MTP 2.x then rather than carrying a broken dependency until it matters.
|
|
-->
|
|
<PackageVersion Include="xunit.v3" Version="3.2.2" />
|
|
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
|
<PackageVersion Include="NSubstitute" Version="6.0.0" />
|
|
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.13.0" />
|
|
<!-- Generic container, for the OpenSSH server the SSH suite talks to. -->
|
|
<PackageVersion Include="Testcontainers" Version="4.13.0" />
|
|
<PackageVersion Include="Respawn" Version="7.0.0" />
|
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.10" />
|
|
<!--
|
|
Stands in for the identity provider so integration tests exercise the real JwtBearer
|
|
pipeline. A TestAuthHandler that bypasses it would hide exactly the claim-mapping
|
|
mistakes that cause real authorization holes.
|
|
-->
|
|
<PackageVersion Include="WireMock.Net" Version="2.13.0" />
|
|
</ItemGroup>
|
|
|
|
</Project>
|