Class SafeHttpResourceLoader
- Namespace
- NetPdf
- Assembly
- NetPdf.dll
Per PR #18 review #1 — built-in HTTP / HTTPS resource loader that closes the SSRF gap left by the bare IResourceLoader contract. The pre-fix flow:
SafeResourceLoader → UriSafetyValidator.Validate (scheme + IP if literal)
→ user IResourceLoader (HttpClient default)
↓
DNS resolution + auto-follow redirects
(NEITHER seen by SafeResourceLoader)
Symbolic hosts (e.g., attacker.com) deferred IP validation
until after DNS, but the user loader was a black box: it could resolve
to a private / loopback / cloud-metadata IP + connect. Auto-redirect
could step through hostile Location: headers without the
wrapper's ValidateRedirect(Uri, Uri, SecurityPolicy, int) running.
What this loader does instead.
- Resolve the host via GetHostAddressesAsync(string, CancellationToken);
immediately reject every resolved address that hits the
IsBlockedIp(IPAddress, out string) blocklist (loopback,
private, link-local incl. AWS metadata
169.254.169.254, IPv6 ULA, IPv4-mapped, etc.). - Use ConnectCallback to intercept the connect step + use the pre-validated IP — defeats the DNS-rebinding attack (resolve, validate, then re-resolve to a different IP at connect time) by pinning the IP from the validation pass.
- Set AllowAutoRedirect = false.
On a 30x response, manually walk the
Location:header through ValidateRedirect(Uri, Uri, SecurityPolicy, int) + recurse with the updated hop count. - Cap response bytes at the configured per-resource limit so a malicious server can't stream gigabytes.
NetPdf v1 ships no default loader. This class is a reference implementation that consumers can opt into via:
options.ResourceLoader = new SafeHttpResourceLoader();
Phase 5's wireup may wire it automatically when an HTTP-fetching surface is requested.
public sealed class SafeHttpResourceLoader : IResourceLoader, IDisposable
- Inheritance
-
SafeHttpResourceLoader
- Implements
- Inherited Members
Constructors
SafeHttpResourceLoader(SecurityPolicy?)
Construct with the active SecurityPolicy. The loader captures the policy at construction so the configured IP blocklist + redirect cap + per-resource size cap apply uniformly across every fetch issued by this loader instance.
<p>Per post-Task-7 review (P1 #1) — when this loader is
wrapped by a <xref href="NetPdf.SafeResourceLoader" data-throw-if-not-resolved="false"></xref> whose
<xref href="NetPdf.ResourceFetchContext.Policy" data-throw-if-not-resolved="false"></xref> differs from the
policy passed here, the wrapper's constructor throws
<xref href="System.ArgumentException" data-throw-if-not-resolved="false"></xref> to surface the misconfig before
any divergent decisions are made. To keep them in sync, prefer
<xref href="NetPdf.SafeResourceLoader.CreateWithSafeHttp(NetPdf.ResourceFetchContext)" data-throw-if-not-resolved="false"></xref>, which
constructs both with the context's policy as the single source
of truth.</p>
public SafeHttpResourceLoader(SecurityPolicy? policy = null)
Parameters
policySecurityPolicy
Properties
Policy
Per post-Task-7 review (recommendation P1 #1) — the SecurityPolicy this loader captured at construction. Exposed so SafeResourceLoader's constructor can detect a policy-divergence misconfig (the wrapper's Policy differing from the inner HTTP loader's policy) + reject it at construction rather than letting redirects / AllowedHosts / per-resource-bytes validation use one policy while scheme / IP-blocklist validation uses another. Read-only — set once at construction.
public SecurityPolicy Policy { get; }
Property Value
Methods
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
LoadAsync(Uri, ResourceKind, CancellationToken)
Per Phase 5 contract — fetch a resource with full SSRF / redirect / size enforcement.
public ValueTask<ResourceResponse> LoadAsync(Uri uri, ResourceKind kind, CancellationToken ct)
Parameters
uriUrikindResourceKindctCancellationToken