Skip to content

Repository files navigation

CHMate

A pure-JavaScript reader for Microsoft Compiled HTML Help (.chm) files — like PDF.js, but for .chm. No plugins, no native modules, no WASM. It parses the ITSS/ITSF container and decompresses LZX entirely in JavaScript, then renders each Help topic in a strictly sandboxed iframe.

CHMate reading the PuTTY User Manual

▶ Live demo

https://alpaq92.github.io/CHMate/ — open a .chm from your machine — it is parsed locally and never leaves the browser — or load the bundled PuTTY manual. Deployed automatically from main by GitHub Pages (.github/workflows/pages.yml remains as a manual fallback).


Features

  • Pure JS — ITSF/ITSP/PMGL container parser and an LZX decompressor with ResetTable seeking, all in plain ES modules. No build step, no dependencies.
  • Faithful decoding — validated byte-for-byte against 13 real .chm files (1,400+ internal files), including PuTTY's 270 KB manual and Windows system Help files.
  • Modern reader UI — Contents / Index / Files sidebar, history (back/forward), zoom, find-in-page, print, drag-and-drop, keyboard shortcuts. The theme toggle (light / dark / follow the OS) drives the whole app — toolbar, sidebar and the document area alike.
  • Security first — every untrusted topic is sanitized and rendered inside a fully sandboxed iframe with a strict Content-Security-Policy; scripts are stripped out, internal resources are served from in-memory blob: URLs, and the network is blocked so a hostile CHM can't phone home.
  • Works offline and as a tiny library or CLI in Node.

Run it locally

ES modules can't load over file://, so use the bundled dev server:

node tools/serve.mjs          # → http://localhost:8080
# or: npm start

Then open http://localhost:8080 and pick a .chm.

Use as a library

import { ChmReader } from './src/chm/chm-reader.js';

const reader = ChmReader.open(arrayBuffer);   // Uint8Array or ArrayBuffer
reader.title;            // "PuTTY User Manual"
reader.defaultTopic;     // "/index.html"
reader.listFiles();      // ["/index.html", "/chapter1.html", ...]
reader.getFile(path);    // Uint8Array
reader.getText(path);    // decoded with the right charset
reader.getContents();    // table-of-contents tree (from the .hhc)
reader.getIndex();       // index tree (from the .hhk)

CLI

node cli.mjs info    file.chm
node cli.mjs list    file.chm
node cli.mjs toc     file.chm
node cli.mjs cat     file.chm /index.html
node cli.mjs extract file.chm ./out

How a CHM is read

CHM read pipeline: container parse (itsf.js) → section 0 raw slice / section 1 LZX decode (lzx.js) → getFile (content.js) → metadata and navigation (system.js, sitemap.js) → sandboxed render (render.js, app.js)

The only genuinely hard part of CHM is LZX decompression. CHMate's LZX decoder is an original implementation written from scratch against Microsoft's published [MS-PATCH] LZX specification — all of CHMate is original MIT code, and no third-party decoder is shipped. See CREDITS.md.

Tests

npm test          # validates samples/*.chm by self-consistency
node test/run.mjs path/to/other.chm

CHM files carry no checksums, so the suite proves correctness by self-consistency: every entry must decompress to exactly its declared length and, where the type is known, carry valid magic bytes / text. Any LZX error turns this into garbage that fails instantly.

Security notes

CHM is a legacy and an active malware vector. CHMate treats every topic as hostile:

  • rendered in <iframe sandbox="allow-same-origin">no allow-scripts, so JavaScript from the help file never executes at all;
  • a strict CSP (default-src 'none'; only blob:/data: for images, styles, fonts) blocks all network access;
  • <script>, event-handler attributes and javascript:/vbscript:/ms-its: URLs are stripped;
  • internal images/stylesheets are rewritten to in-memory blob: URLs sourced from inside the CHM; external url()s in CSS are dropped; links are intercepted by the host and external ones require confirmation;
  • nested <frame>/<iframe> content is itself recursively sanitized and given its own CSP (never loaded as raw HTML), so a frameset can't smuggle in a network beacon.

License

MIT — see LICENSE and CREDITS.md.

About

A pure-JavaScript reader for Microsoft Compiled HTML Help (.chm) files — like PDF.js, but for .chm. No plugins, no native modules, no WASM.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages