Files
onenote-tools/docs/SECURITY.md

10 KiB

Security model

Trust boundary and local processing

Selected .one, .onepkg, and companion .cab files are untrusted. The main thread validates the selection and combined size, reads the files into ArrayBuffers, and transfers them to a dedicated module worker. CAB entries are extracted only into memory; archive paths are never written to the filesystem.

The worker is a fault-containment and responsiveness boundary, not a security sandbox. A successful open keeps its session alive for page/resource/export requests. Clearing the file, choosing another file, or unmounting the app terminates that worker; a failed open also terminates it. JavaScript garbage collection does not promise immediate release or secure erasure.

The application does not:

  • upload or transmit notebook bytes;
  • use a notebook-processing application backend, upload proxy, telemetry, analytics, remote fonts, or notebook-referenced external content fetching; the optional same-origin Toolbox catalogue/manifest is application chrome and receives no notebook data;
  • persist source or derived content in LocalStorage, IndexedDB, Cache Storage, cookies, or a service worker;
  • execute notebook content or inject notebook HTML/SVG;
  • use WebAssembly, a Rust runtime, or a native helper.

Active parser defaults

The main thread accepts one .one, or one .onepkg plus its .cab companions. It permits at most 256 selected parts, 512 MiB per file, and 512 MiB combined before reading.

The OneStore parser applies these independent defaults per section/store:

Resource Default limit
Section bytes 512 MiB
Transaction fragments / entries 16,384 / 1,000,000
File-node list fragments / nesting 16,384 / 64
File nodes 1,000,000
Objects 500,000
Properties per set / property nesting 16,384 / 64
Stream IDs 1,000,000
FSSHTTPB revision-chain entries 16,384
One property vector 64 MiB
Desktop object-reference vector 1,000,000 per vector
FSSHTTPB ExGUID/CellID references 1,000,000 aggregate
Nested PropertySets per ObjectPropSet 1,000,000 aggregate
File-data objects / one payload / total payloads 10,000 / 256 MiB / 512 MiB
Content-graph depth 256
Content blocks / text runs / table cells 100,000 each
Decoded ink points 100,000
Pages 10,000
Diagnostics 10,000
Retained rich/extracted text 2,000,000 UTF-16 units

Images are automatically previewable only when a bounded PNG IHDR or JPEG marker/SOF inspection succeeds. The compressed payload must be at most 32 MiB, each dimension at most 16,384 pixels, and the image at most 67,108,864 pixels. JPEG inspection reads at most 1 MiB of markers and 4,096 marker records. Images outside those limits stay inert and are available only through an explicit download action.

The CAB/package path applies these defaults across a complete cabinet set:

Resource Default limit
One cabinet / complete set input 512 MiB / 512 MiB
Cabinet parts 256
Folder segments / files 1,024 / 10,000
CFDATA fragments 131,072
CAB name bytes 255
Path characters / path segments 4,096 / 64
TOC construction operations / diagnostics 1,000,000 / 10,000
One extracted file 256 MiB
One logical folder output 512 MiB
Total folder output / extracted entry bytes 512 MiB / 512 MiB
Logical-folder compression ratio 200:1
One MSZIP compressed block 32 KiB + 12 B
Quantum window 2 MiB
LZX window 8 MiB
LZX/Quantum decode operations 1,000,000,000

A CFDATA block may declare at most 32 KiB of output. Counts and offsets use checked arithmetic and bounded readers. The CAB parser verifies supplied block checksums; rejects overlapping/out-of-range data, traversal/absolute/control or case-insensitive duplicate paths; and validates a complete cabinet set before decoding. Set IDs, indexes, links, compression, split blocks, and spanning-file records must agree. Reserve areas are private to each cabinet/folder and are validated locally rather than compared across parts.

MSZIP checks CK framing and raw DEFLATE data, writes into a fixed caller buffer with overflow detection, requires the declared output size, and keeps at most 32 KiB of logical-folder history. LZX and Quantum validate their windows, trees/models, blocks/frames, matches, output, trailer, and operation budgets. Codec state is preserved across continued cabinet segments. Extraction checks cancellation between data blocks and periodically yields to the worker loop.

Desktop OneStore traversal checks fragment/reference cycles, nesting, object/property/reference counts, and byte ranges. FSSHTTPB parsing bounds every declared stream body and compact integer, rejects invalid end markers and non-zero package tails, checks aggregate reference/mapping counts and revision cycles, and retains BLOB payloads as bounded source ranges. Fragment records are range-checked, but packages containing them are rejected because fragmented-element reconstruction is not implemented.

Display and export budgets

Parser DTOs are projected through a smaller page-display budget before React creates nodes: 5,000 blocks, depth 64, 10,000 text runs, 500,000 displayed text units, 2,000 table rows, 5,000 table cells, 256 columns, 10,000 ink strokes, 50,000 ink points, and 4,096 characters per metadata label. Exceeding a display budget produces an explicit truncation notice. Newlines remain inside text nodes instead of multiplying DOM elements.

Navigation and inspectors separately expose at most 250 page links, 500 notebook nodes at depth 16, 200 package entries per page, and 100 diagnostics per page. Titles, previews, package paths, diagnostic fields, and panel labels are length-bounded; pagination/truncation remains explicit in the UI.

Export planning independently permits at most 10,000 sections, 100,000 pages, 1,000,000 blocks, depth 256, 100,000 diagnostics, 8,000,000 text units, and 500,000 ink points. One resource and all resources are capped at 64 MiB; one standalone output or all uncompressed static-ZIP members are capped at 128 MiB. Planning accounts for metadata and projected UTF-8 output before serialization, then verifies actual encoded/member sizes. Resource views remain zero-copy until the bounded serializer needs bytes.

DTO strings are rendered as React text nodes. Notebook HTML, scripts, active SVG, and attachment payloads are never injected. Only parsed ink coordinates generate application-owned SVG path attributes. Hyperlinks require an explicit click, are limited to http, https, or mailto, and use rel="noopener noreferrer".

Automatic image requests carry a distinct preview intent. The worker rejects a non-image, unapproved media type, unsafe dimensions, or oversized payload before allocating/copying its bytes to the main thread. Approved image bytes may cross for automatic preview; attachment bytes and rejected image bytes cross only after the user presses a download button.

Static export HTML is inert, contains no script or external fetch, escapes all text/attributes, embeds only images that pass the same inspector, and neutralizes potentially active attachment suffixes. Exports represent the supported reader model; they do not write OneNote files.

Known gaps

  • There is no wall-clock deadline. Synchronous OneStore graph parsing cannot observe cancellation until control returns; terminating the worker is the current hard stop.
  • Package opening can hold source cabinets, decompressed logical folders, extracted entry views, and parsed DTOs at overlapping times. Byte/count limits bound them, but do not guarantee a low peak heap.
  • Section content limits apply per contained section rather than once to the complete .onepkg; cabinet/TOC limits are aggregate for the package set.
  • CAB checksums detect accidental corruption but do not authenticate a package.
  • Format coverage is limited to the documented golden files and structures. New producer/version claims require licensed regression fixtures and malformed variants.
  • The worker boundary reduces UI impact but is not a substitute for browser and operating-system isolation. Open especially sensitive files in a dedicated browser profile if that risk matters.

Planned hardening includes elapsed-time budgets, more cancellation checkpoints, lower-peak-memory extraction, expanded malformed-input/fuzz corpora, fragmented FSSHTTP reconstruction, and additional licensed OneNote producer fixtures.

Fixtures and vulnerability reporting

Never commit private notebooks. Fixture provenance and redistribution terms must be recorded in tests/fixtures/README.md.

Report suspected parser vulnerabilities privately to the repository owner through the Gitea instance before publishing malicious fixtures. Include the application version, browser, smallest reproducible input, diagnostic, and observed resource use. Do not attach sensitive notebooks.