ODF container — ZIP + mimetype +
META-INF/manifest.xml.
Module pkgPackage | Source packages/front/office/odf/src/pkg/package.js | Deps odfErrors, odfShared, zip, pkgMimetype, pkgManifest | Worker-safe yes
An ODF package is a ZIP archive with two constraints:
mimetypemust be the first ZIP entry, STORED (no compression, no extra field).META-INF/manifest.xmldeclares every file entry with its media-type.
Model:
{
mimetype: 'application/vnd.oasis.opendocument.text',
manifest: { version, entries: [...] },
parts: { 'content.xml': Uint8Array, 'styles.xml': Uint8Array, ... }
}
Part paths have no leading slash ('content.xml', 'Pictures/image1.png'), same as manifest entries for non-root parts.
Resolve
const pkg = runtime.resolve('pkgPackage');
// → { read, write, empty, setPart, getPart, getPartText, MIMETYPE_PATH, MANIFEST_PATH, DEFAULT_LIMITS }
API
| Method | Description |
|---|---|
read(bytes, opts?) |
{ mimetype, manifest, parts }. opts overrides the ZIP bomb caps: maxParts (default 4096), maxUncompressed (default 268435456, 256 MiB), maxRatio (default 200); 0 disables a cap. A breached cap throws ParseError('odf/parse-error/zip-bomb') with context.limit naming it; an option that is not a finite number >= 0 throws ContractError('odf/contract-error/pkg'). Throws ParseError('odf/parse-error/pkg') if mimetype/manifest are missing or the ZIP is invalid. |
write(pkg) |
Uint8Array; mimetype emitted first (STORED). Throws ContractError if pkg.mimetype is missing. |
empty(mimetype) |
Skeleton { mimetype, manifest, parts: {} }. |
setPart(pkg, path, bytes, mediaType?) |
Adds the part and a manifest entry. |
getPart(pkg, path) / getPartText(pkg, path) |
Read helpers. |
MIMETYPE_PATH / MANIFEST_PATH |
'mimetype' / 'META-INF/manifest.xml'. |
DEFAULT_LIMITS |
Frozen { maxParts: 4096, maxUncompressed: 268435456, maxRatio: 200 } — the read defaults. |
Examples
const pkg = runtime.resolve('pkgPackage');
const mt = runtime.resolve('pkgMimetype');
const p = pkg.empty(mt.CT_ODT);
pkg.setPart(p, 'content.xml', new TextEncoder().encode('<x/>'), 'text/xml');
const bytes = pkg.write(p);
Security — ZIP bomb guards
read(bytes, opts?) enforces three bounds while unzipping. They are checked per
entry, before that entry is inflated, so a bomb is rejected without paying for
its payload. maxUncompressed and maxRatio are the values @awacloud/ooxml
ships; the entry cap is higher (see below).
| Option | Default | Guards against |
|---|---|---|
maxParts |
4096 |
Archives with an absurd number of entries (directory entries count). |
maxUncompressed |
256 * 1024 * 1024 |
Total inflated size. |
maxRatio |
200 |
Compression-ratio zip bombs (per entry). |
Breaching any of them raises ParseError('odf/parse-error/zip-bomb') with the
offending context.limit (plus max, and actual, name or ratio as
relevant). Pass 0 to disable a cap. There is no per-entry uncompressed-size
cap. The STORED mimetype entry has ratio 1 and never trips maxRatio.
Why 4096 entries: a LibreOffice document with many embedded objects (formula
objects, charts) carries a directory entry and several parts per object, so a
formula-heavy text easily exceeds a thousand entries — the reference case is a
1606-entry document. maxUncompressed and maxRatio still bound the archive.
const pkg = runtime.resolve('pkgPackage');
const mt = runtime.resolve('pkgMimetype');
const p = pkg.empty(mt.CT_ODT);
pkg.setPart(p, 'Pictures/pad.bin', new Uint8Array(1024 * 1024)); // zeros: ratio far above 200
const bytes = pkg.write(p);
try {
pkg.read(bytes);
} catch (e) {
console.log(e.code, e.context.limit); // → odf/parse-error/zip-bomb maxRatio
}
const back = pkg.read(bytes, { maxRatio: 0 }); // cap disabled
console.log(back.parts['Pictures/pad.bin'].length); // → 1048576
Notes
- The
mimetypefile is forced into first position via insertion ordering of thefilesobject passed tozip.zipSync, and always written as[bytes, { level: 0 }]for STORED. - If reading fails at the ZIP layer (non-ZIP bytes), a
ParseErrorodf/parse-error/pkgis thrown with a chainedcause. A ZIP bomb error is never re-wrapped this way: it keeps itsodf/parse-error/zip-bombcode. - The ZIP layer's unsafe-path (zip-slip) refusal surfaces as
odf/parse-error/pkgtoo.