using ClawdDotNet.App.Services; using ClawdDotNet.Core.Config; using Shouldly; namespace ClawdDotNet.App.Tests.Services; /// /// Zugangsdaten der Instanz müssen den Weg über die Platte und zurück überstehen. /// /// SaveInstanceConfig verschlüsselte beim Schreiben, LoadInstanceConfig /// entschlüsselte beim Lesen aber nicht. Was einmal über die Oberfläche gespeichert war, /// kam als enc:v2:… zurück und wurde als Wert benutzt — der OpenRouter-Schlüssel /// ging so verschlüsselt an die API. /// /// Unentdeckt geblieben ist es, weil ConfigLoader.LoadAsync es richtig macht /// und im laufenden Programm gar nicht aufgerufen wird: Gebraucht wird von dieser Klasse /// nur Migrate. /// public sealed class InstanceSecretRoundtripTests { [Fact] public void Zugangsdaten_kommen_im_Klartext_zurueck() { using var host = new TestHost(); host.Host.Instance.OpenRouterApiKey = "sk-or-v1-beispielschluessel"; host.Host.Instance.WebUiToken = "ein-hinreichend-langes-token"; host.Host.Instance.Watchdog.AgentToken = "dc-token-beispiel"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); var geladen = host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath); geladen.OpenRouterApiKey.ShouldBe("sk-or-v1-beispielschluessel"); geladen.WebUiToken.ShouldBe("ein-hinreichend-langes-token"); geladen.Watchdog.AgentToken.ShouldBe("dc-token-beispiel"); } [Fact] public void In_der_Datei_stehen_sie_trotzdem_geschuetzt() { // Die Gegenprobe: Entschluesseln beim Laden darf nicht heissen, dass gar nicht // mehr verschluesselt wird. using var host = new TestHost(); host.Host.Instance.OpenRouterApiKey = "sk-or-v1-beispielschluessel"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); var rohtext = File.ReadAllText( Path.Combine(host.Host.InstancePath, "InstanceSettings.json")); rohtext.ShouldNotContain("sk-or-v1-beispielschluessel"); } [Fact] public void Klartext_aus_aelteren_Dateien_bleibt_lesbar() { // Entschluesseln muss gefahrlos auf etwas anwendbar sein, das nie verschluesselt // war — sonst braeche der erste Start nach dem Update jede bestehende Instanz. using var host = new TestHost(); var pfad = Path.Combine(host.Host.InstancePath, "InstanceSettings.json"); File.WriteAllText(pfad, """ { "instanceId": "alt", "instanceName": "Alt", "openRouterApiKey": "klartext-von-hand-eingetragen", "webUiToken": "auch-klartext" } """); var geladen = host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath); geladen.OpenRouterApiKey.ShouldBe("klartext-von-hand-eingetragen"); geladen.WebUiToken.ShouldBe("auch-klartext"); } [Fact] public void Zweimal_speichern_verschluesselt_nicht_zweimal() { using var host = new TestHost(); host.Host.Instance.WebUiToken = "ein-hinreichend-langes-token"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath) .WebUiToken.ShouldBe("ein-hinreichend-langes-token"); } }