Vendor security questionnaire
The questions a supplier review asks, answered — including the ones where the answer is unfavourable. Signing, penetration testing, certifications and logging are all in here.
The architecture, process by process: what the renderer can reach, what the sidecar can reach, and where the network surface would be if there were one. Start here.
Describes Oxofolio 0.2.1. Generated from the application's source; the release build fails if it is out of date with the code.
Product Oxofolio 0.2.1 — Offline document preparation workbench for audit firms Supplier Chadura Tech Private Limited
A desktop application that prepares client documents on the machine it is installed on. It is not a service. There is no account, no server, no tenant and no vendor-side storage, so there is no vendor environment for a customer's data to be in.
Three processes, all local:
Client documents are read by the sidecar and by the renderer's PDF viewer. They are never copied anywhere outside the folder they were found in, except into the output directories named below.
The application opens no sockets and makes no network connections. There is no telemetry, no update check, no crash reporting and no licence call-home; licences are verified offline against a public key shipped with the application. No document, filename, folder path or other client data is transmitted anywhere, by any means.
One qualification, stated here rather than left for you to find. The interface carries 2 links to our website:
feedback — https://oxofolio.com/feedback/?from=appinterviews — https://oxofolio.com/interviews/?from=appActivating one calls shell.openExternal, which hands the address to the operating system and opens the user's own browser. That browser then makes a request, and we can see it — the visit reaches our web server with the user's IP address, and the from=app marker tells us it came from an installation rather than from the website. The application itself still opens no connection, and nothing about the user's documents is included in the address. But a document that said "no network connections of any kind" and left this out would be answering the wrong question, so it is set out in full.
The addresses are held in the main process and the interface refers to them by name. Code running in the renderer cannot ask for an arbitrary address to be opened; only the ones listed above can be.
Blocking outbound traffic on the host does not affect the application. The links simply fail to load in the browser, and every feature continues to work.
The no-connection property is enforced rather than asserted. scripts/check-no-network.mjs fails the build if any of these patterns appears anywhere in the shipped source:
/\bfetch\s*\(//\bXMLHttpRequest\b//\bWebSocket\b//from\s+['"]node:(https?|net|dgram|dns)['"]//require\(['"](https?|net|dgram|dns)['"]\)//\bimport\s+(requests|urllib|httpx|socket)\b//^\s*import\s+socket\b/mscanned across electron/**/*.{js,cjs,mjs}, src/**/*.{js,vue}, python/**/*.py. It runs in npm run gates, which every release target depends on.
The renderer additionally runs under a Content Security Policy with connect-src 'none', window opening is denied outright, and navigation is cancelled in the main process.
| Setting | Value |
|---|---|
| contextIsolation | true |
| nodeIntegration | false |
| sandbox | true |
| webSecurity | true |
Content Security Policy in production builds:
default-src 'none'script-src 'self'style-src 'self' 'unsafe-inline'img-src 'self' data:font-src 'self'connect-src 'none'Every channel the interface can invoke. There is no general-purpose bridge and no way to reach the filesystem or run a command except through one of these.
app:diagnosticsapp:usageassembly:sealassembly:stateassembly:verifychecks:duplicateschecks:mixingclipboard:writecompress:batchdeck:editdeck:sanitisedialog:pickFileand 85 more — the full list is in the PDF and the source file.
Every method the Python process answers to. An unrecognised method is refused.
assembly_sealassembly_verifybuild_recordcheck_mixingcompress_batchdeck_editdeck_sanitisedecrypt_to_bytesdiagnosticsdoc_formatdoc_personal_datadoc_previewand 50 more — the full list is in the PDF and the source file.
Inside a client folder, the application only ever creates these directories, and writes its outputs inside them:
_assembly/_bundles/_compressed/_corpus_out/_edited/_encrypted/_erasure/_extracted/_marked/_ocr/_record/_redacted/and 6 more — the full list is in the PDF and the source file.
Every document operation writes a new file and leaves the source untouched — unlock, compress, combine, convert, redact, encrypt, split, stamp, OCR, image export and image mark-up all work this way. There is no app-managed document store and there will not be one: a second copy of client statements outside the firm's backup and access controls is a security regression rather than a convenience.
One feature writes in place, deliberately: the spreadsheet viewer's light edit mode. A reviewer correcting three cells should not have to round-trip through Excel to do it. Because that is a change to a client's file, it is constrained in three ways that are worth checking rather than taking on trust:
_originals/, hashed with SHA-256, and never refreshed afterwards — so a later edit cannot quietly become "the original".Edits are applied to the file's own XML rather than by re-saving the workbook, so formulas, cached results, formatting, charts, pivot tables and macros are carried across untouched. Structural changes that would move a formula's references are refused outright rather than approximated.
In the application's own storage (userData, outside any client folder):
| File | Contents |
|---|---|
folder-settings.json | Per-folder preferences, the recent and pinned folder list, retention basis, audit-file seals and the engagement fingerprint used by the client-mixing check. Includes client folder paths. |
firm.json | The firm's own Article 30 particulars: name, address, privacy contact, data protection officer. |
passwords.json | Document passwords the operator chose to remember, encrypted with the operating system keystore. Written only when the OS can encrypt; never in plain text. |
signatures/ | Signature and stamp images the operator added, as PNG. Capped at three. |
licence.key | The installed licence and its signature. |
install-time.json | First-run date, so the trial can be counted; the date last seen, so a clock moved backwards can be detected; and the machine identifier described under Licensing below. No document, folder or client information. |
Licences are files, signed with Ed25519 and verified against a public key compiled into the application. Verification is arithmetic performed on this machine. Nothing is transmitted, there is no activation step, and an installation that never touches a network works indefinitely.
A licence is issued for one computer, so the application derives an identifier for the machine it is running on and compares it with the one in the licence. That identifier is a SHA-256 hash of two values read from the operating system: an installation identifier, which changes when the operating system is reinstalled, and a hardware serial number. Because it is a hash, the values underneath cannot be recovered from it.
What this means in practice, stated plainly because it is the part a reviewer will want to be sure of:
The questions a supplier review asks, answered — including the ones where the answer is unfavourable. Signing, penetration testing, certifications and logging are all in here.
Why the supplier is neither controller nor processor for your documents, what that means for a DPA, and the honest limits of the redaction and erasure functions.
Installing without administrative rights, what the application writes and where, and what to allow through an endpoint agent.