DEV Community

Fahad Farid
Fahad Farid

Posted on

How we audit local PDF processing (and what "no upload" actually proves)

Tags: javascript, webdev, privacy, tutorial


"No upload" is a claim. Anyone can write it under a button. So here's how we actually check it on ToolVero — and, just as important, what the check can't prove.

Step 1: grep the source for upload mechanisms

The blunt version of the audit is a static source review. We took all 74 public tool pages, their inline scripts, and the 8 first-party scripts they reference, and searched for the four ways a browser page can send data out: fetch(), XMLHttpRequest, WebSocket, and sendBeacon.

Result: no direct call to a ToolVero document-processing upload endpoint anywhere in tool code. That's the finding behind the "Private browser processing" badge — 74 pages checked, 42 of them file tools, zero first-party upload paths.

But a static review has a defined edge, and we publish it: it doesn't inspect third-party library internals, it doesn't observe runtime network traffic, and it doesn't mean the pages make no network requests at all. A page can load a library from a CDN, fire analytics, and serve ads — none of which is your file going to our server, but all of which is network activity. The badge wording says exactly this: the file isn't sent to a ToolVero processing server by the tool itself.

Step 2: design the exfiltration out, don't promise it away

The stronger audit pattern is clean-by-construction. Our Image Metadata Remover doesn't promise "we won't keep your EXIF data" — it makes keeping it structurally impossible. The pipeline decodes the image with createImageBitmap, rasterises it to a canvas, and rebuilds a fresh PNG that retains only the signature plus IHDR, IDAT and IEND chunks. There is no chunk in the output where EXIF, GPS, XMP or thumbnails could hide, because the output is assembled from pixels, not copied from the source container.

When the design removes the possibility, the audit only has to verify the design — a much smaller claim to defend than "trust our servers."

Step 3: publish what the audit finds, including the fixes

An audit that never finds anything isn't an audit, it's marketing. Ours found a real one: the Word-to-PDF converter was inserting HTML returned from a DOCX conversion library straight into the page, and that library's own docs warn its output isn't sanitized. We fixed it by rebuilding only a limited set of text, table and image elements — no script elements, no event handlers, no styles, no unsafe URL attributes carried through. Some advanced DOCX formatting gets dropped as a result, and we say so on the page.

The audit page also lists remaining work in plain text: at least 19 file-tool pages still load externally hosted library scripts, analytics and advertising scripts are present, and there's been no independent penetration test or live network capture with sample files. That's the current boundary of what we know.

Step 4: keep the categories separate

This is the part most "privacy" pages fudge. On our Trust Center we keep four things in separate boxes: the privacy design (browser-first processing), the technical audit (the source review above), third-party scans (SSL Labs, SecurityHeaders, Sucuri — transport and header checks, dated, with their own limits), and formal certification (which we don't claim to hold). A scan result is not a certification. An internal review is not a penetration test. Saying which is which is the whole point.

The takeaway for builders

If you ship browser-local tools, audit like this:

  1. Grep for exfiltration primitives (fetch, XHR, WebSocket, sendBeacon) across every page and first-party script. It's crude and it catches the obvious.
  2. Prefer clean-by-construction pipelines — rebuild outputs from primitives so the sensitive data can't survive even if you wanted it to.
  3. Publish the scope box: what you checked, what you explicitly didn't, and what's still open. The full audit is dated October 10, 2026, and it reads like a lab notebook, not a badge.
  4. Never let marketing outrun the audit. Our commitment is mechanical: if a tool ever moves from local to server-side processing, its privacy explanation changes with it — no stale claims.

"No upload" is only as strong as the audit behind it. Ours is public, dated, and honest about its edges — which is exactly why we'd rather you read it than trust it.

Top comments (0)