Fix local workspace contract and complete QA roadmap hardening - #124
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…laims to protect The posix shell had no /work. File tools translated guest paths and the shell did not, so "write a script to /work, then run it" -- the most basic agent workflow -- failed on the provider everyone gets by default. `rewriteGuestPaths` maps guest-absolute paths onto the workspace the shell already runs in: quote-aware, lexical, preserving trailing slashes so `/work/$f` still concatenates, and leaving lookalikes like `/workshop` and jail escapes alone rather than helpfully repointing them at a host file. Heredoc bodies are copied through untouched. `cat > /work/notes.md <<EOF` is how an agent writes a file, and the thing it most often writes about is the workspace, so rewriting inside the body changed the bytes that landed on disk. Worse, the body is prose: an apostrophe in `don't` put the scanner in single-quote state for the rest of the script, and the single-quote branch adds no quoting, so a later host path containing a space came out bare and split into two shell words. The terminator is tracked instead, the body never reaches the path logic, and quote state resets after it. `<<-` and a quoted delimiter are both understood; `<<<` is left alone. The safety language followed from the first paragraph. "/work is jailed" was true of the file tools and false of the shell, in the MCP note the model reads first and in four documents. They now say which half is confined, because a model that believes the shell is contained takes risks it otherwise would not. Also: - A run that cannot fit the model's context is refused before the first call, with the numbers, rather than truncated into nonsense by the server. Local servers still default to tiny contexts. - A failed Chromium launch is bounded and the process reaped, so a launch that never opens its port stops leaking a browser per attempt. - Markdown discovery is content-sniffed. A repository's CHANGELOG is not a pasted chat, and offering it as one made discovery useless where people try it first. - Secrets are stripped from title-derived metadata before it becomes a name, a slug or a filename, where a kept prefix is only a smaller leak. - The distiller no longer reports "configured without tools" while the spec it wrote lists them. - The Windows exit-code smoke test gets the timeout its subprocess needs. The /work contract tests are posix-only, and so are the `rewriteGuestPaths` string expectations: `toHostPath` resolves through `node:path`, so a posix JailMap becomes a drive-qualified Windows path and every expectation would have to be written twice. That code is only reached on the `/bin/sh` branch, which a Windows host never takes. The heredoc tests derive their expectations instead, so they run everywhere, and the isolation-honesty and Windows-messaging assertions stay outside the guard -- skipping the posix block must not take them with it.
Hotragn
force-pushed
the
fix/local-work-contract-and-honest-claims
branch
from
September 18, 2026 20:23
280934b to
0f664b6
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One commit on top of
f8ffbbb. The headline is the first item; the rest are theremaining QA findings, grouped because they are all small and none of them
touches a shared path.
The local
/workcontractThe posix shell had no
/work. File tools translated guest paths and the shelldid not, so "write a script to
/work, then run it" — the most basic agentworkflow there is — failed on the provider everyone gets by default. The WSL
branch bind-mounts a real
/workper exec; a plain posix shell cannot do thatportably, because unprivileged user namespaces are not enabled everywhere and
macOS has none at all.
rewriteGuestPathsmaps guest-absolute paths onto the workspace the shellalready runs in. It is lexical and quote-aware:
https://x/workand/workshopareleft alone
shell-quoted elsewhere
/work/$fstill concatenates and the expansionstays outside the replacement
/work/../../etc/passwd) is passed throughuntouched rather than helpfully repointed at the host file it names —
/workdoes not exist in this mode, so it fails harmlessly
$PWDis also set to the workspace, so it agrees with whatpwdprints.What
localclaims to protect"
/workis jailed" was true of the file tools and false of the shell, and it wasin the MCP note the model reads first. A model that believes the shell is
confined takes risks it otherwise would not, which is the whole argument for
saying
isolated: falseout loud in the first place.The note,
SECURITY.md,docs/SECURITY-MODEL.md,README.md, the MCP packageREADME and the MCP docs page now say which half is confined: file tools are
confined to
/work, shell commands reach whatever the user reaches. Themount-namespace row also stops implying isolation between computers and says
what it actually gives — the
/workan agent sees is always its own workspace.The rest
call, with the numbers, instead of being truncated into nonsense by the server
after a long local load. Local servers still default to tiny contexts, and
Husk's own system prompt plus the computer tool schemas is ~2.5k tokens alone.
now reaped instead of left running, and the poll is bounded with the exec kill
as the hard backstop. Every failed launch used to leak one browser, each
holding its workspace open.
CHANGELOGis not a pasted chat, and offering it as one madehusk importuseless in the place people try it first.
a credential in a transcript title cannot survive into the bot's name or its
filename. Core keeps six characters of a matched secret so an operator can tell
two keys apart in a log; everything leaving
@husk-ai/sessionsis an artifact,and there a kept prefix is only a smaller leak —
sk-ant…[redacted]sluggedinto
sk-antredacted.while the spec it just wrote lists them.
needs.
Verification
Run on Windows 11 ARM64, Node 24.14.0, against
f8ffbbb.npm installnpm run build:packagesnpm run typechecknpm testnpm run driftnpm run linksscripts/secret-scan.mjsgit diff --checkNo UI change: nothing under
apps/weborapps/console, and no.tsx,.jsxor
.cssin the diff. The singleapps/edit is prose inside a fenced codeblock on the MCP docs page.
Two notes on the tests
The
/workcontract tests are posix-only, and therewriteGuestPathsunit testsare too.
toHostPathresolves throughnode:path, so a posixJailMapbecomesa drive-qualified Windows path —
/host/root/scrape.pyarrives asC:\host\root\scrape.py— and every expectation would have to be written twicefor no gain. The function is only reachable on the
/bin/shbranch, which aWindows host never takes: it gets WSL or
cmd.exe. Both blocks are guarded withdescribe.skipIf(process.platform === 'win32').The isolation-honesty and Windows-messaging assertions are deliberately outside
that guard. They are literals and pure functions, they run on every platform, and
windowsHint/windowsReasonare the only thing standing between a Windows userand being told to install the WSL they already have. Skipping the posix contract
block must not take them with it.