Security dependency updates plus three terminal fixes.
Security
npm and cargo lockfiles: npm audit and cargo audit both report 0 vulnerabilities. Fixes include vitest (critical), vite, postcss, undici, rustls, rustls-webpki, quinn-proto, the tar crate, quick-xml and webpki-roots.
Gateway: LiteLLM v1.96.0 → v1.106.6 (CVE-2026-84377 SSRF, CVE-2026-114612 escalation to proxy admin). Digest and cosign signature verified; GATEWAY_REGISTRY_IMAGE kept in lockstep.
Sandbox image: apt-get upgrade in the base layer. The 2026-10-16 image carried fixable libbrotli (HIGH) and zlib CVEs because the gha layer cache never re-ran apt. pnpm's bundled undici is fixed by the rebuild.
STT image: pip 24.0 → 26.2.2 and setuptools 79 → 84.2.1 before installing.
Windows CI fallback Node 22.14.0 → 22.23.4.
Terminal fixes
URLs with trailing punctuation: new lib/urlMatch.ts. The match, and so the underline, excludes trailing .,;:!?* and unbalanced closing brackets. Balanced parens are kept, e.g. Foo_(bar). The URL toast detector uses the same rule.
Shift+click on /tmp paths: Claude Code hard-wraps long paths with cursor moves, so the rows aren't marked isWrapped and each half failed. xtermLineJoin now joins those rows using term.cols. The resolver also handles ~/ and falls back to probing /home/claude and /tmp on a miss. Containers are told to print full paths for files outside the project. That changes the instructions fingerprint, so each container is recreated once on its next start.
Copy artifacts: the OSC 52 handler used atob() without decoding UTF-8, which caused the mojibake (’). Copy now cleans invisible characters (NBSP, narrow NBSP, zero-width), and Ctrl+Shift+C rejoins Claude's hard wraps and strips its gutter markers. Raw copy is unchanged.
Testing
vitest: 1155 passed. npm run build OK. cargo test --locked: 874 passed, 4 ignored.
Gateway, STT and sandbox images were built locally and smoke-tested. Trivy finds no fixable OS issues in the sandbox image.
Not yet tried in a running app.
Follow-ups (not in this PR)
Existing gateways don't pick up the new LiteLLM image: the image isn't part of the gateway's fingerprint, and a local triple-c-gateway:latest takes priority over the pinned image.
uv's bundled rustls (GHSA-c8m5-c952-6cm4) is waiting on an upstream uv release.
Added after review
Code-review fixes: hard-wrap joins require a >=2-cell continuation indent and a bounded walk; the copy rejoin keeps the space before a URL that wrapped whole; table body rows keep their borders; astral wide characters are measured correctly; OSC 52 decodes non-fatally (U+FFFD only for bad bytes); the file-link instruction names the right modifier per platform; constants deduplicated.
Sandbox image: ARG APT_REFRESH with a weekly stamp plus a weekly schedule run of build.yml, so apt-get upgrade actually re-runs. Users will see a base-image update about once a week.
Recreation note: adding the file-link instructions recreates each container once; the recreate commits a snapshot first, so system state is kept.
CI fixes found on this PR
AppImage: appimagetool's continuous build (2026-11-01) refuses AppDirs that other users can't read. The extracted AppDir got the runner's 007 umask, so it is now chmod -R a+rX before the repack, with a post-repack check on file modes.
STT: cache-to ... ignore-error=true, so an unreachable runner-local gha cache no longer fails the build.
needrestart: NEEDRESTART_MODE=a and DEBIAN_FRONTEND=noninteractive at workflow level and through sudo env, so ephemeral runners no longer hang on its prompt.
Security dependency updates plus three terminal fixes.
## Security
- npm and cargo lockfiles: `npm audit` and `cargo audit` both report 0 vulnerabilities. Fixes include vitest (critical), vite, postcss, undici, rustls, rustls-webpki, quinn-proto, the `tar` crate, quick-xml and webpki-roots.
- Gateway: LiteLLM v1.96.0 → v1.106.6 (CVE-2026-84377 SSRF, CVE-2026-114612 escalation to proxy admin). Digest and cosign signature verified; `GATEWAY_REGISTRY_IMAGE` kept in lockstep.
- Sandbox image: `apt-get upgrade` in the base layer. The 2026-10-16 image carried fixable libbrotli (HIGH) and zlib CVEs because the gha layer cache never re-ran apt. pnpm's bundled undici is fixed by the rebuild.
- STT image: pip 24.0 → 26.2.2 and setuptools 79 → 84.2.1 before installing.
- Windows CI fallback Node 22.14.0 → 22.23.4.
## Terminal fixes
1. **URLs with trailing punctuation**: new `lib/urlMatch.ts`. The match, and so the underline, excludes trailing `.,;:!?*` and unbalanced closing brackets. Balanced parens are kept, e.g. `Foo_(bar)`. The URL toast detector uses the same rule.
2. **Shift+click on /tmp paths**: Claude Code hard-wraps long paths with cursor moves, so the rows aren't marked `isWrapped` and each half failed. `xtermLineJoin` now joins those rows using `term.cols`. The resolver also handles `~/` and falls back to probing `/home/claude` and `/tmp` on a miss. Containers are told to print full paths for files outside the project. That changes the instructions fingerprint, so each container is recreated once on its next start.
3. **Copy artifacts**: the OSC 52 handler used `atob()` without decoding UTF-8, which caused the mojibake (`’`). Copy now cleans invisible characters (NBSP, narrow NBSP, zero-width), and Ctrl+Shift+C rejoins Claude's hard wraps and strips its gutter markers. Raw copy is unchanged.
## Testing
- vitest: 1155 passed. `npm run build` OK. `cargo test --locked`: 874 passed, 4 ignored.
- Gateway, STT and sandbox images were built locally and smoke-tested. Trivy finds no fixable OS issues in the sandbox image.
- Not yet tried in a running app.
## Follow-ups (not in this PR)
- Existing gateways don't pick up the new LiteLLM image: the image isn't part of the gateway's fingerprint, and a local `triple-c-gateway:latest` takes priority over the pinned image.
- uv's bundled rustls (GHSA-c8m5-c952-6cm4) is waiting on an upstream uv release.
## Added after review
- Code-review fixes: hard-wrap joins require a >=2-cell continuation indent and a bounded walk; the copy rejoin keeps the space before a URL that wrapped whole; table body rows keep their borders; astral wide characters are measured correctly; OSC 52 decodes non-fatally (U+FFFD only for bad bytes); the file-link instruction names the right modifier per platform; constants deduplicated.
- Sandbox image: `ARG APT_REFRESH` with a weekly stamp plus a weekly `schedule` run of build.yml, so `apt-get upgrade` actually re-runs. Users will see a base-image update about once a week.
- Recreation note: adding the file-link instructions recreates each container once; the recreate commits a snapshot first, so system state is kept.
## CI fixes found on this PR
- AppImage: appimagetool's continuous build (2026-11-01) refuses AppDirs that other users can't read. The extracted AppDir got the runner's 007 umask, so it is now `chmod -R a+rX` before the repack, with a post-repack check on file modes.
- STT: `cache-to ... ignore-error=true`, so an unreachable runner-local gha cache no longer fails the build.
- needrestart: `NEEDRESTART_MODE=a` and `DEBIAN_FRONTEND=noninteractive` at workflow level and through `sudo env`, so ephemeral runners no longer hang on its prompt.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
v1.96.0 is affected by two advisories published since it was pinned:
- CVE-2026-84377 / GHSA-3cv6-jpf6-8222: authenticated SSRF and
provider-credential exfiltration via unvalidated request-body URLs
(fixed 1.96.2 on that line). This container holds a billed provider key.
- CVE-2026-114612 / GHSA-7hp6-4w63-5g45: privilege escalation to proxy
admin via cross-domain reuse of the salt key (fixed 1.103.1; no 1.96.x
backport).
v1.106.6 (2026-10-28) is the newest stable release with no OSV advisories.
Tag and digest updated together; the index digest was read with
`docker buildx imagetools inspect` and cosign-verified against LiteLLM's
pinned public key. GATEWAY_REGISTRY_IMAGE in gateway.rs is kept in
lockstep.
Smoke-tested with a config shaped like render_config()'s output: the
proxy starts, /v1/messages without a key is a 401, and with the master
key the Anthropic-format request is translated and routed to the
configured OpenAI base. Note this LiteLLM refuses to start with no
master_key at all; Triple-C always renders one, so that is not a
regression for us.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
python:3.11-slim ships CPython 3.11's frozen pip 24.0 and setuptools
79.0.1. OSV lists six pip advisories (CVE-2025-8869, CVE-2026-1703,
CVE-2026-3219, CVE-2026-6357, CVE-2026-8643, CVE-2026-13346; fixed by
26.2) and two setuptools ones (CVE-2026-59890, GHSA-xmj3-9gf7-h52w;
fixed by 84.1.0). Upgrading them first also means the app installs run
under a patched pip.
After the change the image resolves pip 26.2.2 and setuptools 84.2.1,
and an OSV batch query over all 41 installed packages comes back clean
(fastapi 0.162.0, starlette 1.10.0, python-multipart 0.0.33,
uvicorn 0.54.0, faster-whisper 1.2.1, ...). Trivy finds no fixable
Debian 13.7 OS vulnerabilities in the current python:3.11-slim, so no
apt upgrade layer is added. Smoke-tested: /health 200 and /transcribe
200 on a generated WAV.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
22.14.0 is from early 2025 and has missed every Node 22 security release
since, the latest being 22.23.4 (2026-10-20, marked security in
nodejs.org/dist/index.json). The step only runs when `node` is absent
from the build VM, so this governs a fresh or rebuilt VM; an existing
install on WindowsBuilder is left as it is.
No advisories exist in OSV for the pinned actions (checkout@v4,
build-push-action@v5, login-action@v3, setup-buildx-action@v3,
setup-qemu-action@v3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing in container/Dockerfile pins a version (gh, docker-ce-cli and
Node come from their apt repos, pnpm/Playwright/uv/ruff/rustup/Claude
Code/AWS CLI resolve latest), so a rebuild is what delivers fixes. Two
things stood in the way:
- `apt-get install` never upgrades packages the ubuntu:24.04 image
already ships, so a noble-security fix stays out until Canonical
refreshes the tag. The first apt layer now runs `apt-get upgrade -y`
inside the existing retry pattern.
- CI builds with `cache-from: type=gha`, so the layers only re-run when
the base digest or a layer's text changes. Trivy on the published
image (built 2026-10-16) found libbrotli1 1.1.0-2build2
(CVE-2026-111497, HIGH), zlib1g 1.3.dfsg-3.1ubuntu2.2 (CVE-2026-85091)
and pnpm's bundled undici 6.29.0 (CVE-2026-109383, CVE-2026-109869,
CVE-2026-87018, all HIGH). Changing this layer invalidates the cache
once, so the next CI build rebuilds everything.
A local `docker build --pull` of this tree succeeds (amd64). Trivy finds
no fixable OS or Node vulnerabilities in it: Node 22.23.4, gh 2.103.3,
docker CLI 29.9.2, AWS CLI 2.39.3, pnpm 12.16.1, Playwright 1.64.1,
Claude Code 2.1.316. What remains is uv 0.13.6's bundled rustls 0.23.45
(GHSA-c8m5-c952-6cm4, MEDIUM). That is fixed on uv's main branch but not
in a release yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The plain-text URL regex handed to WebLinksAddon ran to the next
whitespace, so `See https://x/docs.`, `(https://x/a)` and markdown
`[t](https://x/a)` underlined and opened the URL with the trailing `.`
or `)` attached. The addon underlines exactly what the regex matches and
passes that text to the click handler, so the fix has to be in the match.
lib/urlMatch.ts adds TERMINAL_URL_REGEX, using GitHub/linkify-style
rules: a URL never ends in `.,;:!?*`, and a closing `)]}` is kept only
when it closes a bracket opened inside the URL (one level), so
Wikipedia's `Foo_(bar)`, IPv6 `[::1]` and `a[]=` keys survive while a
wrapping paren is dropped. `-_~=/` are never trimmed (base64url OAuth
state, padding). The allowed character set is unchanged.
UrlDetector's toast candidates use the same trimming via findUrls, but
still decide whether to wait for more output from the raw run's end, so
a chunk ending `.../file.` is not emitted before `tar.gz` arrives.
sanitizeRelayUrl and the opensOnClick gate are untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shift+click did nothing useful on Claude Code's scratchpad paths
(/tmp/claude-<uid>/-workspace/<session>/scratchpad/x.md). Claude Code
wraps its own output: a word longer than the line is broken mid-word and
the rest drawn on the next row with cursor moves, so xterm never marks
that row isWrapped. The link provider saw two fragments: the first has no
extension (no link), the second is a relative `…/scratchpad/x.md` probed
under /workspace and not found.
- xtermLineJoin bridges such breaks when given cols: the row runs to the
edge, the next row continues with path characters after its indent, and
the two runs could not have fit on one line (keeps prose apart).
- Backend: `~/p` resolves to /home/claude/p; a relative path the project
roots do not have is probed again under /home/claude and /tmp, in a
second exec so a project file never becomes a chooser.
- CLAUDE_INSTRUCTIONS asks Claude to print full absolute paths for files
outside the project (changes the fingerprint: one recreation per project).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
appimagetool's continuous build of 2026-11-01 refuses an AppDir with
restrictive modes. --appimage-extract writes it under the runner's
umask, which produced a 0700 root and 0770 AppRun.wrapped, and we
repacked exactly that: an image other users could not start. chmod
-R a+rX before the repack, and assert the modes after it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two column-0 lines of shell output where the first happens to fill the
row (a 40-char path at 40 cols, then src/main.rs) were glued into one
bogus path. Claude Code draws continuation rows at least 2 cells in, so
require that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With hard-wrap bridging a long chain of continuation rows made the walk up
to the chain's start unbounded while the walk down stopped at the
2048-char budget, so a hover deep in the chain could get back text that
did not contain its own row, after reading the whole chain. Stop the walk
up after half the budget, always include the hovered row, and drop the
indent of a mid-chain first row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTAINER_HOME was declared twice (file_viewer/resolve.rs and
commands/inspect_commands.rs); it now lives once in docker/mod.rs as
pub(crate). xtermLineJoin's PATH_CHAR re-declared filePathLinks' TOKEN
character class; filePathLinks now exports PATH_SEGMENT_CLASS and both
regexes are built from it (TOKEN's source is byte-identical).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A selection of just `│ a │ b │` rows passed the 'wholly inside a box'
test, so the outer borders were stripped and the inner │ kept. A row with
a vertical rule inside its borders is a table row, not a box row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
isWide looked at a single UTF-16 code unit, so an astral character at the
break (CJK Ext-B, emoji) was a lone surrogate of width 1 and the rejoin
inserted a space between two wide characters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The empty-joiner rule fired whenever a row ended at the edge and its last
token plus the next row's first token were wider than a line, so a short
word at the edge followed by a URL that wrap-ansi moved down whole became
'thehttps://...'. Lengths alone cannot tell that from one long token split
after 'the', so a next-row token that starts a URL (scheme://) is now
treated as a fresh word, unless one of the two pieces fills its entire row
(only the middle piece of a token longer than a line does). The captured
70-column URL split still rejoins without a space.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single bad or truncated byte made the fatal decoder throw, and the whole
payload was then re-read as Latin-1, re-mojibaking every valid multi-byte
character in it. Decode non-fatally so only the bad bytes become U+FFFD.
No Latin-1 fallback is kept: every sender here emits UTF-8.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The instructions told Claude the user can Shift+click a path, but the gate
(TerminalView's opensOnClick) opens on a plain click unless a program holds
the mouse, and then takes xterm's force-selection modifier: Shift on
Linux/Windows, Option on macOS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI builds with cache-from: type=gha, so the apt-get upgrade layer was
reused until the base digest or the layer text changed, and security
fixes published since then never reached the image. The RUN now uses an
APT_REFRESH build arg, which build.yml sets to the ISO week, and a weekly
schedule trigger rebuilds without a commit. Publishing is gated on
event_name != 'pull_request' so the scheduled run (always on main)
publishes and PR runs still only validate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The STT job failed on the home runner with 'no route to host' to the
act_runner cache service while exporting the build cache. Import errors
are warnings, export errors are fatal without ignore-error=true, which
build.yml already sets. Ephemeral runners hit the same thing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--appimage-extract creates every directory 0700 regardless of the
modes stored in the image (verified by packing a 0755 AppDir and
extracting it under umask 022), so the post-repack guard failed on a
correct image. Check files, which extract with their stored modes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the eph-pve3 runners needrestart stops after apt-get install at an
interactive 'Daemons using outdated libraries' prompt, and the job
hangs until it times out (PR #66's build-linux, PR #68's test job).
Set NEEDRESTART_MODE=a and DEBIAN_FRONTEND=noninteractive for the
workflow and pass them through sudo, which resets the environment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jknapp
merged commit e223a3f64b into main2026-11-02 20:23:38 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Security dependency updates plus three terminal fixes.
Security
npm auditandcargo auditboth report 0 vulnerabilities. Fixes include vitest (critical), vite, postcss, undici, rustls, rustls-webpki, quinn-proto, thetarcrate, quick-xml and webpki-roots.GATEWAY_REGISTRY_IMAGEkept in lockstep.apt-get upgradein the base layer. The 2026-10-16 image carried fixable libbrotli (HIGH) and zlib CVEs because the gha layer cache never re-ran apt. pnpm's bundled undici is fixed by the rebuild.Terminal fixes
lib/urlMatch.ts. The match, and so the underline, excludes trailing.,;:!?*and unbalanced closing brackets. Balanced parens are kept, e.g.Foo_(bar). The URL toast detector uses the same rule.isWrappedand each half failed.xtermLineJoinnow joins those rows usingterm.cols. The resolver also handles~/and falls back to probing/home/claudeand/tmpon a miss. Containers are told to print full paths for files outside the project. That changes the instructions fingerprint, so each container is recreated once on its next start.atob()without decoding UTF-8, which caused the mojibake (’). Copy now cleans invisible characters (NBSP, narrow NBSP, zero-width), and Ctrl+Shift+C rejoins Claude's hard wraps and strips its gutter markers. Raw copy is unchanged.Testing
npm run buildOK.cargo test --locked: 874 passed, 4 ignored.Follow-ups (not in this PR)
triple-c-gateway:latesttakes priority over the pinned image.Added after review
ARG APT_REFRESHwith a weekly stamp plus a weeklyschedulerun of build.yml, soapt-get upgradeactually re-runs. Users will see a base-image update about once a week.CI fixes found on this PR
chmod -R a+rXbefore the repack, with a post-repack check on file modes.cache-to ... ignore-error=true, so an unreachable runner-local gha cache no longer fails the build.NEEDRESTART_MODE=aandDEBIAN_FRONTEND=noninteractiveat workflow level and throughsudo env, so ephemeral runners no longer hang on its prompt.🤖 Generated with Claude Code