Harden bundle against host's boundary-less import regex (#14) #15

Merged
crowmaki merged 1 commit from fix/14-host-import-regex into main 2026-08-29 21:22:06 +00:00
Collaborator

Fixes #14.

Root cause

Hermes Desktop's runtime loader (apps/desktop/src/contrib/runtime-loader.ts) scans plugin bundles with

/(from\s*|import\s*\(\s*|import\s+)(['"])([^'"]+)\2/g

— no word boundary before from. Our bundle inlines Temml (LaTeX, #7), whose symbol table contains "\\mapsfrom" and "\\xtofrom". In the minified output …mapsfrom",!0);o(i,f,"… is read as an import of ,!0);o(i,f, — the garbage specifiers in the error. Our own lint uses \bfrom, so it never caught it.

Fix

  • desktop/scripts/harden-bundle.mjs (new): post-build pass that rewrites from/import glued to an identifier char and followed by a quote (mapsfrom" → mapsfro\x6d"). That can only occur inside a string/regex/template literal, where the hex escape is semantically identical; real import statements are untouched.
  • desktop/esbuild.config.mjs: runs the pass after every build (single and --watch) before lint.
  • desktop/scripts/lint-bundle.mjs + scripts/install.py: additionally check with the host's exact regex, so a future vendored string cannot regress this. Verified both reject the unhardened bundle.
  • desktop/test/harden-bundle.test.ts: host regex misreads the Temml snippet; hardened output is invisible to it and evaluates to identical strings; real imports untouched; idempotent.
  • Docs: AGENTS.md, desktop/README.md, design.md.

Verification

  • npm run build → lint ok; host regex over dist/plugin.js now yields only @hermes/plugin-sdk, react, react/jsx-runtime
  • npm test 25/25, npm run typecheck clean
  • After merging, re-run scripts/install.py to refresh ~/.hermes/plugins/hermes-learning/desktop/plugin.js.

The proper upstream fix is \bfrom / \bimport in hermes-agent's importSpecifierRe; this keeps the plugin working on current hosts regardless.

Fixes #14. ## Root cause Hermes Desktop's runtime loader (`apps/desktop/src/contrib/runtime-loader.ts`) scans plugin bundles with ``` /(from\s*|import\s*\(\s*|import\s+)(['"])([^'"]+)\2/g ``` — no word boundary before `from`. Our bundle inlines Temml (LaTeX, #7), whose symbol table contains `"\\mapsfrom"` and `"\\xtofrom"`. In the minified output `…mapsfrom",!0);o(i,f,"…` is read as an import of `,!0);o(i,f,` — the garbage specifiers in the error. Our own lint uses `\bfrom`, so it never caught it. ## Fix - **`desktop/scripts/harden-bundle.mjs`** (new): post-build pass that rewrites `from`/`import` glued to an identifier char and followed by a quote (`mapsfrom"` → `mapsfro\x6d"`). That can only occur inside a string/regex/template literal, where the hex escape is semantically identical; real import statements are untouched. - **`desktop/esbuild.config.mjs`**: runs the pass after every build (single and `--watch`) before lint. - **`desktop/scripts/lint-bundle.mjs`** + **`scripts/install.py`**: additionally check with the host's *exact* regex, so a future vendored string cannot regress this. Verified both reject the unhardened bundle. - **`desktop/test/harden-bundle.test.ts`**: host regex misreads the Temml snippet; hardened output is invisible to it and evaluates to identical strings; real imports untouched; idempotent. - Docs: `AGENTS.md`, `desktop/README.md`, `design.md`. ## Verification - `npm run build` → lint ok; host regex over `dist/plugin.js` now yields only `@hermes/plugin-sdk, react, react/jsx-runtime` - `npm test` 25/25, `npm run typecheck` clean - After merging, re-run `scripts/install.py` to refresh `~/.hermes/plugins/hermes-learning/desktop/plugin.js`. The proper upstream fix is `\bfrom` / `\bimport` in hermes-agent's `importSpecifierRe`; this keeps the plugin working on current hosts regardless.
Hermes Desktop's runtime loader scans plugin bundles with
/(from\s*|import\s*\(\s*|import\s+)(['"])([^'"]+)\2/g - no word boundary -
so Temml's "\\mapsfrom" / "\\xtofrom" string literals in the minified
bundle were read as imports and the plugin was refused with
'unsupported imports: ,!0);o(i,f,, ,'.

- scripts/harden-bundle.mjs: post-build pass rewriting from/import glued to
  an identifier char (only possible inside a literal) to fro\x6d / impor\x74
- esbuild.config.mjs runs it after every build (single and --watch)
- lint-bundle.mjs and install.py also check with the host's exact regex so a
  new vendored string cannot regress this
- tests + docs
crowmaki deleted branch fix/14-host-import-regex 2026-08-29 21:22:06 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crowmaki/hermes-learning!15
No description provided.