Abstract

A file converter is not one program; it is a thin coordinator sitting on top of many specialized parsing libraries, each one written to read a complex, untrusted format. This study characterizes that attack surface using public data. We enumerate the core parsing engines behind a representative conversion stack, summarize the volume and class of vulnerabilities they have accumulated in public databases over time, and argue that the decisive operational metric is not vulnerability count but patch latency - the time between an upstream security release and its deployment. We pair that with our own patch-cadence target and explain why patching, however fast, is treated here as one layer rather than the defense.

Background

Every "convert this file" action routes input to whichever engine understands the format: an image decoder for raster formats, a PDF library for documents, an office-suite engine for word-processor and spreadsheet files, an archive tool for compressed bundles. These engines are mature, widely deployed, and overwhelmingly written in C and C++ for performance. That combination - rich, ambiguous formats parsed by memory-unsafe code - is precisely the combination that produces a steady stream of vulnerabilities.

This is not a criticism of those projects. They are some of the most scrutinized software in the world, and the continuous flow of fixes is a sign of active maintenance, not neglect. The point is structural: a converter inherits the entire vulnerability history of everything it links against, and it exposes that surface on its main path, because feeding untrusted files to these parsers is the whole job.

The Challenge

For most applications, a parser bug in a linked library is only reachable if the application happens to feed it hostile input. A converter removes that "if". The hostile input is the workload. So two questions decide how exposed a conversion service really is:

Method

We enumerated the core parsing engines in a representative file-conversion stack and, for each, consulted public vulnerability databases to summarize two things: the order of magnitude of recorded CVEs to date, and the share that fall in the memory-safety class (buffer overflows, out-of-bounds reads and writes, use-after-free, integer overflows leading to corruption). Counts are drawn from the CVE Program, the National Vulnerability Database, and per-project trackers such as CVE Details. They are deliberately rounded and shown as approximate ranges: vulnerability totals shift over time and differ slightly between sources, so the figures here are meant to convey relative shape and order of magnitude, not a precise audit. We then measured our own deployment latency for upstream security releases over recent update cycles.

Scope Notice

This study uses public, third-party vulnerability data about open-source libraries; the CVE figures describe those upstream projects, not topriv. The exact composition of our conversion pipeline, our build process, and our deployment mechanics are proprietary and intentionally omitted. What we publish here is the shape of the surface and the cadence we hold ourselves to.

Results

Surface by Parsing Engine

The table below summarizes the engines that do the heavy lifting in common conversions, the format families they handle, and the approximate public CVE history attributed to each. The pattern is consistent: the more formats an engine touches, the larger its historical surface, and the majority of its issues are memory-safety bugs reachable by a crafted file.

Parsing engine Handles Public CVEs (approx, cumulative) Memory-safety share
ImageMagick 100+ image formats ~520 ~75%
Poppler PDF parse / render ~150 ~70%
LibreOffice engine docx, odt, xls, ppt ~110 ~60%
OpenJPEG JPEG 2000 ~60 ~80%
libarchive zip, 7z, tar, rar ~45 ~65%
libtiff TIFF imaging ~90 ~80%
Approx. cumulative public CVEs per engine 200 400 600 ImageMagick ~520 Poppler ~150 libtiff ~90 LibreOffice ~110 OpenJPEG ~60 libarchive ~45
Fig 1. Order-of-magnitude public CVE history. One engine alone carries the bulk of the surface.

Key observation: The surface is both large and lopsided. A single engine can account for more recorded vulnerabilities than every other engine in the stack combined, and across the board the majority of issues are memory-safety bugs - exactly the class a malicious file is built to trigger. You cannot shrink this history. You can only refuse to keep running the parts of it that already have fixes.

Patch Latency: The Metric That Moves

Vulnerability counts are fixed history; the variable a service actually controls is how long it keeps running a version after a fix exists. The window between an upstream security release and its deployment is the window in which a service carries a known, documented issue on its main path. We track this as our primary security-maintenance metric and hold it to a tight target.

Patch-latency metric Target Note
Critical / high-severity engine fix ≤ 72h from upstream release to deployed
Standard security update cycle weekly routine engine + OS package refresh
Known-vulnerable version in production 0 tolerated once a fix is available and verified

Why Patching Is a Layer, Not the Plan

Even a perfect patch cadence only protects against the vulnerabilities that are already known. The gap between a flaw being introduced and a fix being released - the window of unknown bugs - cannot be closed by updating quickly, because there is nothing yet to update to. This is why we do not treat patching as the security model. We treat it as the first of several independent layers.

The companion to fast patching is containment: assume a parser will eventually misbehave on some input, and make sure that when it does, the failure stays inside a single, disposable job with no path to your data, anyone else's data, the host, or the network. We measured exactly that behaviour against a synthetic adversarial corpus in a separate study - Containment Under Hostile Input - where 12,400 hostile files produced zero escapes and zero network egress. Patch latency closes the doors we know about; containment makes sure the doors we do not know about lead nowhere.

Layer 1 - Patch fast closes known issues Layer 2 - Contain catches the unknown A file must defeat both, at the same time, to matter.
Fig 2. Patching and containment are independent layers in series.

Implications for PrivConvert

This is the reasoning behind how PrivConvert is maintained. We keep the conversion engines current as upstream security releases land, against a tight latency target, so the known-vulnerable window stays as small as we can make it. And because we never assume that is sufficient, every conversion still runs confined, with no network path and nothing retained after the job ends. The large, lopsided surface described above is real for every converter on the internet; what differs is whether a service treats keeping it patched - and contained - as routine or as an afterthought.

For a plain-language version of this argument, see our blog post When File-Parsing Libraries Get CVEs, Converters Are the Front Line.

Limitations

The CVE figures are approximate, drawn from public databases, and intended to convey magnitude and relative shape rather than a precise count; vulnerability totals also shift over time as new issues are reported and old records are revised. A raw CVE count is a crude proxy for risk - severity, reachability, and whether a fix exists all matter more than the number itself. The set of engines shown is representative of a common conversion stack, not an exhaustive bill of materials. Finally, this study addresses surface and patch cadence only; the containment behaviour that backs it up is evaluated separately, and neither layer is presented as a standalone guarantee.

No user data was involved in this study. It draws only on public, third-party vulnerability records and our own internal maintenance metrics. We did not observe, sample, or analyze any real conversion, file, or person - by design, the files you convert are never retained, so there is nothing for us to study in the first place.