Class SafeResourceLoader
- Namespace
- NetPdf
- Assembly
- NetPdf.dll
Per Phase D D-1 — the only valid pipeline entry point for resource fetches. Wraps a user-supplied IResourceLoader + applies every safety check the NetPdf threat model requires:
- Per-render budget via ResourceFetchContext:
MaxResourcesPerRender(count cap),MaxTotalResourceBytes(cumulative byte cap). - URI safety via Validate(Uri, SecurityPolicy):
scheme allowlist (file/data/http/https per SecurityPolicy),
IP blocklist for HTTP(S) (loopback, private, link-local incl.
AWS/GCE/Azure metadata 169.254.169.254, IPv6 ULA, IPv4-mapped),
AllowedHostsfilter. - file: base-path check when the URI returns
RequiresBasePathCheck:
the resolved file path is verified to lie under
BaseUri's directory subtree
(canonicalized to defeat
../traversal). - Per-resource size cap from MaxResourceBytes (post-fetch validation; pre-fetch byte estimate not always available from the user loader).
- MIME allowlist per ResourceKind
so an
<img src=...>servingtext/htmlcan't route through the image decoder. - Per-fetch timeout via ResourceTimeout linked to the conversion's cancellation token.
Why this wrapper exists. The user-facing
IResourceLoader contract is intentionally narrow (raw
byte fetch). If any caller in the pipeline went directly to
ResourceLoader, that caller would bypass
every defense above. SafeResourceLoader is the only
pipeline-internal entry point — when Phase 5 wires resource fetching,
every consumer (CSS parser for url() / @import /
@font-face, image decoder, font resolver) calls THIS, never
the underlying IResourceLoader directly.
NetPdf v1 ships no default loader. When ResourceLoader is null, every fetch returns a ResourceFailure immediately without invoking any logic — the wrapper still exists so the contract is consistent for Phase 5's wireup.
public sealed class SafeResourceLoader
- Inheritance
-
SafeResourceLoader
- Inherited Members
Constructors
SafeResourceLoader(IResourceLoader?, ResourceFetchContext)
public SafeResourceLoader(IResourceLoader? inner, ResourceFetchContext context)
Parameters
innerIResourceLoadercontextResourceFetchContext
Methods
CreateWithSafeHttp(ResourceFetchContext)
Per post-Task-7 review (recommendation P1 #1) — factory
that constructs a SafeHttpResourceLoader using the
context's SecurityPolicy + wraps
it in a SafeResourceLoader bound to the same
context. Single source of truth for the policy across both
layers; eliminates the divergence risk entirely.
<p>The returned wrapper does NOT take ownership of the
returned HTTP loader's lifetime. Callers who construct via this
factory should track the underlying loader separately if they
need <xref href="System.IDisposable.Dispose" data-throw-if-not-resolved="false"></xref> on shutdown — typically
by reading the <code>UnderlyingHttpLoader</code> property off the
returned <xref href="NetPdf.SafeResourceLoaderWithHttp" data-throw-if-not-resolved="false"></xref> bundle. (For
most v1 use cases the loader lives for the process lifetime.)</p>
public static SafeResourceLoaderWithHttp CreateWithSafeHttp(ResourceFetchContext context)
Parameters
contextResourceFetchContext
Returns
FetchAsync(Uri, ResourceKind)
Fetch a resource with all the Phase D defenses applied. Returns the loaded bytes on success; a ResourceFailure shape on rejection. Slot reservation happens AFTER the URI safety + base-path checks so a fast-rejected fetch doesn't consume a budget slot — an attacker could otherwise probe the allowlist by firing N+1 obviously-invalid URIs to lock out legitimate fetches.
public ValueTask<SafeResourceResult> FetchAsync(Uri uri, ResourceKind kind)
Parameters
uriUrikindResourceKind