dotnet/wpf · error · FileFormatException

SR.Resource_XpsPackageBoundaryViolation

Error message

SR.Resource_XpsPackageBoundaryViolation

What it means

FontSource.GetUnmanagedStream enforces an XPS package-boundary security check: when loading XPS content, font URIs must resolve inside the current XPS package. A non-file URI that XpsLoadingContext.IsUriAllowedAgainstPackage rejects is blocked with FileFormatException(SR.Resource_XpsPackageBoundaryViolation) to prevent SSRF — a font reference inside a document must not point at an external resource.

Solutions

  1. Fix the XPS document so all font part URIs are relative and resolve within the same package.
  2. Re-generate the document with a compliant XPS producer that never emits external font references.
  3. If the URI is trusted, load the font from a local file/stream instead of relying on the XPS package path.
  4. Catch FileFormatException and surface a clear 'untrusted document resource' error to the user.

Example fix

// before (in .xps FixedPage markup)
<FontUri Source="http://evil.example.com/mal.ttf" />
// after
<FontUri Source="/Resources/Fonts/1-0.ttf" />
Defensive patterns

Strategy: validation

Validate before calling

if (!fontUri.IsFile && !XpsLoadingContext.IsUriAllowedAgainstPackage(xpsPackageOrigin, fontUri))
    throw new FileFormatException("Font URI escapes the XPS package.");

Try / catch

try { stream = fontSource.GetUnmanagedStream(); }
catch (FileFormatException ex) when (ex.Message.Contains("PackageBoundary") || ex.Data.Count >= 0) { /* reject document */ }

Prevention

When it happens

Trigger: Opening an XPS document whose font part URI escapes the containing package — e.g. a pack URI referencing another package (http://, or pack URI for a different package) instead of a resource within the same XPS file, evaluated during deferred font loading.

Common situations: Malicious or hand-edited XPS documents referencing remote fonts; documents produced by tools writing absolute URIs instead of package-relative ones; a package origin/context mismatch where the stored XPS package origin no longer matches the font URI at load time.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/099785013027a624. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs:191

            }

            if (bits == null)
            {
                Stream fontStream;

                if (_isInternalCompositeFont)
                {
                    // We should read this font from our framework resources
                    fontStream = GetCompositeFontResourceStream();
                }
                else
                {
                    // Security: When loading XPS content, block font URIs that escape
                    // the current package to prevent SSRF. Uses stored origin to
                    // handle deferred loading after XPS parse context has ended.
                    if (!_fontUri.IsFile && !XpsLoadingContext.IsUriAllowedAgainstPackage(_xpsPackageOrigin, _fontUri))
                    {
                        throw new FileFormatException(SR.Resource_XpsPackageBoundaryViolation);
                    }

                    WebResponse response = WpfWebRequestHelper.CreateRequestAndGetResponse(_fontUri);
                    fontStream = response.GetResponseStream();
                    if (string.Equals(response.ContentType, ObfuscatedContentType, StringComparison.Ordinal))
                    {
                        // The third parameter makes sure the original stream is closed
                        // when the deobfuscating stream is disposed.
                        fontStream = new DeobfuscatingStream(fontStream, _fontUri, false);
                    }
                }

                // We don't want any memory leaks
                // TODO: Remove FinalizableUnmanagedStream once FontFileStream is migrated from C++/CLI.
                if (fontStream is UnmanagedMemoryStream unmanagedMemoryStream)
                    return new FinalizableUnmanagedStream(unmanagedMemoryStream);

                // Convert the DeobfuscatingStream to byte[]; add it to our cache, dispose it

View on GitHub (pinned to 81131a70a4)