Hardening untrusted input
A document from outside your organisation is untrusted input. It can be built to exhaust memory, disk or CPU: a ZIP bomb disguised as a .docx, a 100-megapixel image, tables nested thousands deep, a PDF that draws itself forever. ResourceLimits caps each of these, and a cap that trips throws a typed exception instead of taking down your process.
Set limits
using DocWright;
using DocWright.Core;
var options = new ConvertOptions();
options.Limits.MaxInputBytes = 50L * 1024 * 1024; // refuse inputs over 50 MB
options.Limits.MaxOutputBytes = 200L * 1024 * 1024;
options.Limits.MaxPages = 2; // tiny, to show a limit tripping
options.Limits.MaxImagePixels = 40_000_000; // about 40 megapixels per image
options.Limits.MaxUncompressedPartBytes = 200L * 1024 * 1024; // zip-bomb guards
options.Limits.MaxPackageCompressionRatio = 200.0;
options.Limits.MaxPackageEntryCount = 5000;
options.Limits.MaxNestedTableDepth = 50;
options.Limits.MaxApproximateMemoryBytes = 1L * 1024 * 1024 * 1024;
options.Limits.MaxWallClockTime = TimeSpan.FromSeconds(30);
try
{
using FileStream input = File.OpenRead("quarterly-report.docx"); // three pages
converter.Convert(input, Stream.Null, options);
}
catch (ResourceLimitExceededException ex)
{
Console.WriteLine($"{ex.LimitName} exceeded (observed {ex.ObservedValue}): {ex.Message}");
}
Output
MaxPages exceeded (observed 3): The layout produced 3 pages, which exceeds MaxPages=2.
ResourceLimitExceededException.LimitName and ObservedValue say what tripped, so you can log it and tell the user.
Every limit
| Limit | Guards against | Default |
|---|---|---|
MaxInputBytes |
Huge uploads | off |
MaxOutputBytes |
Runaway output | off |
MaxPages |
Documents that lay out to thousands of pages | off |
MaxImagePixels |
Decompression bombs in images | off |
MaxUncompressedPartBytes |
ZIP bombs | off |
MaxPackageCompressionRatio |
ZIP bombs | off |
MaxPackageEntryCount |
Packages with millions of entries | off |
MaxNestedTableDepth |
Deeply nested tables | off |
MaxApproximateMemoryBytes |
Memory exhaustion | off |
MaxWallClockTime |
Slow documents | off |
MaxKeyDerivationSpinCount |
Encrypted files that demand millions of hash rounds | 1,000,000 |
MaxPdfNestingDepth |
PDFs whose objects contain themselves | 32 |
MaxPdfObjectCount |
PDFs declaring billions of objects | 5,000,000 |
MaxPdfDecompressedBytes |
Compressed-stream bombs in PDFs, per stream | 256 MB |
MaxPdfContentOperations |
Endless drawing instructions, per page | 20,000,000 |
MaxPdfFunctionSteps |
Expensive PDF functions, per evaluation | 100,000 |
Set any limit to null to turn it off. The limits in bold are on by default, because their worst case doesn't depend on file size: a 200-byte PDF can recurse forever.
For a public upload endpoint, set at least MaxInputBytes, MaxWallClockTime, MaxPages and the two ZIP-bomb limits.
Limits that can't be turned off
Some failures are a StackOverflowException, which no catch can handle and which ends the process. The guards for those can be tightened but never removed:
MaxPdfNestingDepth: settingnull, or anything above 1,000, still applies a 1,000-level ceiling.- The HTML, RTF and Markdown readers use iterative parsers with nesting limits (
MaxNestingDepth: 256, 512 and 128), so hostile nesting throws a normal exception. - Report definitions, report expressions and report processing have their own limits, on by default. See Report expressions.
Also important
- Nothing is fetched. No reader, field, template or merge ever makes a network or file request on a document's behalf. Server-side request forgery through a document isn't possible unless you plug in a resolver yourself.
- Scripts are never run. HTML
<script>is removed, report<Code>is never executed, and form-field macros are never run. - Cancellation. Pair
MaxWallClockTimewith aCancellationTokeninConvertAsyncto stop work when the client goes away. - Isolation still helps. Limits bound DocWright's work, but running conversions in a separate worker process or container with its own memory cap is still good defence in depth for a public service.