Spreadsheet rendering SDK · self-hosted CLI, Docker, and .NET no Office · no LibreOffice · BUSL-1.1
01Security · the input file is hostile

Every workbook is
treated as hostile.

Rendlio Sheets runs on your machines, against files you did not write. So the engine's first assumption is that the input workbook is hostile — every byte of the container is attacker-controlled. This page is the short version, for the person deciding whether to put Rendlio Sheets in front of an upload form. The full normative contract, guard by guard, is the security model.

02Blast radius · what the engine cannot do

The shortest security review
is a list of absences.

Most of Rendlio Sheets' security posture is capability the engine simply does not have. None of the rows below is a setting you could switch on by accident — the enforced ones are held in place by static analyzers that fail the build, not by convention.

capabilities · what the engine never does summary of /docs/security-model
NeverStatusWhat that buys you
Opens a network connectionfor any purpose, in any mode ENFORCED No activation, no telemetry, no font downloads, no phone-home. Works unchanged on air-gapped hosts, and there is no egress rule for you to write.
Executes macros or VBAwhatever a vbaProject.bin part contains ENFORCED Presence is detected and disclosed in the report. The part is never parsed beyond presence and never run.
Writes input content to diskno container part is ever extracted ENFORCED Parts are decompressed to memory streams only, so ZIP path-traversal tricks have nothing to write to.
Decrypts a protected workbookor guesses at a legacy binary container REFUSED Password-protected files and old .xls compound files are identified by magic bytes and refused up front with exit 3 — never fed to a parser that would misread them.
Reads fonts from the inputno code path from workbook bytes to the font stack ENFORCED Only the bundled fonts and the directories you configure are ever parsed. Under the sandbox profile, only the bundled ones.
Puts cell contents in a reportdetail and message strings are static templates ENFORCED A CI leak test asserts it, so a compatibility report from a confidential workbook is safe to attach to a support mail.
03Resource guards · bombs, depth, and deadlines

An .xlsx is a ZIP archive.
That is the attack surface.

Three absolute caps and one ratio guard bound what an archive can cost, and each is enforced on live counters as the entry inflates rather than on the sizes the file claims for itself. Every one is tunable; these are the defaults.

limits · decompression-bomb defaults tunable per run
GuardDefaultWhy it is shaped that way
Input file size 500 MB Checked before the container is opened at all.
Per-entry uncompressed bytes 512 MiB Counted as the entry inflates, not read from what the archive claims.
Total uncompressed bytes per run 2 GB The absolute ceiling on what one workbook can cost.
Per-entry compression ratio 1000:1 Applied past a 1 MiB floor, so ordinary tiny XML parts do not trip it.
ZIP entry count 10,000 A multi-disk or encrypted-entry archive is refused outright.

The full registry, including every environment variable and its default, is on deployment.

XML, not just ZIP

Every XML part is read through one guarded factory — creating a reader any other way is a build error in the engine, so there is no unguarded path to remember. DTDs are prohibited and the entity resolver is null, which closes XXE and entity expansion (the "billion laughs" family) categorically rather than by pattern-matching. A depth cap of 128 nested elements turns deep-nesting stack attacks into a counter, and every spreadsheet part is streamed rather than loaded into a DOM.

Deadlines and memory

--timeout (default 300 s) is a wall-clock deadline backed by a watchdog that also covers the code paths between checkpoints: if the pipeline is still stuck after a short grace period, the watchdog deletes the temp outputs, writes the report, and exits 4. --max-memory is a soft cap sampled at checkpoints — it bounds steady-state growth, not an instantaneous spike, so always set a hard container limit as the backstop. An unparseable limit value is a usage error, never a silently weaker limit.

04Untrusted uploads · the sandbox profile

One variable for
"anyone can upload here".

If you put Rendlio Sheets behind a form anyone can post to, set the hardening profile. It is read once at process start, and anything it forbids fails loudly — warning W8112, exit 3, naming the exact option that was refused. A hardening profile that silently ignored the flag you passed would be worse than none.

bash · the configuration the public playground runs
$ docker run --rm --memory 2g --cpus 2 \
    -e RENDLIO_SANDBOX=1 \
    -v "$PWD:/work" -w /work ghcr.io/rendlio/sheets \
    sheets convert upload.xlsx -o out.pdf --report report.json --timeout 60

With RENDLIO_SANDBOX=1 set, the engine additionally:

  1. hard-disables user font directories — including the environment-variable route, not just the flag;
  2. hard-disables system font enumeration, leaving bundled fonts only, which also makes output deterministic across hosts;
  3. refuses strict recalculation;
  4. refuses any future network-adjacent feature categorically, by a deny-list that sits next to the flag so a new feature has to pass it consciously;
  5. tightens the default timeout to 60 s.

The flag is engine-level, not a playground special case: the public playground runs exactly this configuration, and so can you.

RetentionOne hour, then gone

Playground uploads and results are deleted within 60 minutes — an expired job looks identical to one that never existed.

IsolationScratch space wiped

Each conversion runs in an isolated worker; its temporary files are deleted the moment the job finishes.

TrainingNever used for training

Your file improves nothing unless you explicitly donate it — the corpus checkbox is off by default.

Privacy modeDiagnose only

Structure-only analysis: no cell content rendered or stored, and the report is still the full report.

05Disclosure · how to report a vulnerability

Mail one address.
Bring a workbook.

Vulnerability reports go to security@rendlio.com. Responsible disclosure is appreciated — give us a chance to ship a fix before publishing.

The same address in the machine-readable form scanners and researchers look for: /.well-known/security.txt (RFC 9116).

What makes a report actionable

  • The affected version — the output of rendlio sheets version.
  • The platform: OS, container image tag, or .NET runtime.
  • A reproducing workbook, if one exists. A fixture that reproduces the issue is the single most useful thing you can send: it becomes a regression test, so the class of bug stays closed.

Scope

Probing this site or the public playground is acceptable only as part of a report to that address — see the terms of use. There is no bug-bounty programme and no payout; what we offer is a fast, non-defensive response and credit in the changelog if you want it.

Supply chain

Every release artifact — archives, container image, packages, SBOMs — is cosign-signed from the public build pipeline, a CycloneDX SBOM publishes with every release, and SHA256SUMS covers every artifact. The exact verification commands are on the install page.

For the reviewer who wants all of it

This page is the summary.

The security model is the page to hand to a security reviewer: the guarded container walk entry by entry, hardened XML parsing, image guards, the no-partial-output rule, and the full limit registry.