Skip to content

Confirm document links that open a page running its own script, and refuse non-regular log files - #1201

Merged
nedtwigg merged 5 commits into
mainfrom
local-hardening
Oct 20, 2026
Merged

nedtwigg merged 5 commits into
mainfrom
local-hardening

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Local hardening on top of #1197: a Markdown-link confirmation for pages that run their own script, plus two small follow-ups to #1197.

Log opens refuse anything but a regular file

#1197 moved the update-report log read onto open_log. On top of that, open_log now refuses anything but a regular file (a FIFO, a directory) before it touches the mode, opening with O_NONBLOCK on unix so a FIFO cannot hang the open. security-local.md S163 now says "read or write" and names the non-regular-file refusal.

S168 names C1

#1197 extended the browser-binary gate and the agent-browser session-name check to C1. S168's wording now says so: "a C0 or C1 control character or DEL".

Document links to pages that run their own script ask first

A Tool's document link (an OSC 367 open with a disposition, such as a relative link clicked in the Markdown editor) names its own target, and a Markdown link may climb with .. to anywhere on disk under any link text. When the target is a format the built-in preview serves as a page running the file's own script (HTML and SVG), the host now asks in the existing external-link dialog, showing the full resolved path, before it opens or focuses it. The dialog is host UI the Tool's page cannot reach; it only answers, and a cancel, a replacing link, or an aborted request answers no and reports a navigation failure on the source. Markdown, text, images, and HTML/SVG opened as source (builtin:code) or as a folder keep opening on a plain click, .. included. Back/Forward to such a page asks again, and a same-document anchor never asks.

Specs: new S269 under security-local.md "Terminal output" with a guarantee row; dor-tool.md "Document navigation" carries the mechanism; dor-tools-builtin.md, the OSC 367 open row in terminal-escapes.md (which now mentions disposition/fragment navigation), and two "What is not defended" entries in security.md point at it.

Tests

  • cargo test in standalone/src-tauri: new log_read_refuses_a_symlink_or_non_regular_file (symlink, FIFO, directory with its mode left untouched, and a regular file still reads), beside Close the two failed checks in security-local.md: C1 controls in a browser binaryPath, and the update-log read #1197's symlink assertion.
  • lib/src/components/wall/tool-navigation.test.ts: which targets need confirmation under which handlers.
  • lib/src/components/wall/preview-slot.test.tsx: an OSC 367 document link to an HTML page types and retargets nothing until confirmed, a cancel leaves the source as it was, and Forward to an SVG page asks again while a Markdown target opens on the click alone.
  • lib/src/components/ExternalLinkModalHost.test.tsx: the dialog shows the full path, answers yes only on an explicit open and opens nothing itself, and answers no on cancel, replacement, and abort.
  • Storybook: Modals/ExternalLinkModal → DocumentLinkToActivePage.

Each new test was checked to fail with its fix reverted. Root pnpm test, cargo test, tsc -b in lib, and tsc --noEmit in standalone pass.

🤖 Generated with Claude Code

nedtwigg and others added 3 commits October 19, 2026 15:47
The webview's update-report log read opened the log with a plain File::open,
so a symlink at the log's name was followed. It now goes through open_log like
the write handles: no symlink is followed, and anything but a regular file
(a FIFO, a directory) is refused before its mode is touched, with O_NONBLOCK
so a FIFO cannot hang the open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The browser-binary gate refused C0 and DEL but let U+0080-U+009F through, and
the agent-browser session-name check had the same gap. Both now use one shared
class, CONTROL_CHARACTERS in the new dor-lib-common/control-characters module,
which dor's shell-quote and dor-tools-builtin's CONTROLS now reuse instead of
keeping their own copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Tool's document link (OSC 367 open with a disposition, e.g. a relative
Markdown link) names its own target, which can climb with `..` anywhere on
disk under any link text. When that target is a format the built-in preview
serves as a page running the file's own script (HTML, SVG), the host now asks
in the external-link dialog, showing the full resolved path, before it opens or
focuses it. The dialog lives in host UI the Tool's page cannot reach; it only
answers, and a cancel, a replacing link, or an aborted request answers no.
Markdown, text, images, and active files opened as source or as a folder keep
opening on a click. Back/Forward to such a page asks again.

Adds FAIL IF S269 under security-local.md "Terminal output", with a guarantee
row, and points dor-tool.md, dor-tools-builtin.md, terminal-escapes.md, and
security.md at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1eb5dcf
Status: ✅  Deploy successful!
Preview URL: https://533b49a8.mouseterm.pages.dev
Branch Preview URL: https://local-hardening.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standalone Smoketest is red because of the new dor-lib-common/control-characters subpath. npx tsc --noEmit in standalone/ fails with:

../dor-tools-builtin/src/file-viewer-format.ts(1,36): error TS2307: Cannot find module 'dor-lib-common/control-characters' or its corresponding type declarations.
../dor/src/commands/shell-quote.ts(8,36): error TS2307: Cannot find module 'dor-lib-common/control-characters' or its corresponding type declarations.

That job typechecks against sources without building dor-lib-common's dist/. standalone/tsconfig.json maps the subpaths it knows by name ("dor-lib-common/browser-providers", "dor-lib-common/browser-viewports"), and the new one isn't among them. The local run likely passed because a dist/ was already built. One fix is to add the subpath there. Another is to replace the per-subpath entries with "dor-lib-common/*": ["../dor-lib-common/src/*"], as website/tsconfig.json already does, so the next subpath can't hit the same break.

The Cloudflare Pages failure looks like the same gap in the viewer bundle. I can't read the Pages logs, so this is inferred from the mechanism. dor-tools-builtin/viewer/images-panel.tsx imports editableImageType from file-viewer-format.ts, so the viewer bundle now pulls in dor-lib-common/control-characters. dor-tools-builtin/scripts/build-viewer.mjs aliases only dor-tools-lib to source. The website's prebuild calls it through website/scripts/build-builtin-viewer.js without building dor-lib-common first, so esbuild resolves the subpath through the package exports to a dist/ that isn't there. Two fixes would work. One is to add a dor-lib-common alias next to the dor-tools-lib one. The other is to leave CONTROLS in file-viewer-format.ts as its own literal: it already covered C1, and keeping it removes the viewer bundle's new dependency on dor-lib-common. Neither file is in the diff, so I can push either fix if you want.

#1197 overlaps two of these three changes. It widens the same two gates to C1, routes read_log_tail through open_log, and regenerates the same unc-binary-path canary seed, so whichever lands second will conflict there. If this PR supersedes it, #1197 can be closed.

# Conflicts:
#	.github/audit/canaries/unc-binary-path.patch
#	dor-lib-common/src/browser-providers.ts
#	dor-lib-common/test/browser-providers.test.mjs
#	lib/src/lib/agent-browser-binary.test.ts
#	scripts/spec-word-budgets.json
#	standalone/src-tauri/src/lib.rs
@nedtwigg nedtwigg changed the title Refuse symlinked log reads and C1 controls in binary paths, and confirm Markdown links that open active content Confirm document links that open a page running its own script, and share one control-character class Oct 19, 2026
The new dor-lib-common subpath broke the standalone typecheck and the
viewer bundle, which resolve it without a built dist/. The gates #1197
widened, dor's shell-quote, and the viewer's CONTROLS already cover C1,
and the latter two stay dependency-free by design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg nedtwigg changed the title Confirm document links that open a page running its own script, and share one control-character class Confirm document links that open a page running its own script, and refuse non-regular log files Oct 20, 2026
@nedtwigg
nedtwigg merged commit 8a010de into main Oct 20, 2026
14 checks passed
@nedtwigg
nedtwigg deleted the local-hardening branch October 20, 2026 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants