Why you should verify yourself
Many software tools make claims about privacy, such as ’your files never leave your device’. As a developer or privacy-conscious professional, you should not take these claims at face value. The only way to know for certain is to observe the application’s behaviour directly on your own machine.
This post describes a method you can use to check any desktop application, not just our own. It involves monitoring network traffic to see if any data is sent to external servers during typical use. This approach provides a technical, evidence-based way to assess a vendor’s claims about local processing.
How to check for network activity
To perform this check, you will need to use a network monitoring tool while you use the PDF editor. The specific tools differ by operating system, but the principle is the same: observe all network connections the application makes.
On macOS, you can use the commercial tool Little Snitch, which provides a user-friendly interface for monitoring and blocking connections. Alternatively, you can use the command-line tool `tcpdump`. A command like `sudo tcpdump -i any -n ’tcp port 443 or tcp port 80’` will capture web traffic. Activity Monitor’s Network tab offers a cruder view, showing only the volume of data sent and received per process.
On Windows, two effective tools are Fiddler and Wireshark. Fiddler is particularly useful as a web debugging proxy. By installing its root certificate, you can inspect the contents of HTTPS traffic, which is otherwise encrypted. Wireshark is a packet analyser for more low-level inspection.
The procedure is simple: start your network capture, then use the application as you normally would. Perform these four actions while watching the network monitor:
1. **Open a PDF file.** Immediately after opening a document, watch for any connections. An application that processes files locally should not need to phone home just to open a file.
2. **Perform edits.** Add some text, draw an annotation, or fill a form field. These actions should happen entirely in memory and on your CPU, with no reason to generate network traffic.
3. **Save the document.** Saving writes the modified file to your local disk. This operation should not trigger an outbound connection if the application’s privacy claims are valid.
4. **Export to another format.** Try exporting your PDF to a Word document or an image. This is a computationally intensive task that, if done locally, should not require a network call.
What we found testing PDFluent
We applied this method to our own application, PDFluent, to test the claim that it processes documents locally. We tested the signed and notarised release build for macOS, not a development build. Over a fifteen-minute session where a document was opened, edited, saved, and exported to Word, we monitored all connections.
The monitoring was done using a method that polls open sockets twice a second (`lsof -i`), which attributes every network socket to a specific process ID. This method cannot show the payload of the data, but it can show precisely which process made a connection and to where.
The result was that exactly one outbound connection appeared. This connection occurred about five seconds after launch and was to an address that DNS confirms is pdfluent.com. This is the application’s update check. There were no connections during editing, no connections when saving the file, and no connections during the export process.
The three connections PDFluent can make
Based on our analysis of the code, the PDFluent desktop application can make only three types of network connections. This is the complete list.
First, an update check to `pdfluent.com/releases/latest.json`. This happens about five seconds after launch and also when a user manually selects ’Check for updates’ from the menu. It is an HTTP GET request. Any downloaded updates are signature-checked against a public key that is compiled into the app. There is currently no off switch for this check within the application; the way to prevent it is with a firewall rule.
Second, a crash, bug, or feedback report can be sent to `report.pdfluent.com`. This is turned off by default. It will only send data if the user explicitly turns on the setting and then submits a report. Any messages and stack traces are scrubbed of potentially sensitive information before being sent.
Third, the application can open `pdfluent.com` in the user’s default web browser, but only when the user clicks on a link within the app, such as a ’Help’ or ’About’ link. The web browser makes this request, not the PDFluent application itself.
Why the document itself cannot leave
The architecture of the application prevents document data from being transmitted. There is no code path from the PDF engine, which handles document processing, to any network functionality. The one HTTP client compiled into the binary is part of the updater component; the PDF engine has no access to it.
This architectural separation is visible in the application’s dependency tree. A `cargo tree` analysis shows that the core PDF processing crates have no dependency on networking crates like `reqwest` or `hyper`. The only crate that depends on an HTTP client is the updater module, which is isolated from the document handling code.
The PDF engine’s internal crates do include optional HTTP clients, but these are for specific features: one for cloud-based OCR and another for digital signature timestamping. Neither of these features is enabled or compiled into the standard desktop build of the application. The feature flags for these components are disabled at compile time, so the code is not present in the binary you download.
A JavaScript interpreter, QuickJS, is compiled in to handle calculations for XFA forms. Importantly, it does not execute scripts found within PDF documents by default; that requires setting an environment variable which is off. The interpreter runs in a highly restricted sandbox. Even if script execution were enabled, the sandboxed environment exposes only two form values for calculation purposes. Standard functions like `require`, `process`, and most critically, `fetch`, are designed to throw an error and are non-functional. Automatic actions specified in a PDF, such as `/OpenAction`, are inert and do not execute, providing another layer of protection against exfiltration.
The honest limit of this method
It is crucial to understand what this verification method can and cannot show. A standard packet capture tool like Wireshark or `tcpdump` will show that connections happen, but it will not show the content of those connections because the data is encrypted with TLS.
To see the content, you must use a proxy that you deliberately trust, such as Fiddler on Windows after you have installed its root certificate. This allows you to decrypt and inspect the HTTPS traffic to see exactly what data is being sent. This is a more advanced technique but is necessary for a full inspection of the data payloads.
Furthermore, you can monitor the behaviour of the binary you downloaded. However, you cannot independently verify that this binary was built from the source code we describe because that source code is not public. The method described in this post verifies the runtime behaviour of a specific binary on your machine.
Only a reproducible build process, which allows anyone to compile an identical binary from source, or a third-party security audit could provide assurance that the publicly described code is what is actually running. Neither of these exists for PDFluent at this time. Therefore, this network monitoring method is a strong practical test, but it is not a complete substitute for those more rigorous forms of verification.
Frequently asked questions
- Can I use this method to check any PDF editor?
- Yes, the network monitoring method described can be applied to any desktop application to observe its network behaviour during use.
- Does the update check send my document?
- No, the update check is a simple HTTP GET request to our server to check for new versions; it does not include any document data.
- What if I see a connection I do not recognise?
- A connection to an unknown address during editing or saving suggests the application may be transmitting data, and you should investigate further.
- Can the app upload data later without me knowing?
- This method only shows activity during your test; continuous monitoring would be needed to rule out all future activity, which is not practical for most users.
- Why not just use a firewall to block all connections?
- A firewall is an effective way to prevent any data transmission, but it does not verify the claim; it simply enforces the behaviour you want.
- How do I know the binary I downloaded is the one you tested?
- You cannot independently verify this without a reproducible build process or a third-party audit, neither of which is currently available.