TL;DR

Jan, the local-first desktop app for running models and, since 0.8.5, an agent with file tools, let its write and edit tools follow a symlink whose target did not exist yet, so a repository or prompt that planted notes.md -> ~/.ssh/authorized_keys got the approval prompt to show an in-workspace name while the host wrote outside the project. The vendor advisory GHSA-7j4g-mj5j-p842 (2026-10-29, Moderate, CVSS 3.1 4.7) names 0.8.5 to 0.8.7 and the fix in 0.8.8 (released 2026-10-28). VulnCheck's CVE-2026-122235 (2026-11-04, CVSS 4.0 5.7) covers the same range. Upgrade to 0.8.8; the release also tightens the local API server's origin checks and makes MCP tool execution through the local API opt-in.

What happened

The bug. Jan's agent write and edit tools are meant to stay inside the configured workspace. The guard canonicalised the target path, but canonicalisation fails on a dangling symlink (a link whose target does not exist yet), and the guard fell back to the nearest existing ancestor plus the remaining path segments joined lexically. The link was never resolved, so a symlink inside the workspace pointing at a file under ~/.ssh/ was treated as an ordinary workspace write. The approval prompt showed only the in-workspace name. When the host performed the write it followed the link and created the file outside the project. The vendor advisory says the attack can be carried out through prompt injection or malicious repository contents; VulnCheck's description gives ~/.ssh/authorized_keys as the example target. CWE-59.

The fix. 0.8.8 resolves every link on the target path, including dangling ones, and opens paths without following a trailing symlink to reduce the race window. The release notes (2026-10-28) phrase it as the agent's file tools "no longer follow a link out of their workspace", and add that the home folder, credential folders and startup folders cannot be attached as writable.

What else 0.8.8 changes. The same release, titled "security fixes, llama.cpp 0.7.0, and Trusted Hosts for the local API", lists items with no advisory of their own: chat links open in the system browser instead of inside Jan; the local API server checks the origin of browser requests and accepts only trusted origins; server-side MCP tool execution through the local API requires an explicit setting; thread and assistant ids and file paths are validated before reads and writes; the Claude Code integration writes shell profile values more safely; on Windows, system tools run from the system folder and sign-in links open without cmd; MCP sign-in endpoints must use http or https. The origin-check and opt-in items are the "localhost is not a security boundary" class this corpus tracks for Cline, OpenCode, OpenClaw, Marimo and AutoGen Studio. They are listed here as release-note lines, not as confirmed vulnerabilities.

Dates and ids. Vendor advisory GHSA-7j4g-mj5j-p842 published 2026-10-29 by the vendor, credit Zachariah Mithani (AploSec), "CVE: None known" when read on 2026-11-05. VulnCheck's CNA record CVE-2026-122235 published 2026-11-04 21:06 UTC, affected 0.8.5 before 0.8.8 (semver), CVSS 4.0 5.7 (AV:L/AC:L/AT:P/PR:N/UI:P/VC:N/VI:H/VA:N) and CVSS 3.1 4.7 (AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N). The GitHub Advisory Database row (GHSA-pjpc-p955-xv4f, unreviewed, 2026-11-04) carries the CVE with no package and "No known source code", which is the shape of a desktop application distributed as installers rather than a registry package. The two advisory ids describe one bug: the vendor's and the database's row for VulnCheck's record.

Why it is in this corpus. Jan is a desktop agent with write tools, a Claude Code integration and an MCP client, pointed at repositories the user did not write. A symlink in a cloned repository plus a README that steers the agent is the same shape as the GhostApproval finding (PraisonAI had a workspace-escape record of the same shape in September, not tracked here), with the approval dialog showing a benign name while the host writes a credential file.

Am I affected?

  1. Version. Jan, Settings, About, or jan --version for the CLI. 0.8.5, 0.8.6 and 0.8.7 are affected. Earlier releases did not ship the agent write tools in this form; 0.8.8 and later are fixed.
  2. Did the agent run on a repository you did not author between 0.8.5 and the upgrade? List symlinks in those workspaces: bash find . -type l -not -path '*/node_modules/*' 2>/dev/null | while read -r l; do echo "$l -> $(readlink "$l")"; done # GNU and BSD find A link whose target is under ~, ~/.ssh, ~/.config, a shell profile or a startup folder is the pattern.
  3. Check the targets. ~/.ssh/authorized_keys for keys you did not add, shell profiles (~/.bashrc, ~/.zshrc, PowerShell $PROFILE) for lines you did not write, autostart folders for new entries.

If you are affected

  • Upgrade to Jan 0.8.8 or later from the GitHub releases page.
  • If a foreign key or shell line is present, remove it, rotate the SSH keys and tokens on the machine, and follow if your local AI agent was exploited.
  • Review which folders the agent can write and remove the home directory from the list; 0.8.8 blocks it by default.

Prevention

  • Resolve symlinks at write time, not at check time, and refuse to follow a link out of the workspace. The dialog has to show the resolved path. See agent sandboxing.
  • A local API that accepts requests from any origin is an open door for a browser tab or another local process; keep tool execution through it opt-in. See MCP hygiene.
  • Keep desktop agents on the current release; this fix shipped as one line in a release titled "security fixes" a day before the advisory.

Sources