Stufe 4c (#764): Bezeichner auf Englisch - Typen, Methoden, Felder, Parameter, Locals, Testnamen
Mit Roslyn umbenannt (Verweise folgen, Zeichenketten bleiben unberuehrt). Nicht umbenannt, weil gespeichert oder ueber die API gebunden (Stufe 4e): Properties, Record-Parameter, Enum-Member sowie die Parameter der Endpunkt-Handler (Routen-/Query-Bindung ueber den Namen). Die JSON-Felder anonymer Objekte bleiben ausdruecklich benannt (JsonFilter: pfad). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
837a634a22
commit
77062facc9
@@ -29,7 +29,7 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
catch { /* A leftover test folder does not turn a run red. */ }
|
||||
}
|
||||
|
||||
private static SecretKeyRing Bund(params ISecretProvider[] anbieter) => new(anbieter);
|
||||
private static SecretKeyRing Ring(params ISecretProvider[] provider) => new(provider);
|
||||
|
||||
/// <summary>
|
||||
/// The heart of the matter: two slots, one key.
|
||||
@@ -38,18 +38,18 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// recovery path would then open something, but not the existing values.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Zwei_Faecher_fuehren_zu_demselben_Schluessel()
|
||||
public void Two_slots_lead_to_the_same_key()
|
||||
{
|
||||
var maschine = new PlainFileSecretProvider();
|
||||
var bund = Bund(maschine, PassphraseSecretProvider.With("geheim"));
|
||||
var machine = new PlainFileSecretProvider();
|
||||
var ring = Ring(machine, PassphraseSecretProvider.With("geheim"));
|
||||
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "geheim");
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "geheim");
|
||||
|
||||
// Only with the passphrase, without the machine slot - the move.
|
||||
var nachUmzug = Bund(PassphraseSecretProvider.With("geheim")).Load(Pfad);
|
||||
var afterMove = Ring(PassphraseSecretProvider.With("geheim")).Load(Pfad);
|
||||
|
||||
nachUmzug.ShouldBe(schluessel);
|
||||
afterMove.ShouldBe(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,15 +59,15 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// all credentials would be lost here.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Nach_einem_Maschinenwechsel_oeffnet_die_Passphrase()
|
||||
public void After_a_machine_change_the_passphrase_opens()
|
||||
{
|
||||
var bund = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("umzug"));
|
||||
var schluessel = bund.Load(Pfad);
|
||||
var ring = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("umzug"));
|
||||
var key = ring.Load(Pfad);
|
||||
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "umzug");
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "umzug");
|
||||
|
||||
var neueMaschine = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("umzug"));
|
||||
neueMaschine.Load(Pfad).ShouldBe(schluessel);
|
||||
var newMachine = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("umzug"));
|
||||
newMachine.Load(Pfad).ShouldBe(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,19 +79,19 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// <em>not</em> happen — see <see cref="Ein_Umzug_legt_den_Schluessel_nicht_im_Klartext_ab"/>.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Nach_dem_Umzug_entsteht_das_Maschinenfach_neu()
|
||||
public void After_the_move_the_machine_slot_is_created_anew()
|
||||
{
|
||||
var bund = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("umzug"));
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "umzug");
|
||||
var ring = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("umzug"));
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "umzug");
|
||||
|
||||
// New machine with a working machine slot: it does not open yet,
|
||||
// but the passphrase does - and afterwards the slot is created.
|
||||
var neueMaschine = Bund(new MaschinenFach(), PassphraseSecretProvider.With("umzug"));
|
||||
neueMaschine.Load(Pfad).ShouldBe(schluessel);
|
||||
var newMachine = Ring(new MachineSlot(), PassphraseSecretProvider.With("umzug"));
|
||||
newMachine.Load(Pfad).ShouldBe(key);
|
||||
|
||||
// From now on the machine slot alone suffices, without a passphrase.
|
||||
Bund(new MaschinenFach()).Load(Pfad).ShouldBe(schluessel);
|
||||
Ring(new MachineSlot()).Load(Pfad).ShouldBe(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,15 +105,15 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
[Fact]
|
||||
public void Ein_Umzug_legt_den_Schluessel_nicht_im_Klartext_ab()
|
||||
{
|
||||
var bund = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("umzug"));
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "umzug");
|
||||
var ring = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("umzug"));
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "umzug");
|
||||
|
||||
var ohneMaschinenfach = Bund(
|
||||
var withoutMachineSlot = Ring(
|
||||
new PlainFileSecretProvider(), PassphraseSecretProvider.With("umzug"));
|
||||
|
||||
ohneMaschinenfach.Load(Pfad).ShouldBe(schluessel);
|
||||
ohneMaschinenfach.Slots.ShouldNotContain(s => s.Provider == "file");
|
||||
withoutMachineSlot.Load(Pfad).ShouldBe(key);
|
||||
withoutMachineSlot.Slots.ShouldNotContain(s => s.Provider == "file");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -121,15 +121,15 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// key.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Eine_falsche_Passphrase_oeffnet_nichts()
|
||||
public void A_wrong_passphrase_opens_nothing()
|
||||
{
|
||||
var bund = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("richtig"));
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "richtig");
|
||||
var ring = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("richtig"));
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "richtig");
|
||||
|
||||
var falsch = Bund(new NieOeffnendesFach(), PassphraseSecretProvider.With("falsch"));
|
||||
var wrong = Ring(new NeverOpeningSlot(), PassphraseSecretProvider.With("falsch"));
|
||||
|
||||
Should.Throw<CryptographicException>(() => falsch.Load(Pfad));
|
||||
Should.Throw<CryptographicException>(() => wrong.Load(Pfad));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,14 +141,14 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// credentials without anyone seeing the connection.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Wenn_kein_Fach_aufgeht_wird_abgebrochen_statt_neu_erzeugt()
|
||||
public void If_no_slot_opens_it_aborts_instead_of_regenerating()
|
||||
{
|
||||
Bund(new PlainFileSecretProvider()).Load(Pfad);
|
||||
var vorher = File.ReadAllBytes(Pfad);
|
||||
Ring(new PlainFileSecretProvider()).Load(Pfad);
|
||||
var before = File.ReadAllBytes(Pfad);
|
||||
|
||||
Should.Throw<CryptographicException>(() => Bund(new NieOeffnendesFach()).Load(Pfad));
|
||||
Should.Throw<CryptographicException>(() => Ring(new NeverOpeningSlot()).Load(Pfad));
|
||||
|
||||
File.ReadAllBytes(Pfad).ShouldBe(vorher, "Die Datei darf dabei nicht angefasst werden.");
|
||||
File.ReadAllBytes(Pfad).ShouldBe(before, "Die Datei darf dabei nicht angefasst werden.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -156,23 +156,23 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// it fit for backups.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Mit_Passphrase_steht_der_Schluessel_nirgends_im_Klartext()
|
||||
public void With_passphrase_the_key_is_nowhere_in_plaintext()
|
||||
{
|
||||
var bund = Bund(new DpapiSecretProvider(), PassphraseSecretProvider.With("geheim"),
|
||||
var ring = Ring(new DpapiSecretProvider(), PassphraseSecretProvider.With("geheim"),
|
||||
new PlainFileSecretProvider());
|
||||
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "geheim");
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "geheim");
|
||||
|
||||
var inhalt = File.ReadAllText(Pfad);
|
||||
var klartext = Convert.ToBase64String(schluessel);
|
||||
var content = File.ReadAllText(Pfad);
|
||||
var plaintext = Convert.ToBase64String(key);
|
||||
|
||||
inhalt.ShouldNotContain(klartext);
|
||||
content.ShouldNotContain(plaintext);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
// With a machine slot the unprotected one drops away.
|
||||
inhalt.ShouldNotContain("\"file\"");
|
||||
content.ShouldNotContain("\"file\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,39 +181,39 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// one day. That's why it is named beforehand.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Ein_Bund_ohne_Wiederherstellung_meldet_das()
|
||||
public void A_ring_without_recovery_reports_it()
|
||||
{
|
||||
var bund = Bund(new PlainFileSecretProvider());
|
||||
bund.Load(Pfad);
|
||||
var ring = Ring(new PlainFileSecretProvider());
|
||||
ring.Load(Pfad);
|
||||
|
||||
bund.Problems.ShouldNotBeEmpty();
|
||||
bund.Problems.ShouldContain(p => p.Contains("Wiederherstellungsfach"));
|
||||
bund.Problems.ShouldContain(p => p.Contains("ungeschützt"));
|
||||
ring.Problems.ShouldNotBeEmpty();
|
||||
ring.Problems.ShouldContain(p => p.Contains("Wiederherstellungsfach"));
|
||||
ring.Problems.ShouldContain(p => p.Contains("ungeschützt"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mit_Passphrase_ist_der_Bund_in_Ordnung()
|
||||
public void With_passphrase_the_ring_is_fine()
|
||||
{
|
||||
var bund = Bund(new DpapiSecretProvider(), PassphraseSecretProvider.With("geheim"),
|
||||
var ring = Ring(new DpapiSecretProvider(), PassphraseSecretProvider.With("geheim"),
|
||||
new PlainFileSecretProvider());
|
||||
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "geheim");
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "geheim");
|
||||
|
||||
bund.Problems.ShouldNotContain(p => p.Contains("Wiederherstellungsfach"));
|
||||
ring.Problems.ShouldNotContain(p => p.Contains("Wiederherstellungsfach"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Without a passphrase no recovery slot comes about — there is nothing to make it with.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Ohne_Passphrase_gibt_es_kein_Wiederherstellungsfach()
|
||||
public void Without_passphrase_there_is_no_recovery_slot()
|
||||
{
|
||||
var bund = Bund(new PlainFileSecretProvider(), new PassphraseSecretProvider(() => null));
|
||||
bund.Load(Pfad);
|
||||
var ring = Ring(new PlainFileSecretProvider(), new PassphraseSecretProvider(() => null));
|
||||
ring.Load(Pfad);
|
||||
|
||||
bund.Slots.ShouldNotContain(s => s.Provider == "passphrase");
|
||||
bund.Slots.ShouldContain(s => s.Provider == "file");
|
||||
ring.Slots.ShouldNotContain(s => s.Provider == "passphrase");
|
||||
ring.Slots.ShouldContain(s => s.Provider == "file");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,25 +225,25 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// should please store a passphrase.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Eine_bereitstehende_Passphrase_wird_nachtraeglich_uebernommen()
|
||||
public void A_ready_passphrase_is_adopted_afterwards()
|
||||
{
|
||||
// First without: only the unprotected slot comes about.
|
||||
Bund(new PlainFileSecretProvider(), new PassphraseSecretProvider(() => null)).Load(Pfad);
|
||||
Ring(new PlainFileSecretProvider(), new PassphraseSecretProvider(() => null)).Load(Pfad);
|
||||
|
||||
// Then with: the recovery slot is added, the unprotected one drops away.
|
||||
var mit = Bund(new PlainFileSecretProvider(), PassphraseSecretProvider.With("spaeter"));
|
||||
var schluessel = mit.Load(Pfad);
|
||||
var withValue = Ring(new PlainFileSecretProvider(), PassphraseSecretProvider.With("spaeter"));
|
||||
var key = withValue.Load(Pfad);
|
||||
|
||||
mit.Slots.ShouldContain(s => s.Provider == "passphrase");
|
||||
withValue.Slots.ShouldContain(s => s.Provider == "passphrase");
|
||||
|
||||
// The unprotected slot stays: the automatic path takes nothing away. Removing
|
||||
// it because an environment variable happens to be set would be an
|
||||
// irreversible change nobody asked for.
|
||||
mit.Slots.ShouldContain(s => s.Provider == "file");
|
||||
mit.Problems.ShouldContain(p => p.Contains("ungeschützt"));
|
||||
withValue.Slots.ShouldContain(s => s.Provider == "file");
|
||||
withValue.Problems.ShouldContain(p => p.Contains("ungeschützt"));
|
||||
|
||||
// And it is the same key as before.
|
||||
Bund(PassphraseSecretProvider.With("spaeter")).Load(Pfad).ShouldBe(schluessel);
|
||||
Ring(PassphraseSecretProvider.With("spaeter")).Load(Pfad).ShouldBe(key);
|
||||
}
|
||||
|
||||
// ─── Taking over the legacy formats ───
|
||||
@@ -253,12 +253,12 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// nothing — the same key, just wrapped differently.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Ein_roher_Altschluessel_wird_uebernommen()
|
||||
public void A_raw_legacy_key_is_adopted()
|
||||
{
|
||||
var alt = RandomNumberGenerator.GetBytes(32);
|
||||
File.WriteAllBytes(Pfad, alt);
|
||||
var old = RandomNumberGenerator.GetBytes(32);
|
||||
File.WriteAllBytes(Pfad, old);
|
||||
|
||||
Bund(new PlainFileSecretProvider()).Load(Pfad).ShouldBe(alt);
|
||||
Ring(new PlainFileSecretProvider()).Load(Pfad).ShouldBe(old);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -266,21 +266,21 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// is gone, the value the same.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Nach_der_Uebernahme_steht_das_Faecherformat_in_der_Datei()
|
||||
public void After_adoption_the_slot_format_is_in_the_file()
|
||||
{
|
||||
var alt = RandomNumberGenerator.GetBytes(32);
|
||||
File.WriteAllBytes(Pfad, alt);
|
||||
var old = RandomNumberGenerator.GetBytes(32);
|
||||
File.WriteAllBytes(Pfad, old);
|
||||
|
||||
var bund = Bund(new PlainFileSecretProvider(), PassphraseSecretProvider.With("neu"));
|
||||
var schluessel = bund.Load(Pfad);
|
||||
bund.SetRecoveryPassphrase(Pfad, schluessel, "neu");
|
||||
var ring = Ring(new PlainFileSecretProvider(), PassphraseSecretProvider.With("neu"));
|
||||
var key = ring.Load(Pfad);
|
||||
ring.SetRecoveryPassphrase(Pfad, key, "neu");
|
||||
|
||||
var datei = JsonSerializer.Deserialize<SecretKeyRingFile>(File.ReadAllText(Pfad))!;
|
||||
datei.Version.ShouldBe(3);
|
||||
datei.Slots.ShouldContain(s => s.Provider == "passphrase");
|
||||
|
||||
// And the value has not changed - otherwise all enc:v2 values would be lost.
|
||||
Bund(PassphraseSecretProvider.With("neu")).Load(Pfad).ShouldBe(alt);
|
||||
Ring(PassphraseSecretProvider.With("neu")).Load(Pfad).ShouldBe(old);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -288,15 +288,15 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
/// encrypted values unreadable without anyone finding out why.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Eine_beschaedigte_Datei_wird_nicht_stillschweigend_ersetzt()
|
||||
public void A_damaged_file_is_not_replaced_silently()
|
||||
{
|
||||
File.WriteAllBytes(Pfad, RandomNumberGenerator.GetBytes(17));
|
||||
|
||||
Should.Throw<CryptographicException>(() => Bund(new PlainFileSecretProvider()).Load(Pfad));
|
||||
Should.Throw<CryptographicException>(() => Ring(new PlainFileSecretProvider()).Load(Pfad));
|
||||
}
|
||||
|
||||
/// <summary>A machine slot that works here — the substitute for DPAPI in the test.</summary>
|
||||
private sealed class MaschinenFach : ISecretProvider
|
||||
private sealed class MachineSlot : ISecretProvider
|
||||
{
|
||||
public string Name => "dpapi";
|
||||
public string Description => "Maschinenfach (Test)";
|
||||
@@ -311,7 +311,7 @@ public sealed class SecretKeyRingTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>A slot that never opens — the new machine, the foreign user account.</summary>
|
||||
private sealed class NieOeffnendesFach : ISecretProvider
|
||||
private sealed class NeverOpeningSlot : ISecretProvider
|
||||
{
|
||||
public string Name => "dpapi";
|
||||
public string Description => "Maschinenfach (Test: geht nie auf)";
|
||||
|
||||
@@ -34,7 +34,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_verschluesselter_Wert_laesst_sich_wieder_lesen()
|
||||
public void An_encrypted_value_can_be_read_again()
|
||||
{
|
||||
const string secret = "sk-or-v1-sehr-geheim-12345";
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Der_verschluesselte_Wert_enthaelt_den_Klartext_nicht()
|
||||
public void The_encrypted_value_does_not_contain_the_plaintext()
|
||||
{
|
||||
const string secret = "sk-or-v1-sehr-geheim-12345";
|
||||
|
||||
@@ -55,14 +55,14 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Verschluesselte_Werte_sind_als_solche_erkennbar()
|
||||
public void Encrypted_values_are_recognisable_as_such()
|
||||
{
|
||||
SecretProtector.IsProtected(SecretProtector.Protect("geheim")).ShouldBeTrue();
|
||||
SecretProtector.IsProtected("klartext").ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mehrfaches_Verschluesseln_veraendert_nichts()
|
||||
public void Encrypting_repeatedly_changes_nothing()
|
||||
{
|
||||
// Important, because when saving it is not known whether a value was already
|
||||
// encrypted.
|
||||
@@ -74,7 +74,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Klartext_aus_alten_Konfigurationen_wird_unveraendert_gelesen()
|
||||
public void Plaintext_from_old_configurations_is_read_unchanged()
|
||||
{
|
||||
// Two reasons, not just one:
|
||||
//
|
||||
@@ -90,14 +90,14 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public void Leere_Werte_bleiben_leer(string? value)
|
||||
public void Empty_values_stay_empty(string? value)
|
||||
{
|
||||
SecretProtector.Protect(value).ShouldBe(value);
|
||||
SecretProtector.Unprotect(value).ShouldBe(value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_beschaedigter_Wert_wird_gemeldet_statt_stillschweigend_durchgereicht()
|
||||
public void A_damaged_value_is_reported_instead_of_silently_passed_on()
|
||||
{
|
||||
// After a change of user or machine the value is not readable here. Outputting it as
|
||||
// plain text would send an unusable key to the API.
|
||||
@@ -109,7 +109,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Umlaute_und_Sonderzeichen_ueberstehen_den_Vorgang()
|
||||
public void Umlauts_and_special_characters_survive_the_operation()
|
||||
{
|
||||
const string secret = "P@ssw0rt mit Grüßen & Ümlauten 🦀";
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Neue_Werte_werden_im_plattformuebergreifenden_Format_geschrieben()
|
||||
public void New_values_are_written_in_the_cross_platform_format()
|
||||
{
|
||||
// The core of the port: under Linux the previous version returned the plain text
|
||||
// here. On a server that gets backed up, that was worse than nothing.
|
||||
@@ -131,7 +131,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dieselbe_Eingabe_ergibt_zweimal_verschiedene_Ausgaben()
|
||||
public void The_same_input_gives_two_different_outputs()
|
||||
{
|
||||
// AES-GCM with a random nonce: identical values must not become identical
|
||||
// blocks, otherwise the configuration file reveals where the same password
|
||||
@@ -145,7 +145,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_veraenderter_Block_wird_erkannt_und_nicht_entschluesselt()
|
||||
public void A_modified_block_is_recognised_and_not_decrypted()
|
||||
{
|
||||
// The purpose of the authentication in AES-GCM: a tampered block must
|
||||
// not yield half-guessed plain text.
|
||||
@@ -158,27 +158,27 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_fremder_Schluessel_kann_den_Wert_nicht_lesen()
|
||||
public void A_foreign_key_cannot_read_the_value()
|
||||
{
|
||||
// Exactly the property that makes up the protection: the configuration file alone
|
||||
// is of no use on another machine, because the key does not travel along.
|
||||
var value = SecretProtector.Protect("sk-or-v1-geheim");
|
||||
|
||||
var fremd = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N"));
|
||||
var foreign = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N"));
|
||||
try
|
||||
{
|
||||
SecretKeyStore.UseDirectory(fremd);
|
||||
SecretKeyStore.UseDirectory(foreign);
|
||||
Should.Throw<SecretProtectionException>(() => SecretProtector.Unprotect(value));
|
||||
}
|
||||
finally
|
||||
{
|
||||
SecretKeyStore.UseDirectory(_keyDir);
|
||||
try { Directory.Delete(fremd, recursive: true); } catch { }
|
||||
try { Directory.Delete(foreign, recursive: true); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Der_Schluessel_wird_einmal_angelegt_und_dann_wiederverwendet()
|
||||
public void The_key_is_created_once_and_then_reused()
|
||||
{
|
||||
var first = SecretProtector.Protect("geheim");
|
||||
var keyBytes = File.ReadAllBytes(SecretKeyStore.KeyFilePath);
|
||||
@@ -198,7 +198,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
/// </summary>
|
||||
[LinuxFact]
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("linux")]
|
||||
public void Die_Schluesseldatei_ist_nur_fuer_den_Besitzer_lesbar()
|
||||
public void The_key_file_is_readable_by_the_owner_only()
|
||||
{
|
||||
SecretProtector.Protect("geheim");
|
||||
|
||||
@@ -207,7 +207,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
[LinuxFact]
|
||||
public void Ein_DPAPI_Wert_aus_einer_Windows_Instanz_wird_mit_Begruendung_abgewiesen()
|
||||
public void A_DPAPI_value_from_a_Windows_instance_is_rejected_with_a_reason()
|
||||
{
|
||||
// The moving case. Passing it on as plain text would send an unusable
|
||||
// key to the API — the message must say what to do.
|
||||
@@ -246,7 +246,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
[InlineData("allowedTables", false)]
|
||||
[InlineData("smtpHost", false)]
|
||||
[InlineData("rootPath", false)]
|
||||
public void Nur_bekannte_Geheimnisfelder_werden_behandelt(string key, bool expected)
|
||||
public void Only_known_secret_fields_are_handled(string key, bool expected)
|
||||
{
|
||||
ConfigSecrets.IsSecretKey(key).ShouldBe(expected);
|
||||
}
|
||||
@@ -271,7 +271,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Geheimnisse_einer_Agentenkonfiguration_werden_verschluesselt()
|
||||
public void Secrets_of_an_agent_configuration_are_encrypted()
|
||||
{
|
||||
var agent = AgentWithSecrets();
|
||||
|
||||
@@ -300,7 +300,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Nicht_geheime_Felder_bleiben_lesbar()
|
||||
public void Non_secret_fields_stay_readable()
|
||||
{
|
||||
var agent = AgentWithSecrets();
|
||||
|
||||
@@ -311,7 +311,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Der_Rundlauf_stellt_die_Originalwerte_wieder_her()
|
||||
public void The_round_trip_restores_the_original_values()
|
||||
{
|
||||
var agent = AgentWithSecrets();
|
||||
|
||||
@@ -324,7 +324,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Werte_aus_deserialisiertem_JSON_werden_ebenfalls_erfasst()
|
||||
public void Values_from_deserialised_JSON_are_captured_too()
|
||||
{
|
||||
// After deserialization the values sit in the dictionary as JsonElement,
|
||||
// not as string.
|
||||
@@ -342,7 +342,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Der_Instanzschluessel_und_Telegram_Zugangsdaten_werden_erfasst()
|
||||
public void The_instance_key_and_Telegram_credentials_are_captured()
|
||||
{
|
||||
var config = new InstanceConfig
|
||||
{
|
||||
@@ -367,7 +367,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Eine_Konfiguration_ohne_Telegram_stoert_nicht()
|
||||
public void A_configuration_without_Telegram_does_no_harm()
|
||||
{
|
||||
var config = new InstanceConfig { OpenRouterApiKey = "sk-test", TelegramClient = null };
|
||||
|
||||
@@ -375,7 +375,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Leere_Geheimnisfelder_bleiben_leer()
|
||||
public void Empty_secret_fields_stay_empty()
|
||||
{
|
||||
var agent = new AgentConfig
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user