Files
ClawdDotNet/tests/ClawdDotNet.Tools.Tests/WebFetch/UrlGuardResolutionTests.cs
T
RichardandClaude Opus 5.5 77062facc9 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>
2026-10-04 13:18:43 +02:00

68 lines
2.4 KiB
C#

using System.Net;
using System.Net.Http;
using ClawdDotNet.Tools.WebFetch;
using Shouldly;
namespace ClawdDotNet.Tools.Tests.WebFetch;
/// <summary>
/// #580: Checking the URL only sees the name. localtest.me is on no blocklist
/// and resolves to 127.0.0.1 - in the test run WebFetch thus opened a connection
/// to loopback. That's why the resolved address is checked, without a real network.
/// </summary>
public sealed class UrlGuardResolutionTests
{
private static Func<string, CancellationToken, Task<IPAddress[]>> Resolution(params string[] addresses)
=> (_, _) => Task.FromResult(addresses.Select(IPAddress.Parse).ToArray());
[Theory]
[InlineData("127.0.0.1")]
[InlineData("192.168.40.48")]
[InlineData("169.254.169.254")]
[InlineData("::1")]
[InlineData("::ffff:10.0.0.5")]
public async Task A_name_resolving_into_the_local_network_is_blocked(string address)
{
var ex = await Should.ThrowAsync<HttpRequestException>(
() => UrlGuard.ResolveAllowedAsync("localtest.me", Resolution(address), default));
ex.Message.ShouldContain("localtest.me");
ex.Message.ShouldContain("blocked");
}
[Fact]
public async Task A_name_with_a_public_and_an_internal_address_is_blocked()
{
// Exactly the pattern with which you outwit a sampling check.
await Should.ThrowAsync<HttpRequestException>(
() => UrlGuard.ResolveAllowedAsync("gemischt.example", Resolution("93.184.215.14", "127.0.0.1"), default));
}
[Fact]
public async Task A_name_with_public_addresses_is_allowed()
{
var addresses = await UrlGuard.ResolveAllowedAsync(
"example.com", Resolution("93.184.215.14", "2606:2800:21f:cb07:6820:80da:af6b:8b2c"), default);
addresses.Length.ShouldBe(2);
}
[Fact]
public async Task An_IP_literal_is_checked_without_resolution()
{
var called = false;
await Should.ThrowAsync<HttpRequestException>(() => UrlGuard.ResolveAllowedAsync(
"[::1]",
(_, _) => { called = true; return Task.FromResult(Array.Empty<IPAddress>()); },
default));
called.ShouldBeFalse();
}
[Fact]
public async Task A_name_without_address_is_not_connected()
=> await Should.ThrowAsync<HttpRequestException>(
() => UrlGuard.ResolveAllowedAsync("nirgends.example", Resolution(), default));
}