Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch
Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft. Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch. WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b umbenannten Parameter angeglichen. Dabei behoben: - 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue" gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day. - Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine CSS-Klasse gab - jetzt "success" wie laufende Instanzen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
da4ceac7d5
commit
837a634a22
@@ -7,13 +7,13 @@ using Shouldly;
|
||||
namespace ClawdDotNet.Core.Tests.Security;
|
||||
|
||||
/// <summary>
|
||||
/// S7 aus der Bestandsaufnahme: OpenRouter-Schlüssel, Datenbank-Verbindungszeichenfolgen
|
||||
/// samt Passwort, Mail-Zugangsdaten und das Telegram-2FA-Passwort lagen im Klartext in
|
||||
/// den JSON-Dateien. Wer die Dateien lesen konnte, hatte alle Zugänge.
|
||||
/// S7 from the inventory: OpenRouter keys, database connection strings
|
||||
/// including password, mail credentials and the Telegram 2FA password were in plain text in
|
||||
/// the JSON files. Whoever could read the files had all access.
|
||||
///
|
||||
/// Mit der Linux-Portierung kam <c>enc:v2</c> dazu (AES-GCM statt DPAPI). Die Tests
|
||||
/// laufen deshalb gegen einen eigenen Schlüssel in einem Wegwerfverzeichnis — sonst
|
||||
/// würden sie den echten Benutzerschlüssel anlegen oder lesen.
|
||||
/// With the Linux port <c>enc:v2</c> was added (AES-GCM instead of DPAPI). The tests
|
||||
/// therefore run against a key of their own in a throwaway directory — otherwise
|
||||
/// they would create or read the real user key.
|
||||
/// </summary>
|
||||
[Collection(SecretKeyCollection.Name)]
|
||||
public sealed class SecretProtectorTests : IDisposable
|
||||
@@ -30,7 +30,7 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
{
|
||||
SecretKeyStore.UseDirectory(null);
|
||||
try { Directory.Delete(_keyDir, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
catch { /* Cleaning up is secondary */ }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -64,8 +64,8 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Mehrfaches_Verschluesseln_veraendert_nichts()
|
||||
{
|
||||
// Wichtig, weil beim Speichern nicht bekannt ist, ob ein Wert schon
|
||||
// verschlüsselt war.
|
||||
// Important, because when saving it is not known whether a value was already
|
||||
// encrypted.
|
||||
var once = SecretProtector.Protect("geheim");
|
||||
var twice = SecretProtector.Protect(once);
|
||||
|
||||
@@ -76,14 +76,14 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Klartext_aus_alten_Konfigurationen_wird_unveraendert_gelesen()
|
||||
{
|
||||
// Zwei Gründe, nicht nur einer:
|
||||
// Two reasons, not just one:
|
||||
//
|
||||
// 1. Bestehende Installationen müssen ohne Zutun weiterlaufen.
|
||||
// 2. Die Erstinstallation über den update-agent schreibt Lizenzschlüssel und
|
||||
// Deploymentcenter-Token als Klartext in die Settings.json — der Installer
|
||||
// kennt unsere Verschlüsselung nicht. Beim ersten Speichern werden sie
|
||||
// übernommen und verschlüsselt. Wer diesen Zweig entfernt, weil er nach
|
||||
// Altlast aussieht, sperrt jede frisch installierte Instanz aus.
|
||||
// 1. Existing installations must keep running without intervention.
|
||||
// 2. The initial installation via the update-agent writes license key and
|
||||
// Deploymentcenter token as plain text into Settings.json — the installer
|
||||
// does not know our encryption. On the first save they are
|
||||
// taken over and encrypted. Whoever removes this branch because it looks like
|
||||
// legacy locks out every freshly installed instance.
|
||||
SecretProtector.Unprotect("sk-or-alter-klartext").ShouldBe("sk-or-alter-klartext");
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Ein_beschaedigter_Wert_wird_gemeldet_statt_stillschweigend_durchgereicht()
|
||||
{
|
||||
// Nach Benutzer- oder Rechnerwechsel ist der Wert hier nicht lesbar. Ihn als
|
||||
// Klartext auszugeben würde einen unbrauchbaren Schlüssel an die API schicken.
|
||||
// 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.
|
||||
Should.Throw<SecretProtectionException>(
|
||||
() => SecretProtector.Unprotect("enc:v1:das-ist-kein-gueltiger-block"));
|
||||
|
||||
@@ -117,14 +117,14 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Plattformübergreifendes Format (enc:v2)
|
||||
// Cross-platform format (enc:v2)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Neue_Werte_werden_im_plattformuebergreifenden_Format_geschrieben()
|
||||
{
|
||||
// Der Kern der Portierung: Unter Linux gab die vorige Fassung hier den Klartext
|
||||
// zurück. Auf einem Server, der gesichert wird, war das schlechter als nichts.
|
||||
// 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.
|
||||
var value = SecretProtector.Protect("sk-or-v1-geheim");
|
||||
|
||||
value.ShouldStartWith("enc:v2:");
|
||||
@@ -133,9 +133,9 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Dieselbe_Eingabe_ergibt_zweimal_verschiedene_Ausgaben()
|
||||
{
|
||||
// AES-GCM mit zufälligem Nonce: Aus gleichen Werten dürfen keine gleichen
|
||||
// Blöcke werden, sonst verrät die Konfigurationsdatei, wo dasselbe Passwort
|
||||
// mehrfach benutzt wird.
|
||||
// AES-GCM with a random nonce: identical values must not become identical
|
||||
// blocks, otherwise the configuration file reveals where the same password
|
||||
// is used several times.
|
||||
var a = SecretProtector.Protect("dasselbe-passwort");
|
||||
var b = SecretProtector.Protect("dasselbe-passwort");
|
||||
|
||||
@@ -147,8 +147,8 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Ein_veraenderter_Block_wird_erkannt_und_nicht_entschluesselt()
|
||||
{
|
||||
// Der Zweck der Authentifizierung in AES-GCM: Ein manipulierter Block darf
|
||||
// keinen halb geratenen Klartext ergeben.
|
||||
// The purpose of the authentication in AES-GCM: a tampered block must
|
||||
// not yield half-guessed plain text.
|
||||
var value = SecretProtector.Protect("sk-or-v1-geheim")!;
|
||||
var payload = Convert.FromBase64String(value["enc:v2:".Length..]);
|
||||
payload[^1] ^= 0xFF;
|
||||
@@ -160,8 +160,8 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[Fact]
|
||||
public void Ein_fremder_Schluessel_kann_den_Wert_nicht_lesen()
|
||||
{
|
||||
// Genau die Eigenschaft, die den Schutz ausmacht: Die Konfigurationsdatei allein
|
||||
// nützt auf einem anderen Rechner nichts, weil der Schlüssel nicht mitreist.
|
||||
// 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"));
|
||||
@@ -191,10 +191,10 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>[LinuxFact]</c> hält den Test unter Windows an, der Plattform-Analysator sieht
|
||||
/// das aber nicht — er kennt nur Attribute wie <c>SupportedOSPlatform</c>. Ohne die
|
||||
/// Angabe blieb CA1416 als einzige Warnung im Build stehen, und eine einzelne
|
||||
/// Warnung, die immer da ist, liest bald niemand mehr.
|
||||
/// <c>[LinuxFact]</c> stops the test under Windows, but the platform analyzer does not see
|
||||
/// that — it only knows attributes like <c>SupportedOSPlatform</c>. Without the
|
||||
/// annotation CA1416 remained as the only warning in the build, and a single
|
||||
/// warning that is always there soon nobody reads any more.
|
||||
/// </summary>
|
||||
[LinuxFact]
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("linux")]
|
||||
@@ -209,8 +209,8 @@ public sealed class SecretProtectorTests : IDisposable
|
||||
[LinuxFact]
|
||||
public void Ein_DPAPI_Wert_aus_einer_Windows_Instanz_wird_mit_Begruendung_abgewiesen()
|
||||
{
|
||||
// Der Umzugsfall. Ihn als Klartext durchzureichen würde einen unbrauchbaren
|
||||
// Schlüssel an die API schicken — die Meldung muss sagen, was zu tun ist.
|
||||
// The moving case. Passing it on as plain text would send an unusable
|
||||
// key to the API — the message must say what to do.
|
||||
var ex = Should.Throw<SecretProtectionException>(
|
||||
() => SecretProtector.Unprotect("enc:v1:" + Convert.ToBase64String([1, 2, 3, 4])));
|
||||
|
||||
@@ -233,7 +233,7 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
{
|
||||
SecretKeyStore.UseDirectory(null);
|
||||
try { Directory.Delete(_keyDir, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
catch { /* Cleaning up is secondary */ }
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -326,8 +326,8 @@ public sealed class ConfigSecretsTests : IDisposable
|
||||
[Fact]
|
||||
public void Werte_aus_deserialisiertem_JSON_werden_ebenfalls_erfasst()
|
||||
{
|
||||
// Nach dem Deserialisieren stecken die Werte als JsonElement im Wörterbuch,
|
||||
// nicht als String.
|
||||
// After deserialization the values sit in the dictionary as JsonElement,
|
||||
// not as string.
|
||||
var json = """
|
||||
{ "agentId": "test",
|
||||
"tools": { "Mail": { "smtpHost": "smtp.example.com", "password": "geheim123" } } }
|
||||
|
||||
Reference in New Issue
Block a user