Files
ClawdDotNet/tests/ClawdDotNet.Tools.Tests/DirectAPI/UrlSanitizerTests.cs
T
RichardandClaude Opus 5.5 837a634a22 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>
2026-10-04 12:45:03 +02:00

96 lines
3.3 KiB
C#

using ClawdDotNet.Tools.DirectAPI;
using Shouldly;
namespace ClawdDotNet.Tools.Tests.DirectAPI;
/// <summary>
/// S3 from the inventory: the fetched URL went to the model as "source" —
/// including apikey= in the query string. The key thus ended up in the context, was
/// sent again at every following step and written into ChatContext.json.
/// </summary>
public sealed class UrlSanitizerTests
{
private const string Secret = "GEHEIM-abc123XYZ";
[Theory]
[InlineData("https://api.twelvedata.com/quote?symbol=NVDA&apikey=GEHEIM-abc123XYZ")]
[InlineData("https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=X&apikey=GEHEIM-abc123XYZ")]
[InlineData("https://api.example.com/v1?api_key=GEHEIM-abc123XYZ&x=1")]
[InlineData("https://api.example.com/v1?token=GEHEIM-abc123XYZ")]
[InlineData("https://api.example.com/v1?access_token=GEHEIM-abc123XYZ")]
[InlineData("https://api.example.com/v1?KEY=GEHEIM-abc123XYZ")]
[InlineData("https://api.example.com/v1?ApiKey=GEHEIM-abc123XYZ&other=2")]
[InlineData("https://api.example.com/v1?secret=GEHEIM-abc123XYZ")]
[InlineData("https://api.example.com/v1?a=1&apikey=GEHEIM-abc123XYZ#fragment")]
public void Sensible_Parameter_werden_maskiert(string url)
{
var sanitized = UrlSanitizer.Sanitize(url);
sanitized.ShouldNotContain(Secret);
sanitized.ShouldContain("***");
}
[Fact]
public void Harmlose_Parameter_bleiben_lesbar()
{
var sanitized = UrlSanitizer.Sanitize(
"https://api.twelvedata.com/quote?symbol=NVDA&interval=1day&apikey=GEHEIM-abc123XYZ");
sanitized.ShouldContain("symbol=NVDA");
sanitized.ShouldContain("interval=1day");
sanitized.ShouldContain("apikey=***");
}
[Fact]
public void Der_Pfad_bleibt_erhalten()
{
var sanitized = UrlSanitizer.Sanitize("https://api.massive.com/v2/aggs/ticker/NVDA?apikey=x");
sanitized.ShouldStartWith("https://api.massive.com/v2/aggs/ticker/NVDA?");
}
[Fact]
public void Ein_Fragment_bleibt_erhalten()
{
var sanitized = UrlSanitizer.Sanitize("https://x.de/a?apikey=geheim#abschnitt");
sanitized.ShouldEndWith("#abschnitt");
sanitized.ShouldNotContain("geheim");
}
[Theory]
[InlineData("https://api.example.com/v1")]
[InlineData("https://api.example.com/v1?symbol=NVDA")]
public void URLs_ohne_sensible_Parameter_bleiben_unveraendert(string url)
{
UrlSanitizer.Sanitize(url).ShouldBe(url);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Leere_Eingaben_fuehren_nicht_zu_einem_Fehler(string? url)
{
Should.NotThrow(() => UrlSanitizer.Sanitize(url));
}
[Fact]
public void Ein_Parameter_ohne_Wert_stoert_nicht()
{
var sanitized = UrlSanitizer.Sanitize("https://x.de/a?flag&apikey=geheim");
sanitized.ShouldContain("flag");
sanitized.ShouldNotContain("geheim");
}
[Fact]
public void Ein_Schluessel_als_Teil_eines_anderen_Namens_wird_nicht_faelschlich_maskiert()
{
// "monkey" contains "key", but is not a sensitive parameter.
var sanitized = UrlSanitizer.Sanitize("https://x.de/a?monkey=banane");
sanitized.ShouldBe("https://x.de/a?monkey=banane");
}
}