EgressAddressValidator
AgentPrism.Core.dllThe single place that decides whether an outbound target address is safe.
public static class EgressAddressValidatorInheritance
Section titled “Inheritance”object ← EgressAddressValidator
Inherited Members
Section titled “Inherited Members”object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
Section titled “Remarks”AgentPrism reaches the network from three surfaces — webhook delivery, MCP
server connections and model provider calls. Each one accepts an address
that ultimately came from a user, so each one is an SSRF surface:
left uncontrolled, they become a means of reaching internal-network
services, including cloud metadata endpoints (169.254.169.254),
which often hand out unauthenticated temporary credentials.
The rules live here, once. Three copies of the same resolve-and-check loop end the same way: one gets fixed and two go stale.
Methods
Section titled “Methods”IsAllowedTarget(IPAddress, EgressAddressPolicy)
Section titled “ IsAllowedTarget(IPAddress, EgressAddressPolicy)”Reports whether an address may be connected to under a policy.
public static bool IsAllowedTarget(IPAddress address, EgressAddressPolicy policy)Parameters
Section titled “Parameters”address IPAddress
The resolved address.
policy EgressAddressPolicy
The rules of the calling surface.
Returns
Section titled “Returns”true if a connection to the address may be opened.
Exceptions
Section titled “Exceptions”address is null.
IsPrivate(IPAddress)
Section titled “ IsPrivate(IPAddress)”Reports whether an IP address falls within a private or local range.
public static bool IsPrivate(IPAddress address)Parameters
Section titled “Parameters”address IPAddress
The address.
Returns
Section titled “Returns”true if the address falls within a private range.
Remarks
Section titled “Remarks”IPv4 ranges covered: 0.0.0.0/8, 10.0.0.0/8,
127.0.0.0/8, 169.254.0.0/16 (cloud metadata!),
172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10
(CGNAT), and everything from 224.0.0.0 up (multicast and reserved).
IPv6 ranges covered: ::1, ::, fc00::/7,
fe80::/10, site-local, and multicast.
An IPv6 address that embeds an IPv4 address is reduced to
that IPv4 address first and then judged by the IPv4 rules. Skipping
this is a classic bypass: ::ffff:169.254.169.254,
::169.254.169.254, 64:ff9b::a9fe:a9fe and
2002:a9fe:a9fe:: all reach the metadata endpoint through a host
that has the matching translation or relay configured. See
EgressAddressValidator.TryGetEmbeddedIPv4 for the forms handled.
ResolveAndValidateAsync(string, EgressAddressPolicy, CancellationToken)
Section titled “ ResolveAndValidateAsync(string, EgressAddressPolicy, CancellationToken)”Resolves a host name and validates every address it resolves to.
public static ValueTask<EgressAddressVerdict> ResolveAndValidateAsync(string host, EgressAddressPolicy policy, CancellationToken cancellationToken = default)Parameters
Section titled “Parameters”host string
The host name or IP literal.
policy EgressAddressPolicy
The rules of the calling surface.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”ValueTask<EgressAddressVerdict>
The verdict, carrying every resolved address when allowed.
Remarks
Section titled “Remarks”If any resolved address is rejected, the target is rejected. Picking the first suitable address instead would let an attacker publish a name that resolves to one public and one private address, and reach the private one on the next attempt.
TryGetEmbeddedIPv4(IPAddress, out IPAddress)
Section titled “ TryGetEmbeddedIPv4(IPAddress, out IPAddress)”Extracts the IPv4 address an IPv6 address embeds, if it embeds one.
public static bool TryGetEmbeddedIPv4(IPAddress address, out IPAddress embedded)Parameters
Section titled “Parameters”address IPAddress
The IPv6 address.
embedded IPAddress
The embedded IPv4 address, when the method returns true.
Returns
Section titled “Returns”true if the address carries an embedded IPv4 address.
Remarks
Section titled “Remarks”Forms handled, each of which routes to the embedded IPv4 address on a host with the matching translation configured:
- IPv4-mapped,
::ffff:0:0/96—::ffff:1.2.3.4. - IPv4-translated,
::ffff:0:0:0/96(RFC 2765). - IPv4-compatible,
::/96(RFC 4291, deprecated) —::1.2.3.4. - NAT64 well-known prefix,
64:ff9b::/96(RFC 6052). - 6to4,
2002::/16(RFC 3056) — the IPv4 address sits in the second and third groups.
Teredo (2001::/32) is deliberately not decoded: its client
address is obfuscated and its server address is not the destination.
ValidateAddresses(IPAddress[], EgressAddressPolicy)
Section titled “ ValidateAddresses(IPAddress[], EgressAddressPolicy)”Validates every address a host resolved to.
public static EgressAddressVerdict ValidateAddresses(IPAddress[] addresses, EgressAddressPolicy policy)Parameters
Section titled “Parameters”addresses IPAddress[]
The resolved addresses.
policy EgressAddressPolicy
The rules of the calling surface.
Returns
Section titled “Returns”The verdict, carrying the addresses unchanged when allowed.
Remarks
Section titled “Remarks”If any address is rejected, the whole target is rejected. Picking the first suitable address instead would let an attacker publish a name that resolves to one public and one private address, and reach the private one on a later attempt.
Exceptions
Section titled “Exceptions”addresses is null.
ValidateLiteral(Uri, EgressAddressPolicy)
Section titled “ ValidateLiteral(Uri, EgressAddressPolicy)”Validates a target whose host is written as an IP literal, without resolving DNS.
public static string? ValidateLiteral(Uri target, EgressAddressPolicy policy)Parameters
Section titled “Parameters”target Uri
The target address.
policy EgressAddressPolicy
The rules of the calling surface.
Returns
Section titled “Returns”The rejection reason, or null when the target is acceptable.
Remarks
Section titled “Remarks”Used at save time, in the HTTP endpoints. DNS is deliberately
not resolved there: it would slow the save down, and a
target that is unreachable at that moment — or a name that does not
resolve yet — is not by itself an error. A host name that resolves to a
private address still gets rejected, but at connection time, by
EgressSocketGuard. That check is the one that cannot be
evaded; this one only turns the obvious mistake into an immediate
400 instead of a silent failure hours later.
Exceptions
Section titled “Exceptions”target is null.